CS 124, Spring 2013
Lab 11: Mouse Events

In the previous lab, you worked with keyboard events, but the details of setting up the event handling were done for you (in the method installKeyPress). This lab is a tutorial that will guide you through setting up mouse event handling from scratch. You will be working with the same Sprite and SpritePanel classes that you used in Lab 10. In this lab, you will make it possible for the user to interact with the panel using the mouse. Please follow these instructions carefully to set up your lab11 project in Eclipse:

This lab is due next Thursday, as usual. It is a short lab, and you might well finish it before the end of the lab period. If so, you can work on Lab 10. Remember that Lab 10 is due next Tuesday.

Basic Setup for Events

To handle events in Java, you need to work with classes that are defined in package java.awt.event. To make this easy, you should add the following line to the list of imports at the top of your class (do this -- and everything else in this lab -- in your copy of SpriteDemo1.java):

import java.awt.event.*;

You will need to know something about interfaces, which are covered in Section 5.7 in the textbook. Basically, an interface is similar to a class, except that it only specifies that methods must exist; it does not actually define those methods. A class that "implements" the interface must define the methods. Implementing an interface is like extending a class. To use mouse events, you need an object that implements the MouseListener interface. We will use a nested class for this purpose. A nested class is one that is defined inside another class. Putting this all together, you should add the following lines to your copy of SpriteDemo1.java (this goes inside the SpriteDemo1 class, but not inside any method or constructor):

private class MouseHandler implements MouseListener {

}

When you do this, Eclipse will show an error. The MouseHandler class is supposed to implement the MouseListener, but to do that it has to implement the methods specified by the interface. There are five of them. You can satisfy the requirements of the interface by adding the following methods inside the MouseHandler class:

public void mouseEntered(MouseEvent evt) {
}
public void mouseExited(MouseEvent evt) {
}
public void mousePressed(MouseEvent evt) {
}
public void mouseReleased(MouseEvent evt) {
}
public void mouseClicked(MouseEvent evt) {
}

These methods are meant to be called when the user does certain things to the panel with the mouse, but they will not be called unless you have created an object of type MouseHandler and registered it to received mouse events from the panel. You should do this in the constructor when the panel is first being created. Add the following lines to the constructor of the SpriteDemo1 class:

MouseHandler listener;
listener = new MouseHandler();
addMouseListener(listener);

It is the last command, addMouseListener(listener) that registers the MouseHandler object to "listen" for mouse events. This tells the panel that when a mouse event occurs in the panel, the event should be sent to the listener object for processing. More generally, the listener can be any object that implements the MouseListener interface.

If you have done everything correctly, your MouseHandler is now set up to receive mouse events that occur when the user uses the mouse to interact with the panel. Now, you just need to program what will happen when the events occur. You do that by providing definitions for the methods in the MouseHandler class.

Coding mouseEntered and mouseExited

The mouseEntered and mouseExited events occur as the user moves the mouse into and out of the panel. If the mouse is outside the panel and the user moves it inside, then a mouseEntered event occurs on the panel. If the mouse is inside the panel and the user moves it outside, a mouseExited event occurs on the panel. These events are sent to any object that is listening for mouse events on the panel, by calling the listener's mouseEntered() or mouseExited() methods.

It's actually not very common to do anything in response to these events, but for this lab, you should respond to the events by changing the background color of the panel. You can change the background color of the panel by calling its method

setBackground(c)

where c is a Color. You should use this method in mouseEntered() and mouseExited() to change the background color of the panel when the mouse moves into the panel and to change it back to white when the mouse moves out of the panel.

You can test the program at this point to see whether your code is working.

(By the way, you will be calling a method from the panel object inside the MouseHandler class. This illustrates an important point: A nested class can see all the variables and methods from the class in which it is nested. This even includes the private variables and methods.)

Coding mousePressed

A mousePressed event occurs on the panel when the user presses one of the mouse buttons while the mouse is over the panel. The response is to call the listener's mousePressed() method. What can you do in this program in response to a mousePressed event? A natural idea is to add a sprite at the point where the user clicked the mouse

An event in Java is represented by an object. A mouse event is represented by an object of type MouseEvent. The object contains information about the event. In the case of a MouseEvent, this includes the x- and y-coordinates of the location of the mouse.

Notice that the event-handling methods in MouseHandler all have a parameter of type MouseEvent, named evt in the method definitions given above. You can call evt.getX() to get the x-coordinate of the mouse's location, and you can call evt.getY() to get the y-coordinate of the mouse's location.

You should write a definition for the mousePressed() method that will add a sprite to the panel. The location of the sprite should be set equal to the location of the mouse. You can decide what kind of sprite to use and whether you want to give it a random velocity.

Don't forget to test your program before preceding to the next section!

Coding mouseReleased

A mousePressed event occurs at the moment that the user presses a mouse button. There is also an event that occurs when the user releases the button, a mouseReleased event. When the user moves the mouse between pressing and releasing the buttton, it is called dragging the mouse. As an example, let's use dragging to allow the user to set the initial velocity for a sprite. The velocity should be determined by how far the user moves the mouse between pressing and releasing the button.

You should implement a response to dragging only if the user is holding down the shift key when they press the mouse button. A MouseEvent object evt has several boolean-valued methods to tell you if the user is holding down certain keys when the event occurs:

Here, of course, you want to use evt.isShiftDown(). Use it in the mousePressed() method to decide whether to start a drag operation or to simply add a sprite immediately as you already programmed in the previous section of the lab.

Note that for a drag operation, you do not add the sprite in mousePressed(); instead, you do it in mouseReleased(). However, when you get to mouseReleased(), you need to know where the mouse was at the beginning of the drag, in mousePressed(). The only way to save the information from mousePressed() so that you can use it in mouseReleased() is to save it in instance variables. You will also need an instance variable to remember that a drag operation is happening, since not every mouse release is part of a drag operation.

Add the following instance variables to the MouseHandler class:

boolean dragging; // Set to true if a drag is in progress.
int startX;       // Set to the starting x-coordinate for a drag.
int startY;       // Set to the starting y-coordinate for a drag.

In mousePressed(), if the shift key is down, set dragging to be true, and record the location of the mouse in startX and startY. Then, in keyReleased(), test whether dragging is true. If it is, you need to add a sprite to the panel and set its velocity based on the starting position and the current position of the mouse. Also, don't forget to set the value of dragging back to false.

(For the velocity, you can try setting dx to the difference between the two x-coordinates and dy to the difference between the two y-coordinates. If that seems too fast to you, you can try dividing by some number to decrease the speed.)

(Note about mouseClicked(): We will not use this method. A mouseClicked event occurs when the user presses the mouse and then releases it within a short time period, without moving it. However, the same action also generates a mousePressed event and a mouseReleased. It is common to respond to the mousePressed event and ignore the mouseClicked event.)

Bonus: Click to Delete

For some extra credit, you can make it possible for the user to use the mouse to delete sprites. They should do this by pressing the mouse on the sprite with the right mouse button. As noted above, this is equivalent to holding down the Meta/Command key while pressing the mouse. To implement this, just add another possibility to the if statement in the mousePressed() method.

To implement deletion, you will need to go through all the sprites in the panel, and check whether each one contains the mouse location. You should delete the first one that you find that contains the mouse location (if any). You will need the contains(x,y) method afrom the Sprite class, the die() method from the Sprite class, the getSpriteCount() method from the SpritePanel (and hence SpriteDemo1 class), and the getSprite(i) method from the SpritePanel class. It's a good idea to check the sprites in reverse order: If two sprites are under the mouse, you want to delete the one that is on top, and that will be the one that come later in the list of sprites.