Event Handling in JBuilder Part II, by Charlie Calvert

By: Charlie Calvert

Abstract: This is the second part of a three part article on Event Handling in JBuilder. The text is an excerpt from my book, Charlie Calvert's Learn JBuilder, from WordWare publishing.

Part II

Copyright © 2003 by Charlie Calvert

This is the second part of a three part article .

Go to Part I
Go to Part III

Copyright © 2003 by Charlie Calvert

This is the second part of a three part article .

The ActionEvent Class

As you know, ActionPerformed methods get passed a parameter of type ActionEvent:

void JButton1_actionPerformed(ActionEvent e)

The ActionEvent class contains a good bit of information about the state of the mouse and keyboard when this event was triggered. You will get a chance to explore many of the more important features of this class in the next chapter, called Firing Events. In that chapter, you will learn how to create instances of this class, and how to use many of its methods. For now, however, there are still a few basic things you ought to know about the class.

Consider the methods of the ActionEvent class shown in Table1.

e.getActionCommand();

Retrieve a string associated with the command.

e.getModifiers();

Retrieve the state of the Alt and Ctrl keys

e.getID();

Retreive an integer associated with the command

e.getWhen();

Find out when the command was issued.

e.getSource();

Which control caused the event? In our case, JButton1.

NOTE: The e.getWhen() method is a new feature in JDK 1.4. That means that unless you have JBuilder 8, or unless you switch the JDK to 1.4.0, you will not be able to use this portion of the code. You need to use the IDE tools to switch your JDK. If you are using JBuilder Personal, JDK switching cannot be accomplished on a per project basis, instead you must change the JDK for the entire IDE. For more information, see the source and notes for this book.

A program called ActionEventMadness, found with the source code that accompanies this book, demonstrates many of the important methods supported by the ActionEvent class. The key parts of that program are examined over the course of the next few paragraphs.

Consider the following button response method:

void JButton1_actionPerformed(ActionEvent e)
{
	System.out.println("ParamString: " + e.paramString());
	System.out.println("Action Command:" + e.getActionCommand());
	int modifiers = e.getModifiers();
	System.out.println("Modifiers: " + KeyEvent.getKeyModifiersText(modifiers));
	System.out.println("ID: " + e.getID());
	long a = e.getWhen();
	Date d = new Date(a);
	System.out.println("When: " + d);
	System.out.println("Source: " + e.getSource().getClass().getName());
}

This method outputs the following information:

ParamString: ACTION_PERFORMED,cmd=JButton1,when=1019943559903,modifiers=Alt+Shift+Button1
Action Command:JButton1
Modifiers: Alt+Shift+Button1
ID: 1001                    
When: Sat Apr 27 14:39:19 PDT 2002
Souce: javax.swing.JButton

The paramString() method of the ActionEvent class gives you some general information about the state of the button used to trigger the event. The Action Command is a string associated with the button. You can change this string in the inspector at design time.

The modifiers define which special keys were selected at the time the button was pressed. Here I used the KeyEvent.getKeyModifiersText method to find out the state of the buttons. Here is another method that performs a similar task:

void JButtonBitMasks_actionPerformed(ActionEvent e)
{
	String stateStr = "";
	int keyState = e.getModifiers();
	if ((keyState & InputEvent.ALT_MASK) != 0) 
	{
		stateStr += "Alt is downn";
	}

	if ((keyState & InputEvent.SHIFT_MASK) != 0) 
	{
		stateStr += "Shift is downn";
	}

	if ((keyState & InputEvent.CTRL_MASK) != 0) 
	{
		stateStr += "Ctrl is downn";
	}

	if (stateStr.length() == 0) 
	{
		stateStr = "You did not hold down Alt, Shift, or Ctrl";
	}
	jTextArea1.setText(stateStr);
}

This code uses the logical & operator to perform bitwise operations revealing the state of the pressed keys. For instance, this code will reveal whether or not the Alt key was selected at the time the button that triggered the event was pressed:

int keyState = e.getModifiers();
if ((keyState & InputEvent.ALT_MASK) != 0)

The id returned by a call to ActionEvent.getID() represents a unique number associated with a particular event.

A call to ActionEvent.getWhen() reveals the time when the event occurred. Here is code that relies on the default invocation of the Date.toString() method for translating the returned long integer into a string:

long a = e.getWhen();
Date d = new Date(a);
System.out.println("When: " + d);

If you new to Java, you might think the code shown here is a typo. That is not the case. By default, d.toString() is called. The result of that call might look something like this:

When: Sat Apr 27 14:39:19 PDT 2002

Finally, the getSource method returns an instance of the control that triggered the call to this event handler. Call getSource.getClass.getName to get the type of class that caused the event. Here is code to retrieve the caption of the button that was pressed:

jTextArea1.append("nButton caption: " + ((JButton)(e.getSource())).getText());

Using other Standard Events

It should be obvious that JBuilder supports more than the actionPerformed event. When you select a particular component, and turn to the Events page in the inspector, you are likely to see a range of event handlers that JBuilder will create for you. For instance, JButton supports the ancestorAdded, ancestorRemoved, itemStateChanged, stateChanged, etc, events. Exploring all of these event handlers is a major undertaking, and is really the task of a book on the JDK, not a book on JBuilder. So I will leave it up to you to explore these various event handlers, and to see what you can do with them.

Customizing the JBuilder Event Handling Mechanism

You have seen the technique that JBuilder uses to handle events. In most cases, I suggest just going with the flow, and allowing JBuilder to create your event handlers for you, using the techniques just described. However, I want to take a moment to point out two things:

  1. There are other ways to handle events that those shown so far in this chapter.

  2. JBuilder may not help you create these alternative kinds of event handlers, but it will not prevent you from using them if you want.

The StringTest01 program, found in the ComplexProject subdirectory found in this source for this book, shows an alternative way to handle events. The event code in that program was partially created by JBuilder, and partially created by hand.

At the very bottom of Frame1.java, you will find a class that looks like this:

/** I am using the same ActionListener for all of my menuitems in this
* application. Furthermore, I am sending most of them to the same
* event handler back in the main program.
*/
class MainListener implements ActionListener
{
	Frame1 adaptee;

	MainListener(Frame1 adaptee)
	{
		this.adaptee = adaptee;
	}

 
	public void actionPerformed(ActionEvent e)
	{
		if (e.getSource() == adaptee.jMenuFileExit)
		{
			adaptee.jMenuFileExit_actionPerformed(e);
		}
		else if (e.getSource() == adaptee.jMenuHelpAbout)
		{
			adaptee.jMenuHelpAbout_actionPerformed(e);
		}
		else if (e.getSource() == adaptee.jMenuItemReverse)
		{
			adaptee.jMenuItemReverse_actionPerformed(e);
		}
		else if (e.getSource() == adaptee.jMenuItemGetFirstWord)
		{
			adaptee.jMenuItemReverse_actionPerformed(e);
		}
		else if (e.getSource() == adaptee.jMenuItemStripFirstWord)
		{
			adaptee.jMenuItemReverse_actionPerformed(e);
		}
	}
}

This class is very like the Frame2_JButton1_actionAdapter class you saw earlier in this chapter:

class Frame2_JButton1_actionAdapter implements java.awt.event.ActionListener

It is of the same type, and it is declared in the same location. The only difference is that it has a shorter name and calls not one event handler back in class Frame1, but a whole series of them.

If you have been using Java for a while, I'm sure you have seen code similar to this. It uses a method of the ActionEvent object passed to the handler to sort out which control in Frame1 actually called the class. The method it uses is called getSource, and it returns the instance of the class that called MainListener.

After the class decides which object called it, then it uses that information to call the appropriate method back in Frame1:

else if (e.getSource() == adaptee.jMenuItemStripFirstWord)
{
	adaptee.jMenuItemReverse_actionPerformed(e);
}

There were several different ways to associate the MainListener class with the various event-generating components in Frame1. The technique this program uses looks like this:

MainListener listen = new MainListener(this);
jMenuFileExit.addActionListener(listen);
jMenuItemReverse.addActionListener(listen);
jMenuItemGetFirstWord.addActionListener(listen);
etc

There is no way to make JBuilder generate code of this type automatically. You have to write it yourself. I simply created one event-handler using the JBuilder tools, and then wrote the rest of the code by hand.

The technique shown here clearly uses fewer resources than the technique that JBuilder uses. In particular, it uses only one adapter class, while JBuilder code for handling this kind of situation would generate several adaptor classes. On the other hand, a pure OOP based language like Java is optimized to have very little overhead when creating classes. The overhead created by the adaptor classes is small enough to have no significant impact on the vast majority of programs. So wasting a lot of time doing things as I do here is usually not worth the candle. However, the option is available.

In the long run, perhaps the most important point is to see that JBuilder will allow you to customize your code to whatever extent you want. Do things the JBuilder way if you want, or go off in another direction if you prefer. JBuilder gives you the freedom to do what you want.

Listener Interfaces and Adapter Classes

Very few classes get raw mouse events automatically. In those few classes, if you turn to the Events page of the JBuilder inspector, you will be able to create event handlers for mouse events just as you create event handlers for the actionEvent that occurs when the user clicks on a button.

NOTE: A JButton ActionPerformed event is clearly caused by a button click, but it is not a button event in the strict sense of the word. It tells you that the button was selected, and if you explore the ActionEvent parameter passed to this method you can find out about the state of the mouse and keyboard when the button was selected. But still, the purpose of the event is not to tell you about the mouse itself, but only that the user wanted to access the functionality encapsulated in your response handler. In other words, its primary purpose is to tell you that the button was selected.

But sometimes you will see that a class does not receive events that you might want it to receive. For instance, you might want to allow a JFrame descendant to receive mouse events. This section of the chapter shows how to add mouse events to a class that does not already provide support for mouse events in the events page of the inspector.

So far in this chapter you have had a chance to look at both firing and receiving events. The events we have been looking at are fairly straightforward. Some events, like those associated with the MouseListener interface, are a bit more complex:

public interface MouseListener extends EventListener
{
	public void mouseClicked(MouseEvent e);
	public void mousePressed(MouseEvent e);
	public void mouseReleased(MouseEvent e);
	public void mouseEntered(MouseEvent e);
	public void mouseExited(MouseEvent e);
}

By this point in the chapter, it should be relatively clear what is going on in this interface. We have a single event called a MouseEvent that gets sent to you on five different occasions: when the mouse is clicked, pressed or released, and also when the mouse enters or leaves the current window. You will get an even deeper understanding of how all this occurs when you read the next chapter, and start creating your own events and your own listener interfaces.

NOTE: A clicked event occurs when the mouse has been pressed and released in one motion, while the pressed event occurs when the mouse has been pressed but not necessarily released.

KeyListener, MouseListener, MouseWheelListener and MouseMotionListener

I'm going to show you a program found on the CD called MouseKey that demonstrates how to set up a JFrame descendant that supports the KeyListener, MouseListener, MouseWheelListener, and MouseMotionListener interfaces. The program is shown in Figure 3.

Figure 3: The MouseKey program supports the MDI interface. It demonstrates techniques for accepting mouse and keyboard input.

NOTE: The MouseWheelListener interface described in this chapter is part of JDK 1.4. If you need to switch your JDK, you will find instructions for that process in Chapter 1 and in the index.html file that comes with the source. If you are using JBuilder personal, then you may JDK switching for the entire IDE if you want to use the MouseWheelListener interface inside of JBuilder. Other versions of the product will allow you to switch JDKs on a per project basis.

The MouseKey program uses an MDI interface. Near the end of the chapter, I will explain exactly how to create a program that uses this kind of interface. For now, you need only know that there is a main window in the program, as shown in Figure 3. Residing on top of this main window are three JPanel descendants. The lower of the three panels is the InputPanel. You can type on this panel. The top two panels will be used to display the input that occurs in the InputPanel. In particular, when the user clicks in the InputPanel, then a report of that mouse event will be shown in the MousePanel. If the user types on the InputPanel, then a report of the user's keypresses will be shown in the KeyPanel.

Understanding the MouseKey Program

You may want to follow along here and try to create your own copy of the MouseKey program. I will not give you detailed instructions on how to proceed, but I will try to give you enough information so you should not find it hard to fill in the blanks by yourself. The glue that holds the program together is MDI, or a virtual desktop. I will explain how that glue works at the very end of this chapter.

If building the program feels a bit too daunting, then simply open the copy found with the source that accompanies this book. Run the program a few times, and examine the code included in it so that you can see how the text in the book relates to a real world program.

To create the program yourself, first create a new project and new application, as described in Chapter 3, "Default Applications and Applets. Choose File | New Class from the JBuilder menu and create a JPanel descendant called InputPanel.java. Figure 4 shows how to pick the base class and Figure 5 shows the correctly filled out class wizard. Click Ok to close the wizard and then focus this class in the editor.

Figure 4: Browsing to select the class from which the InputPanel will descend.

Figure 5: Filling out the class wizard to put a new JPanel descendant called InputPanel in the untitled34 package.

If you are using the SE or enterprise versions of JBuilder, choose Wizards | Implement Interface. Browse to java.awt.event, and select the MouseMotionListener, MouseListener, KeyListener, and (if you have JDK 1.4) MouseWheelListener interfaces, as shown in Figure 6.

Figure 6: Selecting interfaces in the interface wizard.

The following code will be added near the top of your InputPanel.java file:

public class InputPanel extends JFrame
	implements MouseListener, MouseMotionListener, MouseWheelListener, KeyListener

If you don't have the Interface Wizard in your version of the product, then you will have to type this code in manually. (Recall that MouseWheelListener is a JDK 1.4 only class.)

In the implementation of your class, code similar to that shown in Listing 1 will be added by the interface wizard. If you are using the Personal edition of JBuilder, you will have to type in this code manually. The code is a little lengthier than this, in that it includes more methods and provides the name of the method that failed if an exception is raised. I've pared it down just a bit here so that it will be easier to read.

Listing 1: The default implementation of the MouseListener, MouseMotionListener and MouseWheelListener that is provided by the Interface Wizard.

public void mouseClicked(MouseEvent e)
{
	/**@todo Implement this java.awt.event.MouseListener method*/
	throw new java.lang.UnsupportedOperationException("not yet implemented.");
}

public void mousePressed(MouseEvent e)
{                                     
	/**@todo Implement this java.awt.event.MouseListener method*/
	throw new java.lang.UnsupportedOperationException("not yet implemented.");
}

public void mouseReleased(MouseEvent e)
{                                      
	/**@todo Implement this java.awt.event.MouseListener method*/
	throw new java.lang.UnsupportedOperationException("not yet implemented.");
}

public void mouseEntered(MouseEvent e)
{                                     
	/**@todo Implement this java.awt.event.MouseListener method*/
	throw new java.lang.UnsupportedOperationException("not yet implemented.");
}

public void mouseExited(MouseEvent e)
{                                    
	/**@todo Implement this java.awt.event.MouseListener method*/
	throw new java.lang.UnsupportedOperationException("not yet implemented.");
}

public void mouseDragged(MouseEvent e)
{                                     
	/**@todo Implement this java.awt.event.MouseMotionListener method*/
	throw new java.lang.UnsupportedOperationException("not yet implemented.");
}

public void mouseMoved(MouseEvent e)
{                                   
	/**@todo Implement this java.awt.event.MouseMotionListener method*/
	throw new java.lang.UnsupportedOperationException("not yet implemented.");
}

public void mouseWheelMoved(MouseWheelEvent e)
{                                             
	/**@todo Implement this java.awt.event.MouseWheelListener method*/
	throw new java.lang.UnsupportedOperationException("not yet implemented.");
}

All of the @todo items will show up at the top of the structure pane under the node called todo, as shown in Figure 7.

Figure 7: The todo items and import statements generated by the Interface wizard all show up in the structure pane.


Server Response from: ETNASC04