Events

The button we created on the last page needs something to do. To make it print something when we click on it, execute this:
  java::bind $button \
              java.awt.event.ActionListener.actionPerformed \
             "toggleButton $button"

  proc toggleButton {b} {
    if { [java::prop $b label] == "Push me!" } {
      java::prop $b label "Again!"
    } else {
      java::prop $b label "Push me!"
    }
  }
What we have just done is to bind a Tcl script that calls the toggleButton Tcl procedure, to the button. When the button is pressed, Tcl Blen will evaluate this script, call toggleButton, and toggle the button label. (An aside: this is very poor user interface design -- button labels should not change dynamically -- but it's a nice simple example.) How did this work?

In Java, events are themselves objects, which inherit from the java.util.EventObject class. When the button is pressed, it creates an object of class java.awt.ActionEvent. Corresponding to each event class is an interface for a listener to that event -- in this case, the listener interface is java.awt.event.ActionListener. Any object that is interested in receiving that kind of event implements the appropriate listener interface, and adds itself to the button with the button's addActionListener method. Any object that implements addActionListener and the corresponding removeActionListener method is known to be capable of generating ActionEvent objects.

You can see the listeners supported with java::info. If you execute

  java::info events $button
you will see that the returned list includes java.awt.event.ActionListener, and several other listener types too. Now, if you query the methods of ActionListener,
  java::info methods java.awt.event.ActionListener
you will see that this listener has a single method, actionPerformed! That is the method we specified in the call to java::bind at the top of this page. In actual fact, we can abbreviate the call provided that the method name is unique -- in this case we could have used:
  java::bind $button actionPerformed "toggleButton $button"

In summary, when we called java::bind, Tcl Blend:

  1. Created, on-the-fly, an object that implements the ActionListener interface, and
  2. Added that listener to the button using addActionListener.

To illustrate how this would work purely in Java, here is a Java class that inherits from java.awt.Button:

package tutorial.tcltk98;
import java.awt.*;
import java.awt.event.*;

public class EagerButton extends java.awt.Button {
    public EagerButton () {
        super("Push me!");       
        addActionListener(new LocalListener());
    }
    class LocalListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (getLabel().equals("Push me!")) {
                setLabel("Again!");
            } else {
                setLabel("Push me!");
            }
        }
    }
}
This object, when it is created, creates an object of class LocalListener that it attaches as an action listener to itself. When the button is pressed, the action listener's actionPerformed method is called. As a result, this button will exhibit exactly the same behavior as we set up above using java::bind. To try it, compile it and then execute:
  set button [java::new tutorial.tcltk98.EagerButton]
  set window [java::new java.awt.Frame]
  $window setLocation 100 100
  $window {add java.awt.Component} $button
  $window pack
  $window show
Can I get an event when I change a property?
Yes. Well, maybe. If a class supports the PropertyChangeListener inetrface, then when a so-called bound property is changed, any listeners attached to the object will be notified with a PropertyChangeEvent object. None of the objects we have dealt with so far have bound events, but the JTree object we use in the detailed example does.

Finally, for this section, here's a slightly more sophisticated set of event bindings. The underlying Java class, tutorial.tcltk98.ScribbleCanvas, extends the regular AWT canvas to remember a series of line segments. The Tcl code creates the canvas in a window, and adds bindings so that drawing with the mouse draw lines on it:

# Create a top-level frame containing a canvas
set window [java::new java.awt.Frame "Scribble!"]
$window setSize 600 400
set canvas [java::new tutorial.tcltk98.ScribbleCanvas]
$window {add java.awt.Component} $canvas
$window show
$window pack

# Create the Tcl callbacks for handling events
proc mousePressed {canvas} {
    global prevX prevY
    set prevX [java::event x]
    set prevY [java::event y]
    $canvas startLine $prevX $prevY
}
proc mouseDragged {canvas} {
    global prevX prevY
    set currX [java::event x]
    set currY [java::event y]
    $canvas extendLine $currX $currY
    set prevX $currX
    set prevY $currY
}
proc mouseReleased {canvas} {
    $canvas stopLine
}

# Bind the callbacks to the canvas
java::bind $canvas mousePressed "mousePressed $canvas"
java::bind $canvas mouseDragged "mouseDragged $canvas"
java::bind $canvas mouseReleased "mouseReleased $canvas"
Yawn!
Yeah, OK, we know how you feel. The AWT in JDK 1.1 is fairly primitive, and we don't like it much either. JDK 1.2, though, is a completely different ball-game. Firstly, Swing becomes part of the core JDK (see next section), and the Java 2D API provides a very sophisticated set of 2D graphics primitives that far exceeds the capabilities of the Tk canvas. We're currently developing Java 2D code, but we elected not to use it in this tutorial because of a) some compatibility problems with Tcl Blend and JDK 1.2, and b) because of the extra hassle of using an unstable version of the JDK (1.2 is still in beta).