Note: Because the event model for JDK 1.0 is rapidly becoming obsolete, material on the JDK 1.0 event model will not be covered in classroom lectures or examinations in Professor Baldwin's CIS 2103K (Intermediate Java Programming) classes at Austin Community College.
Insofar as possible, we will explain events on their own merit without getting involved in detailed discussions about the user interface. Later we will learn about the various tools for building an effective user interface after we understand events. I believe that this will lead to a better understanding of both topics.
The original version of this lesson is being written in February of 1997 to describe events and event handling according to the JDK 1.0.2 specification. The beta version of JDK 1.1 is available for testing at this time, and Sun has announced that it will contain important changes to the Abstract Windows Toolkit, including changes to the implementation of events and event handling.
When JDK 1.1 is released and hard information regarding those changes becomes available, this lesson will be supplemented with a new lesson describing events and event handling under JDK 1.1.
Calvert's book describes how to write ordinary C programs to produce the Graphical User Interface (GUI) that you commonly see on Windows programs. This is an excellent book on the subject which presents an interesting challenge, particularly in 21 days.
The programming approach described by Calvert (which was the only approach in the early days of Windows) makes calls to literally hundreds of functions provided by the Windows API. These functions control how various widgets appear on the screen and how they respond to user activity. Many of these functions have five or more arguments with names that are impossible to pronounce, and even more difficult to type.
Although the concepts involved are not terribly difficult, the approach is very tedious and error prone. Fortunately, with the advances in C++, tools have become available to help us to do the same job in a more pleasant manner.
The user may be moving the mouse, pressing a mouse button, pressing keys on the keyboard, touching a touch-screen, etc. As the user performs these actions, the operating system (OS) responds by sending a stream of messages to the applications that are running. These messages contain information about the actions of the user to which the application might need to respond.
Among other things, Calvert's book describes how to write programs that properly interpret those messages in order to implement a meaningful interface with the user.
In other words, if the user clicks the mouse on a button, this will result in the OS sending a message to the program. The Calvert book describes how to interpret these messages and cause the program to provide an appropriate response.
Rather, the Java runtime system is a program running at that level.
For example, we could execute a Java application by entering the following command at the operating system prompt:
Our Java application is simply a command-line argument to that program.
The first command-line argument (the name of a Java class file) is passed to the program named java.
Additional command-line arguments (if any) are passed to the Java application.
The Java runtime system causes our Java application to execute under its direct control.
Similarly, when we execute an applet, there is already a program being executed which we commonly refer to an a browser. Applets execute under control of the browser.
During the remainder of this lesson, when we refer to the Java runtime system, we will be referring to either the program executed by invoking "java" at the command line, or that portion of the browser which controls the execution of Java applets, whichever is appropriate to the discussion at hand.
One of the beneficial results of this scheme is that it makes event programming a lot easier for the Java programmer than might otherwise be the case.
In particular, whenever there is user activity, the OS continues to send the stream of complex messages mentioned earlier in response to that activity. However, those messages are not sent directly to our Java application or applet. Instead, they are sent to the Java runtime system which performs some significant preprocessing on our behalf.
If you have a chance to review the Calvert book, take a look at the section on "message crackers." These are techniques for converting the complex messages generated by the operating system into something more meaningful. Among other things, the Java runtime system performs the message cracking effort on our behalf.
The API describes the Event class as follows:
In other words, this class is used to encapsulate the many messages generated by the operating system into objects which our Java applications and applets can more-easily handle.
The declaration for the Event class can be separated into the following parts:
public Object arg; public int clickCount; public Event evt; public int id; public int key; public int modifiers; public Object target; public long when; public int x; public int y;An understanding of these fields will be very important for the following reason:
Whenever the user engages in some activity, the operating system will send a message to the Java runtime system which will encapsulate the information regarding that activity into an object of type Event and make that object available to our program (we will discuss shortly how it is made available).
It will be the responsibility of our program to interpret that information and respond in an appropriate way with the user.
Again, the following information regarding the fields was taken from the Java API Documentation.
Some of the information in the fields, such as id, key, and modifiers, is encoded as a series of integer values.
It isn't necessary for us to know the actual numeric value to use the information, although those values are available in the API Documentation.
A symbolic constant has been defined in the class definition for each of the possible values for these fields. If we prefer, we can write code using the symbolic constants rather than the actual numeric values.
The x and y coordinate information fields describe the coordinates of the mouse, or other pointing device, when the event occurred.
The target field contains a reference to the object that generated the event. In other words, it will be the name of the reference variable by which your program identifies that object. This will become clearer later when we actually see the target field in use in a sample program.
The arg field is an object of type Object and can contain a variety of different kinds of information, "depending on the type of event."
To learn more about the makeup of this object for any particular event, you will need to examine the specifications for the actual component involved in the event. This information can be found in the API Documentation under the java.AWT.Component class.
For example, according to the API Documentation, if the component involved in the event is an object of the Button class, "its object is the string label of the button."
In other words, if a button is involved in an event, the label appearing on the button will be encapsulated as a string in the arg field of the Event object that is created by the Java runtime system.
Since this instance variable is of type Object, your program will need to downcast it to the proper type before using it. The program can then use the information for whatever purpose it might be needed.
When programming in a GUI system, all meaningful user activity is associated with some object which appears on some sort of visual display. (In this lesson, we will tend to limit our discussion to Windows-like systems. No Newtons yet please.)
There are at least two ways that user activity can become associated with an object in a typical system:
The first way to associate an object with a user activity is straightforward. Typically, it involves pointing to the object with a pointing device (such as a mouse) and then signaling an action, such as pressing a button on the mouse, selecting a string in a list, selecting a menu item, etc.
The second way to associate an object with a user activity is a little more indirect, and might require some additional explanation.
In a GUI system, one object on the display device (and only one object) has the focus at any given time. This is usually visually obvious. The object will be of a different color, have a halo, be blinking, or something to set it apart from the remaining objects on the display.
For example in a Windows system, when a text-entry object has the focus, it contains a blinking I-beam cursor. When it doesn't have the focus, it does not have such a cursor.
There are usually a variety of ways to cause an object to have the focus. Probably the most common are to click on the object with the mouse, or to use the tab key to cycle the focus through a series of objects until it reaches the desired object.
When an object "has the focus", that is the only object that will accept keyboard input. Therefore, causing an object to have the focus is preparatory to associating keyboard activity with that object.
Simply pointing to an object with the mouse and clicking on it is often all that is required to associate mouse activity with an object.
Back then to my original premise, when programming in a GUI system, all meaningful user activity is associated with some object.
The Component class has many methods, all of which are available to objects of classes derived from Component. The particular method of interest to us right now is a method named handleEvent().
This is where we finally learn how it all fits together.
In the second and third cases, handleEvent() should return false as a signal to the runtime system that additional work is required.
In this latter case, the runtime system identifies the object, if any, which contains the original object associated with the event. (Later when we study the construction of the GUI, you will learn how one object can be contained within another object.)
If such a container object can be identified, the runtime system calls the handleEvent() method of the container object to see if it wants to deal with the event.. The handleEvent() method of the container can respond in the same three ways.
This process continues until either the event is taken care of, or there are no more containers in the hierarchy.
At that point, the runtime system executes some form on default behavior relative to the event which probably means ignoring it completely.
You are all familiar with the fact that you can sometimes click on an object in a GUI and your click will be completely ignored. (Try clicking on a label object sometime.) Your click is ignored because the programmer elected not to have the program respond to that particular user activity.
Hopefully, this will help you to tie it all together in your mind.
This has been a very cursory overview of event handling in Java. In a subsequent lesson, we will discuss event handling in detail and learn about many different types of events, and what it really means to handle an event.
In the meantime, let's resume our study of the Event class, from which the objects are instantiated that make the whole thing possible.
Recall that one of the fields in the class definition is a field named id which contains information about the type of event.
The type of event is encapsulated into the Event object as an integer value.
A set of symbolic constants is defined in the class definition where a symbolic constant is provided that matches the numeric value for each possible type of event.
That list of symbolic constants, as extracted from the API Documentation follows:
public final static int ACTION_EVENT; public final static int GOT_FOCUS; public final static int KEY_ACTION; public final static int KEY_ACTION_RELEASE; public final static int KEY_PRESS; public final static int KEY_RELEASE; public final static int LIST_DESELECT; public final static int LIST_SELECT; public final static int LOAD_FILE; public final static int LOST_FOCUS; public final static int MOUSE_DOWN; public final static int MOUSE_DRAG; public final static int MOUSE_ENTER; public final static int MOUSE_EXIT; public final static int MOUSE_MOVE; public final static int MOUSE_UP; public final static int SAVE_FILE; public final static int SCROLL_ABSOLUTE; public final static int SCROLL_LINE_DOWN; public final static int SCROLL_LINE_UP; public final static int SCROLL_PAGE_DOWN; public final static int SCROLL_PAGE_UP; public final static int WINDOW_DEICONIFY; public final static int WINDOW_DESTROY; public final static int WINDOW_EXPOSE; public final static int WINDOW_ICONIFY; public final static int WINDOW_MOVED;As you can see, there twenty-seven different types of events that can be encapsulated in an Event object as a result of user activity.
Using Java, Special Edition, Second Edition by Joseph Weber, et al., (which claims to "cover new JDK 1.1 features") lists 33 different symbolic constants. In addition to all of the above, there are six more with names like WINDOW_EVENT which contain descriptions like "A general window event."
The nature of some of the event types is fairly obvious from the symbolic name. The nature of others is more cryptic.
Descriptions of all the symbolic constants are contained in the API Documentation and we won't take the time to go over them here. For example, here is the description of the KEY_PRESS type as extracted from the API Documentation.
public final static int KEY_PRESS = 401
The user has pressed a normal key.
The value encapsulated into the key field can be used to determine which key was pressed for those types of events that involve the keyboard.
One possibility is that the values in the key field will match the values that you normally associate with characters of type char. For that situation, you can cast the key field as type char and use it as you would use any ordinary character.
Another possibility is that the key which was pressed or released is a function key, an arrow key, PgUp, PgDn, etc., and is not intended to be interpreted as a normal character.
If this is the case, we would be forewarned of that circumstance because the id field would specify either type KEY_ACTION or KEY_ACTION_RELEASE.
If the id field indicates one of these two types, the following list of symbolic values can be used with the key field to determine which of the "action" keys was involved.
(Later we will be discussing an "action" event, which is unrelated to the use of the term "action" key so don't get them confused.)
The following chart of symbolic constants was extracted from the API Documentation. The names assigned to these symbolic constants should make them self-explanatory.
public final static int DOWN; public final static int END; public final static int F1; public final static int F2; public final static int F3; public final static int F4; public final static int F5; public final static int F6; public final static int F7; public final static int F8; public final static int F9; public final static int F10; public final static int F11; public final static int F12; public final static int HOME; public final static int LEFT; public final static int PGDN; public final static int PGUP; public final static int RIGHT; public final static int UP;Using pseudocode, you might use something like the following to process the key field:
if(ev.key == KEY_ACTION){ //switch statement based on values in above list }else //do something with the normal key valueNote that you should not normally be required to test for both KEY_ACTION and KEY_ACTION_RELEASE because one represents the downstroke and the other represents the upstroke on the same key. One cannot occur without the other.
Note that the action keys do not include the shift key, the control key, etc. Those keys will be discussed in the next section.
The list of symbolic constants, as extracted from the API Documentation is given below.
For this instructor, who has spent the better part of the past fifteen years working on Intel/Microsoft platforms, the names of all of these constants are self-explanatory except for the constant named META_MASK. I will presume that other platforms have a key known as the META key.
public final static int ALT_MASK public final static int CTRL_MASK; public final static int META_MASK; public final static int SHIFT_MASK;Often, you need to know if one or more of these keys were being held down when an event occurred.
For example, did the user press F1 or Ctrl-F1. You can determine this by performing a bitwise and using the value from the modifiers field and one of the symbolic constants listed above.
For example, to determine if the alt key was being held down when the event occurred, you might use code such as the following:
public Event(Object target, int id, Object arg); public Event(Object target, long when, int id, int x, int y, int key, int modifiers); public Event(Object target, long when, int id, int x, int y, int key, int modifiers, Object arg);The availability of these constructors suggests some interesting possibilities.
Recall that in the earlier discussion, I said that the Java runtime system responds to a message from the OS by creating an object of type Event and passing it as a parameter to the method named handleEvent() for the object associated with the event.
So far, I haven't said anything about you writing code that instantiates objects of type Event. However, since the constructors exist and are accessible, there is nothing to stop you from doing so.
Access to the constructors suggests that the code in a java application has the capability to instantiate and dispatch objects of type Event just as though they were instantiated by the runtime system in response to user activity.
We already know enough to be able to instantiate an an object of type Event, because we understand (or can figure out) how to provide the necessary arguments for the constructors shown above.
We don't know yet how to dispatch the object, but we will learn how to do that later.
Then we can create some really cool effects. (The potential for practical jokes is almost endless for programs that appear to have a mind of their own insofar as the GUI is concerned.)
The Event class provides six methods as listed below. Therefore, objects of the class have these instance methods in addition to methods that they inherit from the Object class.
public boolean controlDown(); public boolean metaDown(); protected String paramString(); public boolean shiftDown(); public String toString(); public void translate(int dX, int dY);
If you use the Event object to invoke these methods, the boolean return value will tell you if the corresponding modifier key was being held down when the event occurred. These are convenience methods which make it possible for you to easily determine something that you can also determine using the bitwise and operation described earlier.
Although we haven't discussed applets in any detail yet, there is a param tag that can be used in the HTML file that invokes an applet. Maybe this has something to do with that.
I suspect that this will not remain a mystery for long. Some bright inquisitive student will probably discover more about the use of the method and send me an explanation via e-mail.
public void translate(int dx, int dy) Translates this event so that its x and y position are increased by dx and dy respectively. Parameters: dX - the amount to translate the x coordinate dY - the amount to translate the y coordinateThis method could be useful in any number of situations involving graphics.
The API Documentation doesn't make it easy to determine exactly what information about the object will be returned, but it will be easy enough to find out later when we start using these methods in sample programs.
The user does something that that may be meaningful to the program.
The OS sends a message to the Java runtime system containing information about the users activity.
The runtime system encapsulates the information in an object of type Event and passes it as a parameter to the handleEvent() method for the object on which the activity was performed or which was associated with the activity.
The handleEvent() method either processes the event and returns true, or doesn't process the event and returns false.
If the handleEvent() method returns false, the runtime system attempts to invoke the same method on the object which contains (is the parent of) the original object.
This process continues until the event is either handled within the Java program, or given back to the runtime system for default handling.
Along the way, if the Java program decides to process the event, there are a number of symbolic constants and several methods that can be used to interpret the information encapsulated in the object.
Some, but probably not all of the information encapsulated in the object is likely to be used in the processing of the event.
Not all of the fields in the Event object are germane to every type of event. For example, when the user clicks on an "OK" button, the x and y coordinates of the mouse pointer are usually not of great importance.
-end-