Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Event Handling in JDK 1.1, Component Impersonation

Java Programming, Lecture Notes # 106, Revised 02/21/98.

Preface

Students in Prof. Baldwin's Intermediate Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.

JDK 1.1 was released on February 18, 1997 and JDK 1.1.1 was released on March 27, 1997. This lesson was originally written on March 30, 1997 using the software and documentation in the JDK 1.1.1 download package.

Introduction

This lesson continues the discussion of posting events to the SystemEventQueue.

One of my teaching objectives is to present every concept in the simplest possible terms, devoid of confusing extraneous material. This lesson contains about the simplest example program that I was able to write that illustrates the posting of events.

In this lesson, a Label object impersonates a Button object by posting counterfeit ActionEvent objects attributable to the Button. The events are picked up by the system and processed just as though they were actually generated by the Button. In particular, they are delivered to an ActionListener object registered on the Button object where they are processed just as though they originated at the Button object.

This sample program operates completely within the Source/Listener concept of the Delegation Event Model.

Sample Program

This program was originally designed to be compiled and executed under JDK 1.1.1. It was later compiled and successfully tested under JDK 1.1.3. It demonstrates the use of the postEvent() method to post ActionEvents to the SystemEventQueue. In this program, a Label object impersonates a Button object by posting counterfeit ActionEvent objects and attributing them to the Button object.

Unlike a previous sample program, this program does not override any of the processXxxxEvent() methods. Rather, this program works completely within the Source/Listener concept of the Delegation Event Model.

Two Label objects and a Button object are instantiated and added to a Frame object. When the Button object is clicked, an ActionEvent is generated and trapped by an ActionListener object registered on the Button object. Code in the actionPerformed() method of the ActionListener object toggles the background color of one of the Label objects back and forth between yellow and blue.

Up to this point, everything is pretty normal. However, a MouseListener object is registered on the other Label. When that Label object is clicked, code in the mouseClicked() method of the MouseListener object generates a counterfeit ActionEvent object and posts it to the SystemEventQueue.

The code in the Label's MouseListener object impersonates the Button object by placing the identification of the Button object into the "source" field of the counterfeit ActionEvent object.

The runtime system delivers the counterfeit ActionEvent object to the ActionListener object registered on the Button object. The final result is that clicking on the Label object invokes the actionPerformed() method registered on the Button object, so clicking on the Label object has exactly the same result as clicking on the Button object.

The program was tested using JDK 1.1.1 and Win95. An interesting sidelight is that the counterfeit button constructed from the Label object is much more responsive than the real button. In other words, the counterfeit button can service mouse clicks at a much more rapid rate than the real button.

This probably has something to do with the requirement to redraw the button twice each time it is clicked in order to animate it. An interesting exercise for the student would be to respond to the pressed and released events of the mouse on the Label to change the background color of the Label while it is pressed to see if that would produce a similar degradation of the responsiveness of the counterfeit button.

Interesting Code Fragments

Much of the material in this program has been included and discussed in sample programs in earlier lessons. Those portions of the code won't be highlighted here.

The first interesting code fragment is the pair of statements in the constructor which register Listener objects on the Label and the Button. An important point to note is that a reference to the Button object is passed to the constructor of the MouseListener object for the Label. It is through this link that the Label is able to create a counterfeit ActionEvent object and attribute it to the Button.

It is also interesting to note that the Listener object for the Label knows nothing about the other Label object whose color is changed during the operation of the program. This is because the Label object that impersonates the Button does not directly change the color of the other Label object. Rather, by impersonating the Button object, it causes the ActionListener object on the Button to perform that task. In fact, it has no knowledge of the outcome of its impersonation. All it knows is that it posts an ActionEvent object and attributes it to the Button.
 
    clickMeLabel.addMouseListener(
            new MyMouseListener(clickMeButton));
    clickMeButton.addActionListener(
            new MyActionListener(colorMeLabel));
The next interesting code fragment is the statement inside the mouseClicked() method of the MouseListener object that creates and posts the counterfeit ActionEvent object and attributes it to the Button object. It is possible for this code to attribute the event to the Button object only because it has access to a reference to the Button object.

In this case, that reference is passed as a parameter when the MouseListener object is constructed but there are probably other ways to accomplish this as well. For example, code might be able to "peek" into the SystemEventQueue in hopes of copying an ActionEvent object and obtaining a reference to the Button. That is why special security provisions apply to the SystemEventQueue for Applets.

Because the counterfeit ActionEvent object is attributed to the Button object, it is delivered to the ActionListener object for the Button where it is processed just as though it actually originated at the Button.
 
    //Note that the following is a single statement
    Toolkit.getDefaultToolkit().
        getSystemEventQueue().
        postEvent(new ActionEvent(clickMeButton,
                                  ActionEvent.
                                  ACTION_PERFORMED,
                                  "counterfeit"));
One additional thing worthy of note is the syntax of an ActionEvent object for comparison with the above statement. There are two constructors for an ActionEvent object. The following definition matches the constructor used in the above statement to instantiate the counterfeit ActionEvent object. Note that the reference to the Button object was used for the first (source) parameter.
 
 public ActionEvent(Object source,
                    int id,
                    String command)
Constructs an ActionEvent object with the 

specified source object. 
Parameters: 
source - the object where the event 

originated  
id - the type of event 

command - the command string for this  
action event 
The other constructor has a fourth parameter that deals with the modifier keys being held down when the event occurred.

As mentioned in an earlier lesson, it is very important that you specify a correct id parameter. Otherwise, the ActionEvent object won't be delivered by the runtime system.

I promised you earlier that this was going to be a simple program. The remaining code is standard code that you have seen many times before so we won't highlight it as being interesting. A complete listing of the program is presented in the next section.

Program Listing

This section contains a complete listing of the program with additional comments. Refer to previous sections for an operational description of the program.
 
/*File Event31.java Copyright 1997, R.G.Baldwin
Reformatted 10/5/97 to fit better on the screen.

This program was designed to be compiled and executed under
JDK 1.1.1.

This program demonstrates the use of the postEvent() method
to post ActionEvents to the SystemEventQueue.  In this 
program, a Label object impersonates a Button object by 
posting counterfeit ActionEvent objects and attributes them
to the Button object.

Unlike a previous sample program, this program does not 
override any of the processXxxxEvent() methods. Rather, 
this program works completely within the Source/Listener 
concept of the Delegation Event Model.

Two Labels and a Button are instantiated and added to a 
Frame object. When the Button object is clicked, an 
ActionEvent is generated and trapped by an ActionListener 
object registered on the Button object. Code in the 
actionPerformed() method of the Listener object toggles
the background color of one of the Label objects back and 
forth between yellow and blue.

So far, everything is pretty standard.  However, a 
MouseListener object is registered on the other Label.  
When that Label object is clicked, code in the 
mouseClicked() method of the MouseListener object generates
a synthetic or counterfeit ActionEvent object and posts it 
to the SystemEventQueue.

The code in the Label's MouseListener object impersonates 
the Button object by placing the identification of the 
Button object into the "source" field of the counterfeit 
ActionEvent object.  

The runtime system delivers the counterfeit ActionEvent 
object to the ActionListener object registered on the 
Button object.  The final result is that clicking on the 
Label object invokes the actionPerformed() method 
registered on the Button object, so clicking on the Label 
object has exactly the same result as clicking on the 
Button object.

The program was tested using JDK 1.1.1 (and later 
JDK 1.1.3) and Win95.  An interesting sidelight is that the
counterfeit button constructed from the Label object is
more responsive than the real button.  In other words, the
counterfeit button can service mouse clicks at a more rapid
rate than the real button.
*/

import java.awt.*;
import java.awt.event.*;

//=========================================================
public class Event31 extends Frame{
  public static void main(String[] args){
    Event31 displayWindow = new Event31();//instantiate obj
  }//end main
  //-------------------------------------------------------

  public Event31(){//constructor
    setTitle("Copyright 1997, R.G.Baldwin");
    setLayout(new FlowLayout());
    Button clickMeButton = new Button("Click Me");
    Label colorMeLabel = new Label("Color Me");
    Label clickMeLabel = new Label("Click Me");  

    add(clickMeButton);//add components to the Frame object
    add(colorMeLabel);
    add(clickMeLabel);

    setSize(250,100);//set frame size    
    setVisible(true);//display the frame

    //Register listener objects
    clickMeLabel.addMouseListener(
                       new MyMouseListener(clickMeButton));
    clickMeButton.addActionListener(
                       new MyActionListener(colorMeLabel));
    //terminate when Frame is closed                       
    this.addWindowListener(new Terminate());
   }//end constructor
}//end class Event31
//=========================================================

/*This MouseListener class is used to monitor for mouse 
clicks on a Label object.  Whenever the user clicks on the 
label, the code in an object of this class creates a 
counterfeit ActionEvent object and posts it to the 
SystemEventQueue.  The source of the event is specified to 
be a particular Button object that is passed in when an 
object of this class is instantiated.  Thus, the Label 
object "claims" to be the Button object and posts 
ActionEvent objects that are interpreted by the runtime 
system as originating at the Button object.  The type of
ActionEvents generated are ACTION_PERFORMED events.  The 
events are automatically delivered to the actionPerformed()
method of an ActionListener object registered on the 
button. */

class MyMouseListener extends MouseAdapter{
  Button clickMeButton;//reference to the Button
  //-------------------------------------------------------
  MyMouseListener(Button inButton){//constructor
    clickMeButton = inButton;//save reference to Button
  }//end constructor
  //-------------------------------------------------------
  //overridden mouseClicked() method
  public void mouseClicked(MouseEvent e){
    //Note that the following is a single statement
    Toolkit.getDefaultToolkit().
        getSystemEventQueue().
        postEvent(new ActionEvent(clickMeButton,
                                  ActionEvent.
                                  ACTION_PERFORMED,
                                  "counterfeit"));
  }//end overridden mouseClicked() method
}//end MyMouseListener

//=========================================================
/*This ActionListener class is used to instantiate a 
Listener object for the Button object.  Whenever the button
is clicked, or a counterfeit ActionEvent is posted with the
Button as the specified source object, the code in an 
object of this class toggles the background color of a 
Label object back and forth between yellow and blue.*/

class MyActionListener implements ActionListener{
  int toggle = 0;
  Label myLabel;
  //-------------------------------------------------------
  MyActionListener(Label inLabel){//constructor
    myLabel = inLabel;
  }//end constructor  
  //-------------------------------------------------------
  public void actionPerformed(ActionEvent e){
    if(toggle == 0){
      toggle = 1;
      myLabel.setBackground(Color.yellow);
    }else{
      toggle = 0;
      myLabel.setBackground(Color.blue);
    }//end else
  }//end actionPerformed()
}//end class myActionListener
//=========================================================

class Terminate extends WindowAdapter{
  public void windowClosing(WindowEvent e){
    //terminate the program when the window is closed  
    System.exit(0);
  }//end windowClosing
}//end class Terminate
//=========================================================

Review

Q - Write a Java application that meets the specifications given below.

A - See the specifications and the solution below.
 
/*File SampProg133.java Copyright 1997, R.G.Baldwin
Without viewing the solution that follows, write a Java
application that replicates the behavior of the application
named Event31.java (discussed in lesson 106) with the 
following changes.

This is an upgrade to the original version of Event31.
In particular, in its original form, there is no visual 
indication when the user clicks on the Label object 
identified by the Click Me caption. Since this Label object
is being used to simulate a Button object, a visual 
indication that the Label object has been clicked would be
appropriate.

In your upgraded version, the white Label object which 
originally had the Click Me caption will appear as a 
magenta colored rectangle when the program starts and will
not display a caption.  

When the user clicks the magenta Label object, the caption 
"ouch" will momentarily appear and disappear in synchronism
with the down stroke and the up stroke of the left mouse 
button.  The caption will appear on the down stroke and 
will disappear on the upstroke.

In addition to the above, make your name appear in the
banner at the top of the Frame object.

The upgraded version of the program has been tested using
JDK 1.1.3 under Win95.

*/
//=========================================================
import java.awt.*;
import java.awt.event.*;

//=========================================================
public class SampProg133 extends Frame{
  public static void main(String[] args){
    //instantiate obj
    SampProg133 displayWindow = new SampProg133();
  }//end main
  //-------------------------------------------------------

  public SampProg133(){//constructor
    setTitle("Copyright 1997, R.G.Baldwin");
    setLayout(new FlowLayout());
    Button clickMeButton = new Button("Click Me");
    Label colorMeLabel = new Label("Color Me");
    Label clickMeLabel = new Label("      ");
    clickMeLabel.setBackground(Color.magenta);  

    add(clickMeButton);//add components to the Frame object
    add(colorMeLabel);
    add(clickMeLabel);

    setSize(300,100);//set frame size    
    setVisible(true);//display the frame

    //Register listener objects
    clickMeLabel.addMouseListener(
          new MyMouseListener(clickMeButton,clickMeLabel));
    clickMeButton.addActionListener(
                       new MyActionListener(colorMeLabel));
    //terminate when Frame is closed                       
    this.addWindowListener(new Terminate());
   }//end constructor
}//end class SampProg133
//=========================================================

/*This MouseListener class is used to monitor for mouse 
clicks on a Label object.  Whenever the user clicks on the 
label, the code in an object of this class creates a 
counterfeit ActionEvent object and posts it to the 
SystemEventQueue.  The source of the event is specified to 
be a particular Button object that is passed in when an 
object of this class is instantiated.  Thus, the Label 
object "claims" to be the Button object and posts 
ActionEvent objects that are interpreted by the runtime 
system as originating at the Button object.  The type of
ActionEvents generated are ACTION_PERFORMED events.  The 
events are automatically delivered to the actionPerformed()
method of an ActionListener object registered on the 
button. */

class MyMouseListener extends MouseAdapter{
  Button clickMeButton;//reference to the Button
  Label clickMeLabel;//reference to the Label
  //-------------------------------------------------------
  //constructor
  MyMouseListener(Button inButton,Label inLabel){
    clickMeButton = inButton;//save reference to Button
    clickMeLabel = inLabel;//save reference to Label
  }//end constructor
  //-------------------------------------------------------
  //overridden mouseClicked() method
  public void mouseClicked(MouseEvent e){
    //Note that the following is a single statement
    Toolkit.getDefaultToolkit().
        getSystemEventQueue().
        postEvent(new ActionEvent(clickMeButton,
                                  ActionEvent.
                                  ACTION_PERFORMED,
                                  "counterfeit"));
  }//end overridden mouseClicked() method
  //------------------------------------------------------
  //overridden mousePressed() method
  public void mousePressed(MouseEvent e){
    clickMeLabel.setText(" ouch ");
  }//end overridden mousePressed() method
  //------------------------------------------------------
  //overridden mouseReleased() method
  public void mouseReleased(MouseEvent e){
    clickMeLabel.setText("      ");
  }//end overridden mousePressed() method
}//end MyMouseListener

//=========================================================
/*This ActionListener class is used to instantiate a 
Listener object for the Button object.  Whenever the button
is clicked, or a counterfeit ActionEvent is posted with the
Button as the specified source object, the code in an 
object of this class toggles the background color of a 
Label object back and forth between yellow and blue.*/

class MyActionListener implements ActionListener{
  int toggle = 0;
  Label myLabel;
  //-------------------------------------------------------
  MyActionListener(Label inLabel){//constructor
    myLabel = inLabel;
  }//end constructor  
  //-------------------------------------------------------
  public void actionPerformed(ActionEvent e){
    if(toggle == 0){
      toggle = 1;
      myLabel.setBackground(Color.yellow);
    }else{
      toggle = 0;
      myLabel.setBackground(Color.blue);
    }//end else
  }//end actionPerformed()
}//end class myActionListener
//=========================================================

class Terminate extends WindowAdapter{
  public void windowClosing(WindowEvent e){
    //terminate the program when the window is closed  
    System.exit(0);
  }//end windowClosing
}//end class Terminate
//=========================================================
-end-