The Java AWT: Overview

Jan Newmarch
Web: http://jan.newmarch.name
Email: jan@newmarch.name


Introduction

Java is an Object Oriented language based on C. Many years in gestation, it has shown an explosive growth in popularity since its release early last year. This is partly because of the relative simplicity of the language when compared to C and particularly languages such as C++, but also because the design and implementation of Java allow compiled programs to be shipped across the Internet to run on client systems.

This network capability was shown in the HotJava World Wide Web browser, which would accept an extended form of HTML containing compiled Java programs (applets) and run those programs locally. This has since been adopted by other browsers such as Netscape. HotJava was, of course, entirely written in Java using the Java libraries.

Since then Java has been licensed to Borland and Microsoft among others, and the growth looks set to continue.

Why Java is of particular interest to readers of the X Advisor is in the AWT (Abstract Window Types) library that is used for the GUI components of HotJava and that can also be used for the GUI components of applets. The library is complete enough to both build fully functional standalone GUI applications, to perform drawings and show images and animations.

There are many tutorials on the Java language, and some books. The reader is referred to these for information on Java itself. Several of these also deal with applets, which use many (but not all) of the AWT toolkit.

This overview concentrates on using the AWT toolkit to build standalone applications, and relates some of the implementation aspects back to X. Later articles will deal in more detail with particular features of the AWT toolkit, such as the Event class, geometry management, dialogs and extending the toolkit with new widgets.

GUI Hierarchy

The GUI hierarchy subclasses directly from Object. The classes of interest are

Subclasses of Component are the basic building blocks for GUI applications - what in Motif would be called the Primitive widgets and other widgets such as Dialogs and Managers.

Pulldown menus are handled by a different hierarchy, subclasses of MenuComponent.

The Event class defines the various types of event that can occur. Since this is a library designed to work across multiple platforms, these are not the familiar X Event types.

Geometry management is not built into Manager widgets like it is in Motif/Xt. Some Component classes allow objects to be placed within them. Each of these ``Container'' classes has an associated Layout object. The various layout management schemes subclass from Layout, and implement geometry management. In fact, the Manager classes of AWT are all implemented for X using a Motif DrawingArea, and the Layout objects place visible components within this.

Primitive Components

Unlike Motif, there is no special class for Primitive widgets - they all subclass from Component (except for Menu items). It is still convenient to use this terminology though. These classes include Button, Checkbox, Label, List, ScrollBar, TextArea (multi-line text) and TextField (single-line text).

There are a few Motif widgets missing from this: ArrowButton, DrawnButton, Scale and Separator, as well as the newer widgets from Motif 2.0 such as SpinBox.

In addition, the objects often have restrictions compared to the Motif widgets. For example, Label will only show a single-line label, discarding everything after a newline character. There is also no means of setting the direction of text display. None of the fancier effects that are possible using Motif 2.0 RenderTables (such as multiple fonts and colours) are possible. It is not possible to display a Bitmap/Pixmap instead of text. While the AWT Labelis implemented using a Motif Label such aspects of the Motif widget are either not accessible or are deliberately turned off.

Nevertheless the set is large enough to be useful. The following figure shows an AddressBook containing a List, three Label's, two TextField's and a TextArea. Notice that the TextArea shows both its scrollbars, even though they are not needed here. This behaviour is hard-coded into the Motif implementation.

Container Components

In Motif, the Manager widgets are used to enforce different geometry policies. In the AWT toolkit geometry management is done by separate Layout classes (this is sufficiently complex to require separate articles). The AWT Container classes cover a wider range of behaviour: Dialog, FileDialog, Frame, Panel and Window.

Many Motif widgets such as SelectionBox, and conveniences such as WarningDialog are missing from AWT. You have to build them yourself.

Every standalone application requires at least one Frame. This is implemented for X by an ApplicationShell plus a Motif MainWindow containing a DrawingArea. The MainWindow allows a Pulldown menu to be attached, while other objects can be placed in the DrawingArea.

For internal organisation of objects into groups, a Panel is an AWT general container.

A FileDialog gives the familiar Motif FileDialog (but modal only), while the Dialog class is used to build other dialogs.

Hello World

Hello World using a Label in a Frame is
import java.awt.*;

public class Hello extends Frame {
  public static void main(String argv[])
  {
    new Hello();
  }

  Hello() {
    Label hello = new Label("Hello World");
    // bring it under geometry control
    add("Center", hello);
    resize(200, 200);
    // map the widgets
    show();
  }
}
The static method main() creates a new object of class Hello, that extends Frame. A new Label is created and then added to the geometry control of the Hello object. The Layout object responsible for geometry control is not visible here: the Hello object has a default Layout object of class BorderLayout that is doing the management. The "Center" argument tells the BorderLayout object to use all remaining space for the Label.

For those interested in some implementation aspects: when the AWT Label is created using the new() method, the Motif Label is not yet created - it can't be, because the parent widget is not known. However, when it is added to the Hello object the parent is then known and the Motif Label widget can be created. All widgets are created Managed but Unmapped. The show() method maps all the widgets.

Event Handling

When an event of interest to the AWT toolkit occurs, the toolkit calls postEvent() for the object it occurred in. For subclasses of Component, this calls the handleEvent() method in the object. If this returns false, it calls handleEvent() in the parent object, etc, till either the method returns true or the top of the tree is reached.

Note that these events are not X events, but AWT events. They are triggered by certain actions occurring in widgets, but not by all actions. For example, when a Motif PushButton is activated, an AWT event with field id set to ACTION_EVENT is sent to the AWT Button. However, when the Motif PushButton is merely armed, no AWT event is generated.

This often causes problems due to expectations from native GUI implementations: for example, the TextArea class is implemented using a Motif Text widget. In Motif, callbacks can be added to XmNmodifyVerifyCallback and XmNmotionVerifyCallback to track key and pointer events within the widget. However, the AWT toolkit does not use these callbacks, so it is impossible to track such actions within TextArea.

Most AWT classes inherit the handleEvent() method from Component (note: this does not include classes derived from MenuComponent). This does a switch on event id and calls another method:

public boolean handleEvent(Event evt) {
   switch (evt.id) {
      case Event.MOUSE_ENTER:
         return mouseEnter(evt, evt.x, 
			evt.y);
      case Event.MOUSE_EXIT:
         return mouseExit(evt, evt.x, 
		       evt.y);
      ...
      default:
	 return false;
   }
}
public boolean mouseEnter(Event evt, 
			 int x, int y) {
   return false;
}
Not all events call methods: some fall through to the default case which returns false. The methods called for AWT objects all return false. They can be overridden in a user-defined subclass.

For a concrete example of this, consider Button. When the Motif PushButton is activated, an AWT event with id set to ACTION_EVENT is generated. handleEvent() calls the method public boolean action(Event evt, Object what) The second argument here is the value of the button's label as a String. To examine it, it will need to be coerced to this class.

If you use a Button and wish to attach application code to the callback, you do so by overriding action() in a subclass

class MyButton extends Button {
    public boolean action(Event evt, Object what) {
	// handle button ...
	System.out.println("I've been pushed");
        return true;
    }
}
(Note that you need to return true to prevent the event from being passed to its parent.)

To give a realistic example of this, consider the problem of password entry. The password should be entered into a single-line TextField. This class actually has a special method to set the echo character to something other than the input character (general access to more dynamic modification might have been more useful, but never mind). When the newline character is pressed, the method action() is invoked. Override this in a subclass of TextField to get useful behaviour:

import java.awt.*;

public class Password extends Frame {

    public static void main(String argv[])
    {
	new Password();
    }

    Password() {
	add("Center", new PasswordText());
	resize(100, 20);
	show();
    }
}

class PasswordText extends TextField {

    PasswordText() {
	setEchoCharacter('*');
    }

    public boolean action(Event evt, Object what) {
	System.out.println(getText());
	return true;
    }
}

Menus

All menu components are subclassed from MenuComponent (MenuBar, Menu, MenuItem, CheckboxMenuItem). Menus are pulldown menus only. You can have pulldown menus only in a standalone application, not an applet, because they need to be attached to a Frame (unless the applet creates its own Frame). Menus can be changed by the setMenu() of Frame, which unmaps the old menu and maps the new one.

A menu consists of a MenuBar which holds a set of Menu objects. Each element of a pulldown pane is a MenuItem or CheckboxMenuItem (or subclasses), and it is added to the appropriate Menu object by the add() method. Here is a simple menubar with one pulldown pane, which would be, say, a private method of a subclass of Frame:

private void CreateMenu()
{
   MenuBar mb = new MenuBar();
   Menu fileB = new Menu("File");
   mb.add(fileB);

   MenuItem newB = new MenuItem("New");
   MenuItem quitB = new MenuItem("Quit");
   fileB.add(newB); 
   fileB.add(quitB);
   
   setMenuBar(mb);
}
This creates a menubar with one item File. When this is selected a menupane with items New and Quit is displayed.

Unfortunately, event handling within menus is not ideal. It has been designed to be different to Component, which I find to be a nuisance. When a MenuItem is selected, an event with id set to ACTION_EVENT is generated and postEvent is executed for that object. However, this does not call handleEvent like Component objects do - there is no method handleEvent for MenuItems. Instead it calls the postEvent method for the parent in the GUI tree. So it walks up the GUI tree till it gets to the ancestor Frame object, and will execute handleEvent for the Frame.

Because there is no handleEvent method for MenuComponent objects, there are none of the convenience methods to handle events such as action or mouseDown.

I find this a poor approach because it requires that the Frame object knows all sorts of detail information about the structure of its menu. This will make the application difficult to change as it evolves.

It is quite easy to make menus behave in the same way as Components, but means tampering with a method that the designers of the AWT toolkit would probably prefer you not to: the postEvent method. This is really part of the implementation side that normally should not be overridden. However, that seems to be the simplest thing to do to get consistency in event handling.

We have no interest in actions for MenuBar and Menu - only in MenuItem. Define a new subclass of MenuItem with two methods postEvent and action. This shortcuts the Component mechanism that also uses the handleEvent method.

class MenuButton extends MenuItem {
    public void postEvent(Event evt) {
	if (evt.id == Event.ACTION_EVENT)
	    if (action(evt, evt.arg))
		return;
	}
	super.postEvent(evt);
    }

    public boolean action(Event evt, Object what) {
        return false;
    }
}

In future, subclass all menu buttons from MenuButton, with application logic in action()

class QuitButton extends MenuButton {
   public boolean action(Event evt, Object what) {
      System.out.println("Exiting...");
      System.exit(0);
      return true;
   }
}

As an example of this, let us look at a Label in a Frame, where we can change the foreground colour of the Label by selection from a Menu. The complete code for this is

import java.awt.*;

public class ColorMenu extends Frame {
    private Label label;

    public static void main(String argv[]) {
	ColorMenu cm = new ColorMenu();
	cm.show();
    }

    ColorMenu() {
	label = new Label("Hello");
	add("Center", label);
	CreateMenu();
	resize(100, 100);
    }

    private void CreateMenu()
    {
	MenuBar mb = new MenuBar();
	Menu fileB = new Menu("Color");
	mb.add(fileB);

	ColorMenuButton blueB = new ColorMenuButton("Blue", this);
	ColorMenuButton redB = new ColorMenuButton("Red", this);
	fileB.add(blueB); 
	fileB.add(redB);

	setMenuBar(mb);
    }

    public void changeColor(Color col) {
	label.setForeground(col);
    }
}


class MenuButton extends MenuItem {
    MenuButton(String name) {
	super(name);
    }

    public void postEvent(Event evt) {
	if (evt.id == Event.ACTION_EVENT)
	    if (action(evt, evt.arg))
		return;
	}
	super.postEvent(evt);
    }

    public boolean action(Event evt, Object what) {
        return false;
    }
}

class ColorMenuButton extends MenuButton {
    ColorMenu toplevel;

    ColorMenuButton(String name, ColorMenu top) {
	super(name);
	toplevel = top;
    }

    public boolean action(Event evt, Object what) {
	String name = (String) what;

	if (name.equals("Red"))
	    toplevel.changeColor(Color.red);
	else
	    toplevel.changeColor(Color.blue);
	return true;
    }
}
Let us look at selected parts of this code. The class ColorMenu is a toplevel Frame extended to hold a label and a menu. An additional method is used to set the color of the label.

MenuButton derives from MenuItem and adds the new postEvent and action() methods. We also have to add in a constructor method that just calls the super constructor. We need to do this because MenuItem has only one constructor, which takes a String parameter, and such constructors cannot be inherited in Java.

The buttons we actually want in the menu are of class ColorMenuButton. This derives from the MenuButton class and overrides the action() method. In this, it calls the method changeColor of the toplevel ColorMenu, which resets the foreground of its Label. The toplevel is passed through as a parameter in the constructor. An alternative could be to walk up the parent tree when required till the toplevel is reached.

Conclusion

The AWT toolkit allows quite respectable applications to be written in a platform independent manner. As is inevitable, there are many things missing from the native GUI toolkits (for example, a three mouse button model is supported, unlike the five possible in X).

The AWT model differs from the Motif/Xt model in many places, so forms a new API to be learnt, with its own vagaries. Some of these have been pointed out in this article. Further articles will deal in more detail with other aspects of the toolkit.

This article is based on a tutorial to be given at the X Technical Conference in February, 1996. The tutorial also discusses geometry management and adding new new widgets, among other things.

Further information and availability

For tutorials on Java, see http://sunsite.unc.edu/javafaq/javatutorial.html

For other tutorials on the AWT library, see http://jan.newmarch.name/java/index.html , http://ugweb.cs.ualberta.ca/~nelson/java/AWT.Tutorial.html .

For information on Java books, see http://www.aw.com/cp/javaseries.html or http://www.digitalfocus.com/digitalfocus/faq/books.html .

The principal site for Java is the development site http://java.sun.com/ . This points to pages to download executables for Solaris, Windows NT and Windows 95. You can also get the source code, under certain conditions. An excellent Linux port of Java has been performed by Randy Chapman This is available from ftp://java.blacktown.org/pub/Java/linux