Day 12

Managing Simple Events and Interactivity

by Laura Lemay


CONTENTS

Java events are part of the Java awt (Abstract Windowing Toolkit) package. An event is the way that the awt communicates to you, as the programmer, and to other Java awt components that something has happened. That something can be input from the user (mouse movements or clicks, keypresses), changes in the system environment (a window opening or closing, the window being scrolled up or down), or a host of other things that might, in some way, affect the operation of the program.

In other words, whenever just about anything happens to a Java awt component, including an applet, an event is generated. Some events are handled by the awt or by the environment your applet is running in (the browser) without you needing to do anything. paint() methods, for example, are generated and handled by the environment-all you have to do is tell the awt what you want painted when it gets to your part of the window. However, you may need to know about some events, such as a mouse click inside the boundaries of your applet. By writing your Java programs to handle these kinds of events, you can get input from the user and have your applet change its behavior based on that input.

Today you'll learn about managing simple events, including the following basics:

You'll also learn about the handleEvent() method, which is the basis for collecting, handling, and passing on events of all kinds from your applet to other components of the window or of your applet itself. Tomorrow you'll learn how to combine events with other awt components to create a complete interface for your applet.

Mouse Clicks

Let's start with the most common event you might be interested in: mouse clicks. Mouse-click events occur when your user clicks the mouse somewhere in the body of your applet. You can intercept mouse clicks to do very simple things-for example, to toggle the sound on and off in your applet, to move to the next slide in a presentation, or to clear the screen and start over-or you can use mouse clicks in conjunction with mouse movements to perform more complex motions inside your applet.

Mouse Down and Mouse Up Events

When you click the mouse once, the awt generates two events: a mouse down event when the mouse button is pressed and a mouse up event when the button is released. Why two individual events for a single mouse action? Because you may want to do different things for the "down" and the "up." For example, look at a pull-down menu. The mouse down extends the menu, and the mouse up selects an item (with mouse drags between-but you'll learn about that one later). If you have only one event for both actions (mouse up and mouse down), you cannot implement that sort of user interaction.

Handling mouse events in your applet is easy-all you have to do is override the right method definition in your applet. That method will be called when that particular event occurs. Here's an example of the method signature for a mouse down event:

public boolean mouseDown(Event evt, int x, int y) {
...
}

The mouseDown() method (and the mouseUp() method as well) takes three parameters: the event itself and the x and y coordinates where the mouse down or mouse up event occurred.

The evt argument is an instance of the class Event. All system events generate an instance of the Event class, which contains information about where and when the event took place, the kind of event it is, and other information that you might want to know about this event. Sometimes having a handle to that Event object is useful, as you'll discover later in this section.

The x and the y coordinates of the event, as passed in through the x and y arguments to the mouseDown() method, are particularly nice to know because you can use them to determine precisely where the mouse click took place. So, for example, if the mouse down event were over a graphical button, you could activate that button.

For example, here's a simple method that prints out information about a mouse down when it occurs:

public boolean mouseDown(Event evt, int x, int y) {
    System.out.println("Mouse down at " + x + "," + y);
    return true;

By including this method in your applet, every time your user clicks the mouse inside your applet, this message will get printed. The awt system calls each of these methods when the actual event takes place.

Note
Unlike with Java applications, where System.out.println() outputs to the screen, the output that appears in applets varies from system to system and browser to browser. Netscape has a special window called the Java console that must be visible for you to see the output. Internet Explorer logs Java output to a separate file. Check with your environment to see where Java output from applets is sent.

Note that this method, unlike the other system methods you've studied this far, returns a boolean value instead of not returning anything (void). This will become important tomorrow when you create user interfaces and then manage input to these interfaces; having an event handler method return true or false determines whether a given component can intercept an event or whether it needs to pass it on to the enclosing component. The general rule is that if your method intercepts and does something with the event, it should return true. If for any reason the method doesn't do anything with that event, it should return false so that other components in the system can have a chance to see that event. In most of the examples in today's lesson, you'll be intercepting simple events, so most of the methods here will return true. Tomorrow you'll learn about nesting components and passing events up the component hierarchy.

The second half of the mouse click is the mouseUp() method, which is called when the mouse button is released. To handle a mouse up event, add the mouseUp() method to your applet: mouseUp() looks just like mouseDown():

public boolean mouseUp(Event evt, int x, int y) {
    ....
}

An Example: Spots

In this section you'll create an example of an applet that uses mouse events-mouse down events in particular. The Spots applet starts with a blank screen and then sits and waits. When you click the mouse on that screen, a blue dot is drawn. You can place up to 10 dots on the screen. Figure 12.1 shows the Spots applet.

Figure 12.1 : The Spots applet.

Let's start from the beginning and build this applet, starting from the initial class definition:

import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;

public class Spots extends java.applet.Applet {

    final int MAXSPOTS = 10;
    int xspots[] = new int[MAXSPOTS];
    int yspots[] = new int[MAXSPOTS];
    int currspots = 0;

}

This class uses three other awt classes: Graphics, Color, and Event. That last class, Event, needs to be imported in any applets that use events. The class has four instance variables: a constant to determine the maximum number of spots that can be drawn, two arrays to store the x and y coordinates of the spots that have already been drawn, and an integer to keep track of the number of the current spot.

Note
This class doesn't include the implements Runnable words in its definition. As you'll see later as you build this applet, it also doesn't have a run() method. Why not? Because it doesn't actually do anything on its own-all it does is wait for input and then do stuff when input happens. There's no need for threads if your applet isn't actively doing something all the time.

Let's start by adding the init() method, which does only one thing: set the background color to white:

public void init() {
    setBackground(Color.white);
}

We've set the background here in init() instead of in paint() as you have in past examples because you need to set the background only once. Because paint() is called repeatedly each time a new spot is added, setting the background in the paint() method unnecessarily slows down that method. Putting it here is a much better idea.

The main action of this applet occurs with the mouseDown() method, so let's add that one now:

public boolean mouseDown(Event evt, int x, int y) {
    if (currspots < MAXSPOTS) {
        addspot(x,y);
        return true;
    }
    else {
       System.out.println("Too many spots.");
       return false;
    }
}

When the mouse click occurs, the mouseDown() method tests to see whether there are fewer than 10 spots. If so, it calls the addspot() method (which you'll write soon) and returns true (the mouse down event was intercepted and handled). If not, it just prints an error message and returns false.

What does addspot() do? It adds the coordinates of the spot to the arrays that store the coordinates, increments the currspots variable, and then calls repaint():

void addspot(int x, int y) {
    xspots[currspots] = x;
    yspots[currspots] = y;
    currspots++;
    repaint();
}

You may be wondering why you have to keep track of all the past spots in addition to the current spot. It's because of repaint(): Each time you paint the screen, you have to paint all the old spots in addition to the newest spot. Otherwise, each time you painted a new spot, the older spots would get erased. Now, on to the paint() method:

public void paint(Graphics g) {
    g.setColor(Color.blue);
    for (int i = 0; i < currspots; i++) {
        g.fillOval(xspots[i] -10, yspots[i] - 10, 20, 20);
    }
}

Inside paint(), you just loop through the spots you've stored in the xspots and yspots arrays, painting each one (actually, painting them a little to the right and upward so that the spot is painted around the mouse pointer rather than below and to the right).

That's it! That's all you need to create an applet that handles mouse clicks. Everything else is handled for you. You have to add the appropriate behavior to mouseDown() or mouseUp() to intercept and handle that event. Listing 12.1 shows the full text for the Spots applet.


Listing 12.1. The Spots applet.
 1: import java.awt.Graphics;
 2: import java.awt.Color;
 3: import java.awt.Event;
 4:
 5: public class Spots extends java.applet.Applet {
 6:
 7:     final int MAXSPOTS = 10;
 8:     int xspots[] = new int[MAXSPOTS];
 9:     int yspots[] = new int[MAXSPOTS];
10:     int currspots = 0;
11:
12:     public void init() {
13:         setBackground(Color.white);
14:     }
15:
16:     public boolean mouseDown(Event evt, int x, int y) {
17:         if (currspots < MAXSPOTS) {
18:             addspot(x,y);
19:             return true;
20:         }
21:         else {
22:            System.out.println("Too many spots.");
23:            return false;
24:         }
25:     }
26:
27:     void addspot(int x,int y) {
28:          xspots[currspots] = x;
29:          yspots[currspots] = y;
30:          currspots++;
31:          repaint();
32:     }
33:
34:     public void paint(Graphics g) {
35:         g.setColor(Color.blue);
36:         for (int i = 0; i < currspots; i++) {
37:               g.fillOval(xspots[i] - 10, yspots[i] - 10, 20, 20);
38:        }
39:     }
40: }

Double-Clicks

What if the mouse event you're interested in is more than a single mouse click-what if you want to track double- or triple-clicks? The Java Event class provides a variable for tracking this information, called clickCount. clickCount is an integer representing the number of consecutive mouse clicks that have occurred (where "consecutive" is usually determined by the operating system or the mouse hardware). If you're interested in multiple mouse clicks in your applets, you can test this value in the body of your mouseDown() method, like this:

public boolean mouseDown(Event evt, int x, int y) {
    switch (evt.clickCount) {
      case 1:  // single-click
      case 2:  // double-click
      case 3:  // triple-click
      ....
    }
}

Mouse Movements

Every time the mouse is moved a single pixel in any direction, a mouse move event is generated. There are two mouse movement events: mouse drags, where the movement occurs with the mouse button pressed down, and plain mouse movements, where the mouse button isn't pressed.

To manage mouse movement events, use the mouseDrag() and mouseMove() methods.

Mouse Drag and Mouse Move Events

The mouseDrag() and mouseMove() methods, when included in your applet code, intercept and handle mouse movement events. Mouse move and move drag events are generated for every pixel change the mouse moves, so a mouse movement from one side of the applet to the other may generate hundreds of events. The mouseMove() method, for plain mouse pointer movements without the mouse button pressed, looks much like the mouse-click methods:

public boolean mouseMove(Event evt, int x, int y) {
    ...
}

The mouseDrag() method handles mouse movements made with the mouse button pressed down (a complete dragging movement consists of a mouse down event, a series of mouse drag events for each pixel the mouse is moved, and a mouse up when the button is released). The mouseDrag() method looks like this:

public boolean mouseDrag(Event evt, int x, int y) {
    ...
}

Note that for both the mouseMove() and mouseDrag() methods, the arguments for the x and y coordinates are the new location of the mouse, not its starting location.

Mouse Enter and Mouse Exit Events

Finally, there are the mouseEnter() and mouseExit() methods. These two methods are called when the mouse pointer enters or exits an applet or a portion of that applet. (In case you're wondering why you might need to know this, it's more useful on awt components that you might put inside an applet. You'll learn more about the awt tomorrow.)

Both mouseEnter() and mouseExit() have signatures similar to the mouse click methods-three arguments: the event object and the x and y coordinates of the point where the mouse entered or exited the applet. These examples show the signatures for mouseEnter() and mouseExit():

public boolean mouseEnter(Event evt, int x, int y) {
    ...
}

public boolean mouseExit(Event evt, int x, int y) {
    ...
}

An Example: Drawing Lines

Examples always help to make concepts more concrete. In this section you'll create an applet that enables you to draw straight lines on the screen by dragging from the startpoint to the endpoint. Figure 12.2 shows the applet at work.

Figure 12.2 : Drawing lines.

As with the Spots applet (on which this applet is based), let's start with the basic definition and work our way through it, adding the appropriate methods to build the applet. Here's a simple class definition for the Lines applet, with a number of initial instance variables and a simple init() method:

import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Point;

public class Lines extends java.applet.Applet {

    final int MAXLINES = 10;
    Point starts[] = new Point[MAXLINES]; // starting points
    Point ends[] = new Point[MAXLINES];    // ending points
    Point anchor;    // start of current line
    Point currentpoint; // current end of line
    int currline = 0; // number of lines

    public void init() {
        setBackground(Color.white);
    }
}

This applet adds a few more things than Spots. Unlike Spots, which keeps track of individual integer coordinates, this one keeps track of Point objects. Points represent an x and a y coordinate, encapsulated in a single object. To deal with points, you import the Point class and set up a bunch of instance variables that hold points:

Finally, the init() method, as in the Spots applet, sets the background of the applet to white.

The three main events this applet deals with are mouseDown(), to set the anchor point for the current line, mouseDrag(), to animate the current line as it's being drawn, and mouseUp(), to set the ending point for the new line. Given that you have instance variables to hold each of these values, it's merely a matter of plugging the right variables into the right methods. Here's mouseDown(), which sets the anchor point (but only if we haven't exceeded the maximum number of lines):

public boolean mouseDown(Event evt, int x, int y) {
   if (currline < MAXLINES) {
       anchor = new Point(x,y);
       return true;
  }
   else  {
      System.out.println("Too many lines.");
      return false;
   }
}

While the mouse is being dragged to draw the line, the applet animates the line being drawn. As you drag the mouse around, the new line moves with it from the anchor point to the tip of the mouse. The mouseDrag() event contains the current point each time the mouse moves, so use that method to keep track of the current point (and to repaint for each movement so the line "animates"). Note that if we've exceeded the maximum number of lines, we won't want to do any of this. Here's the mouseDrag() method to do all those things:

public boolean mouseDrag(Event evt, int x, int y) {
   if (currline < MAXLINES) {
       currentpoint = new Point(x,y);
       repaint();
       return true;
    }
   else return false;
}

The new line doesn't get added to the arrays of old lines until the mouse button is released. Here's mouseUp(), which tests to make sure you haven't exceeded the maximum number of lines before calling the addline() method (described next):

public boolean mouseUp(Event evt, int x, int y) {
     if (currline < MAXLINES) {
         addline(x,y);
         return true;
     }
     else return false;
}

The addline() method is where the arrays of starting and ending points get updated and where the applet is repainted to take the new line into effect:

void addline(int x,int y) {
    starts[currline] = anchor;
    ends[currline] = new Point(x,y);
    currline++;
    currentpoint = null;
    anchor = null;
    repaint();
}

Note that in this method you also set currentpoint and anchor to null. Why? Because the current line you were drawing is over. By setting these variables to null, you can test for that value in the paint() method to see whether you need to draw a current line.

Painting the applet means drawing all the old lines stored in the starts and ends arrays, as well as drawing the current line in progress (whose endpoints are in anchor and currentpoint, respectively). To show the animation of the current line, draw it in blue. Here's the paint() method for the Lines applet:

public void paint(Graphics g) {

    // Draw existing lines
    for (int i = 0; i < currline; i++) {
        g.drawLine(starts[i].x, starts[i].y,
            ends[i].x, ends[i].y);
    }

    // Draw current line
    g.setColor(Color.blue);
    if (currentpoint != null)
        g.drawLine(anchor.x, anchor.y,
            currentpoint.x, currentpoint.y);
}

In paint(), when you're drawing the current line, you test first to see whether currentpoint is null. If it is, the applet isn't in the middle of drawing a line, so there's no reason to try drawing a line that doesn't exist. By testing for currentpoint (and by setting currentpoint to null in the addline() method), you can paint only what you need.

That's it-just 60 lines of code and a few basic methods, and you have a very basic drawing application in your Web browser. Listing 12.2 shows the full text of the Lines applet so that you can put the pieces together.


Listing 12.2. The Lines applet.
 1: import java.awt.Graphics;
 2: import java.awt.Color;
 3: import java.awt.Event;
 4: import java.awt.Point;
 5:
 6: public class Lines extends java.applet.Applet {
 7:
 8:     final int MAXLINES = 10;
 9:     Point starts[] = new Point[MAXLINES]; // starting points
10:     Point ends[] = new Point[MAXLINES];    // endingpoints
11:     Point anchor;    // start of current line
12:     Point currentpoint; // current end of line
13:     int currline = 0; // number of lines
14:
15:     public void init() {
16:         setBackground(Color.white);
17:     }
18:
19:     public boolean mouseDown(Event evt, int x, int y) {
20:        if (currline < MAXLINES) {
21:            anchor = new Point(x,y);
22:            return true;
23:       }
24:        else  {
25:           System.out.println("Too many lines.");
26:           return false;
27:        }
28:     }
29:
30:    public boolean mouseUp(Event evt, int x, int y) {
31:         if (currline < MAXLINES) {
32:             addline(x,y);
33:             return true;
34:         }
35:         else return false;
36:    }
37:
38:     public boolean mouseDrag(Event evt, int x, int y) {
39:        if (currline < MAXLINES) {
40:            currentpoint = new Point(x,y);
41:            repaint();
42:            return true;
43:         }
44:        else return false;
45:     }
46:
47:     void addline(int x,int y) {
48:         starts[currline] = anchor;
49:         ends[currline] = new Point(x,y);
50:         currline++;
51:         currentpoint = null;
52:         anchor = null;
53:         repaint();
54:     }
55:
56:     public void paint(Graphics g) {
57:
58:         // Draw existing lines
59:         for (int i = 0; i < currline; i++) {
50:             g.drawLine(starts[i].x, starts[i].y,
51:                  ends[i].x, ends[i].y);
52:         }
53:
54:         // draw current line
55:         g.setColor(Color.blue);
56:         if (currentpoint != null)
57:             g.drawLine(anchor.x,anchor.y,
58:             currentpoint.x,currentpoint.y);
59:     }
60:}

Keyboard Events

A keyboard event is generated whenever a user presses a key on the keyboard. By using keyboard events, you can get hold of the values of the keys the user pressed to perform an action or merely to get character input from the users of your applet.

The keyDown() and keyUp() Methods

To capture a keyboard event, use the keyDown() method:

public boolean keyDown(Event evt, int key) {
    ...
}

The keys generated by key down events (and passed into keyDown() as the key argument) are integers representing Unicode character values, which include alphanumeric characters, function keys, tabs, returns, and so on. To use them as characters (for example, to print them), you need to cast them to characters:

currentchar = (char)key;

Here's a simple example of a keyDown() method that does nothing but print the key you just typed in both its Unicode and character representation (it can be fun to see which key characters produce which values):

public boolean keyDown(Event evt, int key) {
    System.out.println("ASCII value: " + key);
    System.out.println("Character: " + (char)key);
    return true;
}

As with mouse clicks, each key down event also has a corresponding key up event. To intercept key up events, use the keyUp() method:

public booklean keyUp(Event evt, int key)  {
   ...
}

Default Keys

The Event class provides a set of class variables that refer to several standard nonalphanumeric keys, such as the arrow and function keys. If your applet's interface uses these keys, you can provide more readable code by testing for these names in your keyDown() method rather than testing for their numeric values (and you're also more likely to be cross-platform if you use these variables). For example, to test whether the up arrow was pressed, you might use the following snippet of code:

if (key == Event.UP) {
    ...
}

Because the values these class variables hold are integers, you also can use the switch statement to test for them.

Table 12.1 shows the standard event class variables for various keys and the actual keys they represent.

Table 12.1. Standard keys defined by the Event class.

Class VariableRepresented Key
Event.HOME The Home key
Event.END The End key
Event.PGUP The Page Up key
Event.PGDN The Page Down key
Event.UP The up arrow
Event.DOWN The down arrow
Event.LEFT The left arrow
Event.RIGHT The right arrow
Event.f1 The f1 key
Event.f2 The f2 key
Event.f3 The f3 key
Event.f4 The f4 key
Event.f5 The f5 key
Event.f6 The f6 key
Event.f7 The f7 key
Event.f8 The f8 key
Event.f9 The f9 key
Event.f10 The f10 key
Event.f11 The f11 key
Event.f12 The f12 key

An Example: Entering, Displaying, and Moving Characters

Let's look at an applet that demonstrates keyboard events. With this applet, you type a character, and that character is displayed in the center of the applet window. You then can move that character around on the screen with the arrow keys. Typing another character at any time changes the character as it's currently displayed. Figure 12.3 shows an example.

Figure 12.3 : The Keys applet.

Note
To get this applet to work, you might have to click once with the mouse on it in order for the keys to show up. This is to make sure the applet has the keyboard focus (that is, that its actually listening when you type characters on the keyboard).

This applet is actually less complicated than the previous applets you've used. This one has only three methods: init(), keyDown(), and paint(). The instance variables are also simpler because the only things you need to keep track of are the x and y positions of the current character and the values of that character itself. Here's the initial class definition:

import java.awt.Graphics;
import java.awt.Event;
import java.awt.Font;
import java.awt.Color;

public class Keys extends java.applet.Applet {

    char currkey;
    int currx;
    int curry;
}

Let's start by adding an init() method. Here, init() is responsible for three things: setting the background color, setting the applet's font (here, 36-point Helvetica bold), and setting the beginning position for the character (the middle of the screen, minus a few points to nudge it up and to the right):

public void init() {
    currx = (size().width / 2) - 8;
    curry = (size().height / 2) - 16;
    setBackground(Color.white);
    setFont(new Font("Helvetica", Font.BOLD, 36));
}

Because this applet's behavior is based on keyboard input, the keyDown() method is where most of the work of the applet takes place:

public boolean keyDown(Event evt, int key) {
   switch (key) {
         case Event.DOWN:
             curry += 5;
             break;
         case Event.UP:
             curry -= 5;
             break;
         case Event.LEFT:
             currx -= 5;
             break;
         case Event.RIGHT:
             currx += 5;
             break;
         default:
             currkey = (char)key;
         }
         repaint();
         return true;
}

In the center of the keyDown() applet is a switch statement that tests for different key events. If the event is an arrow key, the appropriate change is made to the character's position. If the event is any other key, the character itself is changed (that's the default part of the switch). The method finishes up with a repaint() and returns true.

The paint() method here is almost trivial; just display the current character at the current position. However, note that when the applet starts up, there's no initial character and nothing to draw, so you have to take that into account. The currkey variable is initialized to 0, so you paint the applet only if currkey has an actual value:

public void paint(Graphics g) {
    if (currkey != 0) {
        g.drawString(String.valueOf(currkey), currx,curry);
    }
}

Listing 12.3 shows the complete source code for the Keys applet.


Listing 12.3. The Keys applet.
 1: import java.awt.Graphics;
 2: import java.awt.Event;
 3: import java.awt.Font;
 4: import java.awt.Color;
 5:
 6: public class Keys extends java.applet.Applet {
 7:
 8:     char currkey;
 9:     int currx;
10:    int curry;
11:
12:     public void init() {
13:         currx = (size().width / 2) -8;  // default
14:         curry = (size().height / 2) -16;
15:
16:         setBackground(Color.white);
17:         setFont(new Font("Helvetica",Font.BOLD,36));
18:     }
19:
20:     public boolean keyDown(Event evt, int key) {
21:         switch (key) {
22:         case Event.DOWN:
23:             curry += 5;
24:             break;
25:         case Event.UP:
26:             curry -= 5;
27:             break;
28:         case Event.LEFT:
29:             currx -= 5;
30:             break;
31:         case Event.RIGHT:
32:             currx += 5;
33:             break;
34:         default:
35:             currkey = (char)key;
36:         }
37:
38:         repaint();
39:         return true;
40:     }
41:
42:     public void paint(Graphics g) {
43:         if (currkey != 0) {
44:             g.drawString(String.valueOf(currkey), currx,curry);
45:         }
46:     }
47: }

Testing for Modifier Keys and Multiple Mouse Buttons

Shift, Control (Ctrl), and Meta are modifier keys. They don't generate key events themselves, but when you get an ordinary mouse or keyboard event, you can test to see whether those modifier keys were held down when the event occurred. Sometimes it may be obvious-shifted alphanumeric keys produce different key events than unshifted ones, for example. For other events, however-mouse events in particular-you may want to handle an event with a modifier key held down differently from a regular version of that event.

Note
The Meta key is commonly used on UNIX systems; it's usually mapped to Alt on pc keyboards and Command (apple) on Macintoshes.

The Event class provides three methods for testing whether a modifier key is held down: shiftDown(), metaDown(), and controlDown(). All return boolean values based on whether that modifier key is indeed held down. You can use these three methods in any of the event- handling methods (mouse or keyboard) by calling them on the event object passed into that method:

public boolean mouseDown(Event evt, int x, int y ) {
    if (evt.shiftDown())
         // handle shift-click
    else // handle regular click
}

One other significant use of these modifier key methods is to test for which mouse button generated a particular mouse event on systems with two or three mouse buttons. By default, mouse events (such as mouse down and mouse drag) are generated regardless of which mouse button is used. However, Java events internally map left and middle mouse actions to meta and Control (Ctrl) modifier keys, respectively, so testing for the key tests for the mouse button's action. By testing for modifier keys, you can find out which mouse button was used and execute different behavior for those buttons than you would for the left button. Use an if statement to test each case, like this:

public boolean mouseDown(Event evt, int x, int y ) {
    if (evt.metaDown())
         // handle a right-click
    else if (evt.controlDown())
        // handle a middle-click
    else // handle a regular click
}

Note that because this mapping from multiple mouse buttons to keyboard modifiers happens automatically, you don't have to do a lot of work to make sure your applets or applications work on different systems with different kinds of mouse devices. Because left-button or right-button mouse clicks map to modifier key events, you can use those actual modifier keys on systems with fewer mouse buttons to generate exactly the same results. So, for example, holding down the Ctrl key and clicking the mouse on Windows or holding the Control key on the Macintosh is the same as clicking the middle mouse button on a three-button mouse; holding down the Command (apple) key and clicking the mouse on the Mac is the same as clicking the right mouse button on a two- or three-button mouse.

Consider, however, that the use of different mouse buttons or modifier keys may not be immediately obvious if your applet or application runs on a system with fewer buttons than you're used to working with. Consider restricting your interface to a single mouse button or to providing help or documentation to explain the use of your program in this case.

The awt Event Handler

The default methods you've learned about today for handling basic events in applets are actually called by a generic event handler method called handleEvent(). The handleEvent() method is how the awt generically deals with events that occur between application components and events based on user input.

In the default handleEvent() method, basic events are processed and the methods you learned about today are called. To handle events other than those mentioned here (for example, events for scrollbars or for other user interface elements-which you'll learn about on Day 13, "Creating User Interfaces with the awt"), to change the default event handling behavior, or to create and pass around your own events, you need to override handleEvent() in your own Java programs. The handleEvent() method looks like this:

public boolean handleEvent(Event evt) {
    ...
}

To test for specific events, examine the id instance variable of the Event object that gets passed in to handleEvent(). The event ID is an integer, but fortunately the Event class defines a whole set of event IDs as class variables whose names you can test for in the body of handleEvent(). Because these class variables are integer constants, a switch statement works particularly well. For example, here's a simple handleEvent() method to print out debugging information about mouse events:

public boolean handleEvent(Event evt) {
    switch (evt.id) {
    case Event.MOUSE_DOWN:
        System.out.println("MouseDown: " +
                evt.x + "," + evt.y);
        return true;
    case Event.MOUSE_UP:
        System.out.println("MouseUp: " +
                evt.x + "," + evt.y);
        return true;
    case Event.MOUSE_MOVE:
        System.out.println("MouseMove: " +
                evt.x + "," + evt.y);
        return true;
    case Event.MOUSE_DRAG:
        System.out.println("MouseDrag: " +
                evt.x + "," + evt.y);
        return true;
    default:
        return false;
    }
}

You can test for the following keyboard events:

You can test for these mouse events:

In addition to these events, the Event class has a whole suite of methods for handling awt components. You'll learn more about these events tomorrow.

Note that if you override handleEvent() in your class, none of the default event-handling methods you learned about today will get called unless you explicitly call them in the body of handleEvent(), so be careful if you decide to do this. One way to get around this is to test for the event you're interested in, and if that event isn't it, call super.handleEvent() so that the superclass that defines handleEvent() can process things. Here's an example of how to do this:

public boolean handleEvent(Event evt) {
    if (evt.id == Event.MOUSE_DOWN) {
        // process the mouse down
        return true;
    } else {
        return super.handleEvent(evt);
    }
}

Also, note that like the individual methods for individual events, handleEvent() also returns a boolean. The value you return here is particularly important; if you pass handling of the event to another method, you must return false. If you handle the event in the body of this method, return true. If you pass the event up to a superclass, that method will return true or false; you don't have to yourself.

Summary

Handling events in Java's Abstract Windowing Toolkit is easy. Most of the time all you need to do is stick the right method in your applet code, and your applet intercepts and handles that event at the right time. Here are some of the basic events you can manage in this way:

All events in the awt generate an Event object; inside that object, you can find out information about the event, when it occurred, and its x and y coordinates (if applicable). You can also test that event to see whether a modifier key was pressed when the event occurred, by using the shiftDown(), controlDown(), and metaDown() methods.

Finally, there is the handleEvent() method, the "parent" of the individual event methods. The handleEvent() method is actually what the Java system calls to manage events; the default implementation calls the individual method events where necessary. To override how methods are managed in your applet, override handleEvent().

Q&A

Q:
In the Spots applet, the spot coordinates are stored in arrays, which have a limited size. How can I modify this applet so that it will draw an unlimited number of spots?
A:
You can do one of a couple things:
The first thing to do is test, in your addspot() method, whether the number of spots has exceeded MAXSPOTS. Then create a bigger array, copy the elements of the old array into that bigger array (use the System.arraycopy() method to do that), and reassign the x and y arrays to that new, bigger array.
The second thing to do is to use the Vector class. Vector, part of the java.util package, implements an array that is automatically growable-sort of like a linked list is in other languages. The disadvantage of Vector is that to put something into Vector, it has to be an actual object. This means you'll have to cast integers to Integer objects, and then extract their values from Integer objects to treat them as integers again. The Vector class allows you to access and change elements in the Vector just as you can in an array (by using method calls, rather than array syntax). Check it out.
Q:
What's a Meta key?
A:
It's popular in UNIX systems, and often mapped to Alt on most keyboards (Option on Macs). Because Shift and Control (Ctrl) are much more popular and widespread, it's probably a good idea to base your interfaces on those modifier keys if you can.
Q:
How do I test to see whether the Return key has been pressed?
A:
Return (line feed) is character 10; Enter (carriage return) is character 13. Note that different platforms may send different keys for the actual key marked Return. In particular, UNIX systems send line feeds, Macintoshes send carriage returns, and DOS systems send both. So to provide cross-platform behavior, you may want to test for both line feed and carriage return.
The word from the Java team is that a Return is a Return is a Return, regardless of the platform. However, at the time of this writing, it is questionable whether this is currently true in the Java Developer's Kit. You may want to check the API documentation for the Event class to see whether this has changed in the interim.
Q:
I looked at the API for the Event class, and there are many more event types listed there than the ones you mention today.
A:
Yes. The Event class defines many different kinds of events, both for general user input, such as the mouse and keyboard events you learned about here, and also events for managing changes to the state of user interface components, such as windows and scrollbars. Tomorrow you'll learn about those other events.