| ## Java 1.1 Unleashed # - 14 - The Windowing (AWT) Package by David R. Chung revised by Christopher Burdess IN THIS CHAPTER Components Events Layout Management Utility Classes Peers The Abstract Windowing Toolkit (AWT) package in the JDK is a library of Java classes you can use to create graphical user interfaces (GUIs). These interfaces allow users to interact with the Java application or applet in the same way they are accustomed to interacting with other applications on their native platforms. Because Java is a multiplatform language, the AWT is not intended to implement all the features of any specific GUI operating system, merely the features common to all operating systems. Programming with the AWT is therefore a highly portable user interface solution. The AWT classes can essentially be divided into the following three categories: Components Layouts Utility classes This chapter examines each of these categories and provides examples of how to incorporate AWT classes into your Java applications and applets. Additionally, this chapter presents a discussion of peer components and their advantages and limitations. ## Components Otherwise known as user interface control elements, controls, or widgets, components is the name given to a wide variety of interface elements that can be displayed to the user. Components can also be programmed to respond to interaction by the user with the mouse or keyboard. This event handling is an important part of GUI programming and is discussed later in this chapter in the "Events" section. Most AWT components are derived from the `Component` class. The notable exception is the `MenuComponent` class and its subclasses; these are discussed later in this section. The `Component` class is an abstract class that defines elements common to all components: font, color, painting, reshaping, and event handling. NOTE: Abstract classes are classes that declare one or more methods as `abstract`. Abstract methods must be overridden by a subclass; abstract classes cannot be instantiated. The `Component` class tree is shown in Figure 14.1. All classes belong to the `java.awt` package except the `Applet` class, which is in the `java.applet` package. NOTE: A class in one package can be derived from a superclass in another package. Thus, the `Applet` class, in the `java.applet` package, is derived from the `Panel` class`` in the `java.awt` package. A package is not an indication of class hierarchy, merely a convenient way to bundle certain kinds of functionality. The aspect of primary functional importance of `Applet` objects is that they can be embedded in Web documents and be interpreted by a Web browser according to certain security rules, not that they form part of a graphical user interface to a Java program. Figure 14.1. The `Component` class tree. Components can be further subdivided into four main functional groups: Simple widgets Text components Containers The `Canvas` class This chapter also discusses menus. Although menus are not derived from the `Component` class and do not share some of the properties of components (such as position, width, color, and so forth), menus do have other properties similar to components--notably fonts and event handling. In any case, I will continue to refer to menu elements as components because they are evidently visible elements of the user interface. See the "Menus" section, later in this chapter, for further details. ### Simple Widgets Simple widgets are components for which the user interaction required is minimal or nonexistent and which are not containers for any other kind of component. Simple widgets include the `Button`, `Checkbox`, `Choice`, `Label`, `List`, and `Scrollbar` classes. Correlations of these components exist on most GUI operating systems. The `SimpleWidgetApplet` applet in Figure 14.2 shows the AWT's simple widgets on the Microsoft Windows NT 4.0 platform. The code for the `SimpleWidgetApplet` applet is shown in Listing 14.1 and is provided on the CD-ROM that accompanies this book. Figure 14.2. The `SimpleWidgetApplet` applet shows the AWT's simple widgets. #### Listing 14.1. The SimpleWidgetApplet applet.
    import java.awt.*;

    public class SimpleWidgetApplet extends java.applet.Applet {

         Button button = new Button("Click me!");
         Checkbox checkbox = new Checkbox("Tick me!");
         Choice choice = new Choice();
         Label label = new Label("This just displays some text.");
         List list = new List();
         Scrollbar scrollbar = new Scrollbar();

         public void init() {
              choice.add("Item 1");
              choice.add("Item 2");
              choice.add("Item 3");

              list.add("Item 1");
              list.add("Item 2");
              list.add("Item 3");

              add(button);
              add(checkbox);
              add(choice);
              add(label);
              add(list);
              add(scrollbar);
         }
    }
First of all, we create the widgets. Some of them have handy constructors that can be used to set their initial values; for example, the `Button` class can be passed a `String` in its constructor to initialize the button's label. Other components do not have useful constructors, usually because the number of values that must be passed is not known (as is the case with the `Choice` and `List` components). In this case, you use the default constructor and set its properties later. With the `Choice` widget in `SimpleWidgetApplet`, we use its `add(`String`)` method to set the strings we want to appear in the choice list. Note that when you add items to a list (and later to menus) in this way, no sorting takes place--the strings appear in the order you add them. Then we are ready to add these components to the applet using the applet's `add(`Component`)` method. Sometimes, you can cut out a step by creating a component and adding it at the same time: `add(new Button("Hello!"));` This approach is sometimes useful for labels. However, you usually want to keep a reference to the object you have created, as we do in `SimpleWidgetApplet`. Keeping a reference to the object allows you to easily refer to the objects you have created in other methods in the class-- especially event-handling methods--so that you can examine and set their properties. The functionality provided by the simple widgets is as follows: The `Label` component, as we have discovered, is used just to display text for the user--the user can't do anything with a label. The `Button` component is used to trigger an associated action. The `Checkbox` component is used to represent the state of a `boolean` variable (`true` or `false`, on or off). Checkboxes can be associated with a particular `CheckboxGroup` object, either by constructing them with it or by using the `setCheckboxGroup(`CheckboxGroup`)` method. When a checkbox is part of a group, only one of the checkboxes can be selected at a time; if one is selected, the others are all deselected. The `Choice` component is used to select one of a number of string options. Only the selected option is shown--other options are displayed in a popup window when the `Choice` component is clicked. The `List` component can be used in the same way as the `Choice` component: to select one of a number of string options. Additionally, a `List` component can contain multiple selections; you can specify this either in the constructor or using the `setMultipleMode(`boolean`)` method. For a multiple-selection `List` component, the user can select several options at a time. The `Scrollbar` component is used to represent a bounded numeric value. Normally, this value is itself used to represent the amount of a component that is visible in a container. See the discussion about the `ScrollPane` class in the "Containers" section, later in this chapter, for details. ### Text Components You can add to your Java applications and applets two AWT components into which the user can enter text with the keyboard. These text components are `TextField` and `TextArea`; they are both subclasses of `TextComponent` (refer back to Figure 14.1). The `TextComponent` class is an abstract base class that encapsulates much that is common to the two components. The `TextField` class is a simple one-line text editing component; the `TextArea` class supports multiple lines and vertical scrolling. Both components can be used to display and let the user specify string values. An example is shown in Figure 14.3. The code for the `TextComponentApplet` applet is given in Listing 14.2 and on the CD-ROM that accompanies this book. Figure 14.3. The `TextComponentApplet` applet. #### Listing 14.2. The TextComponentApplet applet.
    import java.awt.*;

    public class TextComponentApplet extends java.applet.Applet {
         TextField textfield = new TextField("This is a TextField");
         TextArea textarea = new TextArea("This is a TextArea\nThis is the second line.");

         public void init() {
              add(textfield);
              add(textarea);
         }
    }
TIP: As you can see from this example, text components have handy constructors you can use to specify the strings to be displayed initially. You can also set other properties of text components: Both `TextField` and `TextArea` allow you to specify the number of (character) columns in the component; the `TextArea` component also allows you to set the number of rows (lines) in the component. ### Containers A container is a component that can hold other components. All containers in the AWT are derived from the `Container` abstract class (refer back to Figure 14.1). The main containers are `Panel`, `ScrollPane`, and `Window`. Panels are normally used to group components into specific areas of the display. Because every component can be given a separate layout, panels are useful if you want to position components according to a different layout than what was used by its parent container (usually either some form of `Window` or an `Applet`). An `Applet` is a kind of panel with additional properties relating to browser implementations; the `Applet` class in the `java.applet` package is the only AWT-derived class that is not in the `java.awt` package or one of its subpackages. Scroll panes are a form of panel. You can add a child component to the `ScrollPane` container that may have dimensions greater than the `ScrollPane` container itself. When this is the case, the scroll pane provides horizontal and/or vertical scrollbars that allow the user to display different areas of the child component. The `Window` class encapsulates a top-level window. By itself, the `Window` class represents a window with no title bar or border. Subclasses of the `Window` class provide titles, icon images, and window user control functions such as close commands and minimize/maximize capabilities. These subclasses are `Frame` and `Dialog`. TIP: You can use the `Window` class as a base superclass for popup windows and components, such as ToolTips. The `Frame` class provides all the functionality of an application window in the native operating system--with the notable exception that you cannot access the operating system's handle or PID for the window (or other windows in the environment). Frames are the basis for the majority of Java GUI applications. Because frames also implement the `MenuContainer` interface, they can possess a menu bar (see "Menus," later in this chapter). The `Dialog` class represents a window that normally appears for a short time to display or request transient information. If some information is needed for a step in a computation (for example, `This file has been edited. Do you want to save it?`) but is not required after that step, you should use a dialog box. In the same way, if your application must notify the user that some step has been carried out and requires feedback from the user indicating that the user has understood the notification, you should use a dialog box. A `Dialog` object can be declared modal either by constructing it as such or by calling `setModal(true)`. A modal dialog box is one that does not allow user interaction with its parent frame until the dialog box has been dismissed. The `FileDialog` class represents a special kind of dialog box that prompts the user for a path or filename. Because paths and filenames are specified differently under different operating systems, this class is used to remove the associated difficulty of catering for all possible specifications. Note that all windows are initially invisible when they are created. To make a window you have created visible, you must invoke its `show()` method. Normally, you do this after you have added all the components to it (unless, of course, you are developing dynamic components). For an example of how frames and dialog boxes work, look at the `DialogFrame` application detailed in "Events," later in this chapter. ### Menus Menu components, as mentioned earlier in this chapter, are not derived from the `Component` class. Menu components all derive from the abstract `MenuComponent` class. Figure 14.4 shows the `MenuComponent` hierarchy. Figure 14.4. The `MenuComponent` class tree. The `MenuBar` class encapsulates the concept of a menu attached to a `Frame`. You can set the `MenuBar` for a `Frame` by invoking its `setMenuBar(`MenuBar`)` method. The `MenuFrame` application shown in Figure 14.5 and Listing 14.3 (and provided on the CD-ROM that accompanies this book) demonstrates how to do this. Figure 14.5. The `MenuFrame` application. #### Listing 14.3. The MenuFrame application.
    import java.awt.*;

    public class MenuFrame extends Frame {
         MenuItem fileNew = new MenuItem("New");
         MenuItem fileOpen = new MenuItem("Open...");
         MenuItem fileSave = new MenuItem("Save");
         MenuItem fileExit = new MenuItem("Exit");
         MenuItem editUndo = new MenuItem("Undo");
         MenuItem editCut = new MenuItem("Cut");
         MenuItem editCopy = new MenuItem("Copy");
         MenuItem editPaste = new MenuItem("Paste");
         MenuItem helpContents = new MenuItem("Contents");
         MenuItem helpAbout = new MenuItem("About MenuFrame...");

         public MenuFrame() {
              super("Menu example");

              MenuBar menubar = new MenuBar();
              Menu fileMenu = new Menu("File");
              Menu editMenu = new Menu("Edit");
              Menu helpMenu = new Menu("Help");

              fileMenu.add(fileNew);
              fileMenu.add(fileOpen);
              fileSave.setEnabled(false);
              fileMenu.add(fileSave);
              fileMenu.addSeparator();
              fileMenu.add(fileExit);

              editUndo.setEnabled(false);
              editMenu.add(editUndo);
              editMenu.addSeparator();
              editCut.setEnabled(false);
              editMenu.add(editCut);
              editCopy.setEnabled(false);
              editMenu.add(editCopy);
              editPaste.setEnabled(false);
              editMenu.add(editPaste);

              helpMenu.add(helpContents);
              helpMenu.addSeparator();
              helpMenu.add(helpAbout);

              menubar.add(fileMenu);
              menubar.add(editMenu);
              menubar.add(helpMenu);
              menubar.setHelpMenu(helpMenu);

              setMenuBar(menubar);
              setSize(new Dimension(400, 300));
              show();
         }

         public static void main(String[] args) {
              MenuFrame me = new MenuFrame();
         }
    }
As well as specifying the label to appear on menu items, the `MenuItem` class also allows you to construct the menu with a hotkey that can activate the specific `MenuItem`. As you may have noticed, the code in Listing 14.3 disables menu items that are not applicable. In this small example, it is redundant to do so because there will never be a time when the `MenuFrame` object has any content that must be saved or edited. The menu items were disabled only for purposes of explanation. Furthermore, nothing actually happens when the menu items are selected because we have not yet added any event-handling functionality to the application. To see an example of an application that responds to user interaction, see the "Events" section, later in this chapter. #### Popup Menus Popup menus, used to provide contextual functions for the underlying object of a referential component, have become very common on platforms such as Windows 95, NT 4.0, and CDE. The base class for creating popup menus is `PopupMenu`, derived from `MenuComponent`. You can compose a popup menu with this class and display it relative to the referential component by means of its `show(`Component`,` int`,` int`)` method. The other two variables in the `show()` method are the x and y coordinates (in pixels) relative to the specified component. You must add the popup menu to a valid component first, which may or may not be the component from which you specify its position. One of the issues relating to popup menus is the event used to trigger the menu; this event differs from platform to platform. For example, in Motif, the menu pops up on a mouse-down event; in Windows, the menu appears on a mouse-up event. The `MouseEvent` class has a special `isPopupTrigger()` function to deal with this problem. ### The Canvas Class and Graphics Contexts The `Canvas` class is a generic component class that can be used to implement graphics methods. By itself, the `Canvas` class appears similar to a `Panel` class but it is not a container and you cannot add other components to it. Essentially, the `Canvas` class is just an instantiable `Component` class. Canvases are subclassed extensively to display images, draw graphs, and create custom components such as progress bars, tabbed folders, and custom buttons. This is achieved by overriding the `paint(`Graphics`)` method. With the provided `Graphics` context, you can invoke graphics methods to draw points, lines, text, and images into the canvas. The `Graphics` object provides an encapsulation of the state information required for the various rendering methods Java uses, such as the current color, font, and rendering translation origin. Coordinates passed to `Graphics` methods are considered relative to this origin; rendering methods invoked on the `Graphics` object only modify pixels within the bounds of its clipping rectangle. Some of the more useful methods provided by this object include `drawImage()`, which is used to render an `Image` into the graphics context; `drawLine()`, which is used to draw lines; `drawOval()`, which is used to draw ellipses and circles; `drawString()`, which is used to draw text; `fillRect()`, which is used to draw filled rectangles; `getFontMetrics()`, which retrieves a `FontMetrics` object containing information about the current font, which can then be used to calculate where to draw text; and `setColor()` and `setFont()`, which are used to select colors and fonts for drawing. For more information about fonts, colors, and images, refer to "Utility Classes," later in this chapter. The `ImageButton` class demonstrates how to subclass the `Canvas` class to draw a button that can display an image and text. Figure 14.6 shows an `ImageButton` object added to a `Frame` object on the Windows NT 4.0 platform. Listing 14.4 shows the code to accomplish this feat (the code is also provided on the CD-ROM that accompanies this book). Figure 14.6. The `ImageButton` component displayed in a frame. #### Listing 14.4. The ImageButton class.
    import java.awt.*;

    public class ImageButton extends Canvas {

         private String label;
         private Image image;

         public ImageButton() {
              this(null, null);
         }

         public ImageButton(String label) {
              this(label, null);
         }

         public ImageButton(Image image) {
              this(null, image);
         }

         public ImageButton(String label, Image image) {
              this.label = label;
              this.image = image;
         }

         public String getLabel() {
              return label;
         }

         public Image getImage() {
              return image;
         }

         public void setLabel(String label) {
              if(!label.equals(this.label)) {
                   this.label = label;
                   repaint();
              }
         }

         public void setImage(Image image) {
              if(!image.equals(this.image)) {
                   this.image = image;
                   repaint();
              }
         }

         public void paint(Graphics g) {
              Color b = getBackground();
              Font f = g.getFont();
              Rectangle r = g.getClipBounds();

              super.paint(g);

              // Draw the image.
              // Scale it so it fits in the clip rectangle and centre it vertically.
              if(image != null && r.width>4 && r.height>4) {
                   Dimension d = new Dimension(image.getWidth(this), image.getHeight(this));
                   float scale = 1;
                   float fdwidth = (new Integer(d.width)).floatValue();
                   float frwidth = (new Integer(r.width)).floatValue();
                   float fdheight = (new Integer(d.height)).floatValue();
                   float frheight = (new Integer(r.height)).floatValue();
                   if((fdwidth*scale) > (frwidth*scale))
                        scale = scale * (frwidth/fdwidth);
                   if((fdheight*scale) > (frheight*scale))
                        scale = scale * (frheight/fdheight);
                   int ndwidth = (new Float(fdwidth * scale)).intValue() - 4;
                   int ndheight = (new Float(fdheight * scale)).intValue() - 4;
                   int yOffset = (r.height - ndheight) / 2;
                   g.drawImage(image, 2, yOffset, ndwidth, ndheight, this);
              }

              // Draw the label.
              // Centre it in the clip rectangle.
              if(label!=null) {
                   FontMetrics fm = g.getFontMetrics();
                   g.drawString(label, (r.width - fm.stringWidth(label)) / 2, (r.height/2) + (fm.getAscent()/2));
              }

              // Draw the button bevel.
              // We need to draw the lines twice to make them thicker.
              g.setColor(b.brighter());
              g.drawLine(0, r.height-1, 0, 0);
              g.drawLine(1, r.height-2, 1, 1);
              g.drawLine(0, 0, r.width-1, 0);
              g.drawLine(1, 1, r.width-2, 1);
              g.setColor(b.darker());
              g.drawLine(r.width-1, 0, r.width-1, r.height-1);
              g.drawLine(r.width-2, 1, r.width-2, r.height-2);
              g.drawLine(r.width-1, r.height-1, 0, r.height-1);
              g.drawLine(r.width-2, r.height-2, 1, r.height-2);
         }
    }
You may have noticed that in the `paint()` routine, the variables `height` and `width` refer to the height and width of the `ImageButton`. These numbers are `1` greater than the index of the last pixel in the clip rectangle; the pixels are numbered `0` to `width-1` and `0` to `height-1`, in the same way that the length of an array is `1` greater than the index of the last element of the array. The `ImageButton` class is far from encapsulating a real button that can be clicked to trigger events. To make it a clickable button, you must define a `boolean` instance variable (for example, `down`) that is flagged when the button receives a mouse-down event and unflagged when it receives a mouse-up event. The `paint()` routine can then examine the `down` variable and draw the button appropriately. For example, you may want to switch the `g.setColor()` calls in the bevel-drawing routine to reverse the bevel; you may also want to offset the image and text to the right and down by some factor to give the user some feedback that the button has been clicked. In Windows, button backgrounds are dithered when they are clicked; you may want to implement this feature. To bring some life to the `ImageButton` object, it must handle events. Event handling is discussed next. ## Events Event handling is one of the largest concerns for GUI developers. The other is layout management, which is dealt with in "Layout Management," later in this chapter. Fortunately, the AWT makes event handling relatively easy to learn and implement. Before the JDK 1.1 API, an inheritance event model was used, which was found to have considerable overhead and was cumbersome to deploy. In the current version of the JDK, the AWT uses a delegation event model, which is much more robust and flexible and allows for a greater degree of compile-time checking. When the user initiates some event, such as clicking the mouse or typing at the keyboard, the event is generated and made available to your Java application or applet. The following sections describe how to catch these events and process them. ### Listeners and Adapters An event is propagated from a source object (component) to a "listener" object. The source object is the GUI object the user clicked on, typed text into, or otherwise interacted with. Listeners are objects that register themselves as interested in certain kinds of events on that source. Listeners can be other GUI objects or separate objects responsible for delegated events; in the latter case, you can write event-handling code that separates the GUI elements from their functionality. In the AWT, events that are passed are typically derived from the `AWTEvent` class. The AWT provides for two conceptually different kinds of events: low-level and semantic. Low-level events are concerned with the specific user input or window system-level event occurrences. Semantic events are concerned not with the specifics of what interaction occurred, but with its meaning. For example, when you press the Enter key after typing something into a text field, you want to perform some action (such as validating the contents of the text field). In the same way, when you click a button or double-click an item in a list, you want to perform an action. However, if you click a scrollbar (or drag its marker), you want to adjust the value of something. You can see that the low-level event (the click with the mouse) does not perform the same role for different components; this is why you need semantic events. Low-level events and semantic events are both subclassed from the `AWTEvent` class, which is subclassed from `java.util.EventObject`. `AWTEvent` is in the `java.awt` package. All other events discussed here are in the `java.awt.event` package. Figure 14.7 shows the class tree for low-level events. Figure 14.7. The class tree for low-level events. There is, in fact, one more event, the (very) low-level `PaintEvent`, which is normally used only internally; it is not designed to be used with the listener model. Figure 14.8 shows the corresponding class tree for the semantic events. Figure 14.8. The class tree for semantic events. The `ActionEvent` encapsulates our notion of "doing something" or "performing an action." The other two events are more specific: The `AdjustmentEvent` is representative of a numeric variable being adjusted; the `ItemEvent` indicates that the state of an item has changed. According to this model, all your class has to do is to implement a certain listener interface and get itself registered with the source object. However, listeners for low-level events are designed to listen to multiple event types (the `WindowListener`, for example, listens to window activation, closed, closing, deactivation, deiconification, iconification, and opened events); if your class implements such an interface, you must supply the methods that handle these events. TIP: To make things simpler, the AWT event model includes adapter classes for low-level event listeners. These classes, located in the `java.awt.event` package, provide default implementations of all the methods so that you can choose which methods to override in your code. Because the body of the adapter method is empty, using adapters is equivalent to using the listener interfaces directly except that you don't have to specify every method in the listener interface. Figure 14.9 shows the low-level event listener and adapter hierarchy; Figure 14.10 shows the semantic event listener interfaces. All listeners inherit the `java.util.EventListener` interface and are contained in the `java.awt.event` package. Figure 14.9. Low-level event listeners and adapters. Figure 14.10. Semantic event listeners. ### The Event Queue The AWT provides a system event queue, which is represented by the `java.awt.EventQueue` class. The `EventQueue` class provides a static method to return the current system event queue: `getEventQueue()`. You can use the event queue to get sneak previews of events destined for objects in the system, and, most importantly, you can push new events onto the queue. There are security issues involved here: Although applications can make free use of the event queue, allowing untrusted applets to freely manipulate events can have serious implications. Thus, the `getEventQueue()` method is protected by a `SecurityManager` check that does not allow untrusted applets direct access to the queue. Instead, appropriate `Applet` methods for peeking at the queue exist. These methods are restricted so that applets can only access events contained on components within their own containment hierarchy. ### Event Handling The `DialogFrame` application in Listing 14.5 (and included on the CD-ROM that accompanies this book) demonstrates the use of the various event objects and methods, as well as how the `Frame` and `Dialog` classes work. #### Listing 14.5. The DialogFrame application.
    import java.awt.*;
    import java.awt.event.*;

    public class DialogFrame extends Frame implements ActionListener, WindowListener {

         Dialog dialog;
         Button openDialogButton;
         Button closeDialogButton;
         Button closeFrameButton;

         public DialogFrame() {
              super("Dialog and Frame example");
              setLayout(new FlowLayout());
              addWindowListener(this);

              openDialogButton = new Button("Open dialog");
              openDialogButton.addActionListener(this);

              closeFrameButton = new Button("Close me");
              closeFrameButton.addActionListener(this);

              add(openDialogButton);
              add(closeFrameButton);

              pack();
              show();
         }

         public void showDialog() {
              dialog = new Dialog(this, "This is the dialog", true);
              dialog.setLayout(new FlowLayout());
              dialog.addWindowListener(this);

              closeDialogButton = new Button("Close dialog");
              closeDialogButton.addActionListener(this);

              dialog.add(closeDialogButton);

              dialog.pack();
              dialog.show();
         }

         public void actionPerformed(ActionEvent e) {
              String buttonCommand = e.getActionCommand();
              if(buttonCommand.equals("Open dialog"))
                   showDialog();
              else if(buttonCommand.equals("Close dialog"))
                   dialog.dispose();
              else if(buttonCommand.equals("Close me"))
                   processEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
         }

         public void windowClosing(WindowEvent e) {
              Window originator = e.getWindow();
              if(originator.equals(this)) {
                   this.dispose();
                   System.exit(0);
              } else if(originator.equals(dialog))
                   dialog.dispose();
         }

         public void windowActivated(WindowEvent e) { }
         public void windowDeactivated(WindowEvent e) { }
         public void windowDeiconified(WindowEvent e) { }
         public void windowClosed(WindowEvent e) { }
         public void windowIconified(WindowEvent e) { }
         public void windowOpened(WindowEvent e) { }

         public static void main(String[] args) {
              DialogFrame me = new DialogFrame();
         }
    }
First, we ensure that the `DialogFrame` class can receive events from the sources it is interested in. And it is interested in the clicking of buttons (which are semantic events) and window events (so that it can intercept low-level window closing events--otherwise, the user cannot terminate the application by closing the window). Thus, `DialogFrame` implements the `ActionListener` and `WindowListener` interfaces and provides methods to handle the events it receives as a result. When `DialogFrame` is constructed, it first calls the superconstructor in the `Frame` class with a string argument that sets the specified string to be the window title. The next line sets the layout (as described in "Layout Management," later in this chapter). The next method, `addWindowListener(`WindowListener`)`, registers the `DialogFrame` with itself for the purposes of receiving window events. Then the code creates the buttons that go on the frame and registers itself as an `ActionListener` for action events on those buttons. Because windows are always created invisible, the code then calls `show()` to display the frame. The `showDialog()` function constructs a modal dialog box and registers `DialogFrame` as a window listener for it. The method also creates a button on the dialog box and registers `DialogFrame` as an action listener for the button. Now we implement the event-handling methods for `DialogFrame`. First, we implement the `actionPerformed(`ActionEvent`)` method for the button clicks. We work out which button has been clicked and perform the relevant action for it. In this example, we use the `ActionEvent`'s `getActionCommand()` method to return the button label. Alternatively, we could have used `ActionEvent`'s `getSource()` method to return the actual source object and then compared it with the button references we have (`openDialogButton` and so on). The `openDialogButton` click calls `showDialog()` to show the dialog box; the `closeDialogButton` click calls `dispose()` on the dialog box, which hides it and removes any excess system resources used to manage the window. The `closeFrameButton` click, however, does not close the frame directly; it posts a new window-closing event to the frame. This event is picked up by `DialogFrame` in its role as window listener and is processed as if it is a request to close the window (which, effectively, it is). Why would we bother to go through this tortuous arrangement of posting events to ourselves in this way, when disposing of the window and calling `System.exit()` is easy and straight-forward? In this case, that is all the processing to be done by the window listener's closing routine, but when you develop more complex applications, you will find that you must frequently do more processing before the application can exit successfully. If you start off duplicating code in different methods, and you have to change the code in some way at a later date, you must always consider all the other code snippets that are supposed to be providing the same functionality. Many experienced programmers write special methods such as `closeAndExit()`, which is called by any routine that wants to close down the application, thereby ensuring that there is only one way that the system can exit. TIP: Because `DialogFrame` is an application and not an applet, instead of calling `processEvent()`, we could easily have requested a handle to the system event queue and posted the window-closing event there: `EventQueue.getEventQueue().postEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));``` However, because `processEvent()` causes the event to be handled immediately, this method is usually more useful. Next, we must override the relevant methods in `WindowListener` to handle window events. We compare the source window with our references to `DialogFrame` and its child dialog box and then dispose of the windows accordingly. We must also provide empty implementations of the other `WindowListener` methods because we are not using a separate adapter object. Note that because this is an application and not an applet, we must provide a `main()` method with the following form: `public static void main(String[])` In this form, the `String` array parameter references any command-line parameters passed to the application. As soon as we have compiled this class, we can run it with a Java interpreter from the command line, like this: `java DialogFrame` ### The Focus and Keyboard Events The focus is a term used to describe which component is to receive keyboard events. Because numerous components, each potentially capable of handling keyboard events, may be visible in the same window, the focus is used to determine the destination of the keyboard events. For example, if you want to receive keyboard events on a `Frame` or `Canvas` object, the component must first request the focus using the `requestFocus()` method. The AWT provides for mouseless focus traversal, which is implemented by pressing the Tab key (to move forwards) or Shift+Tab (to move backwards). Some components (those based on peers in the underlying platform) have this ability innately. For example, if you develop new components derived from `Canvas` that are destined to receive keyboard events, you should override the `isTabbable()` method to return `true` and catch the mouse-down event on the component to call `requestFocus()`. Most user-interface component designers also implement additional drawing routines when the component has the focus; a button, for example, may have a dotted line around the inside of the bevel. ### Clipboard Functions Most GUI users expect to be able to transfer data such as text and images between applications using the clipboard functions cut, copy, and paste. The API for transferable objects is based around the `java.awt.datatransfer.Transferable` interface. Objects that implement this interface must provide a list of "flavors" or formats in which it can provide the actual data. The `java.awt.datatransfer` package provides one convenient class for the most common data type: the `StringSelection` class implements the `Transferable` interface. The `Clipboard` class encapsulates a clipboard in Java. You are free to create as many of these objects as you like for private purposes. One clipboard object, however, called `System`, is used to interface with the system clipboard and hence nonJava applications. You can retrieve the `System` clipboard with a call to `getSystemClipboard()` on a valid `Toolkit` object. To write data to a `Clipboard` object, you must implement the `ClipboardOwner` interface. Transfer occurs with two methods: To write data to a clipboard, you use the `Clipboard` method `setContents(`Transferable`,` ClipboardOwner`)`; to read from the clipboard, use the `getContents(`Object`)` method. The ClipboardOwner and Object`` parameters refer to the calling object; the Transferable objects involved are the clipboard data. When you read from the clipboard, you must also request a list of available flavors from the Transferable`` object and read the data in the desired flavor with the Transferable object's `getTransferData()` method. ## Layout Management So far in this chapter, the examples have just been adding components to containers without any indication of how those components are to be arranged in the display. The AWT provides a group of classes known as layout managers, or layouts, that handle this kind of placement. All layouts implement the `LayoutManager` interface. Once you set a layout for a container using the `setLayout(`LayoutManager`)` method, and then add a component to the container, you also add the component to the container's layout. The layouts provided with the AWT library are discussed in the following sections. ### The FlowLayout Manager The `FlowLayout` manager sets out the components in rows. This layout is most useful for implementing button toolbars and the like. The `FlowLayout` manager fits as many components as it can on one row before populating the row below. The order of the components in the row is determined by the order in which they are added to the container. `FlowLayout` is the default layout for the `Panel` class, so applets also use this layout manager if none is specified. You can specify the spacing between components in a `FlowLayout` by using the `setHgap()` and `setVgap()` methods or by constructing it with these values (the default is `5` pixels for both horizontal and vertical gaps). The `FlowLayout` manager respects a component's `Insets`, so these are added to the total gap between components if they are specified. TIP: You can also specify the alignment of the flow (which side it aligns the components against) either in the constructor or with the `setAlignment()` method. The `ToolbarApplet` shown in Figure 14.11 and Listing 14.6 (and included on the CD-ROM that accompanies this book) displays a set of buttons positioned with a `FlowLayout` manager with `RIGHT` alignment. Figure 14.11. The `ToolbarApplet` applet. #### Listing 14.6. The ToolbarApplet applet.
    import java.awt.*;

    public class ToolbarApplet extends java.applet.Applet {

         public void init() {
              setLayout(new FlowLayout(FlowLayout.RIGHT));
              add(new Button("Button 1"));
              add(new Button("Button 2"));
              add(new Button("Button 3"));
              add(new Button("Button 4"));
              add(new Button("Button 5"));
         }
    }
### The BorderLayout Manager The `BorderLayout` manager is a simple layout that places controls so that they fill their container. When you add a component to a container that has a `BorderLayout` layout, you can also specify one of five constraints in the form of a string: `"North"`, `"East"`, `"South"`, `"West"`, and `"Center"`. The first four constraints specify which edge of the container the component will be attached to (north, east, south, or west). The `"Center"` constraint places the component in the center of the container. If you do not specify a constraint, the default constraint is `"Center"`. A maximum of five components can be displayed in the container, although you can create more complex arrangements by embedding containers within one another. The `BorderLayout` manager is the default layout for the `Window` class, so frames and dialog boxes use the `BorderLayout` manager if none is specified. TIP: You can specify horizontal and vertical gaps with the `BorderLayout` manager in the same way you can with the `FlowLayout` manager. Figure 14.12 shows how components are laid out with the `BorderLayout` manager; Listing 14.7 shows the code used to accomplish this. This code is also included on the CD-ROM that accompanies this book. Figure 14.12. The `BorderButtonApplet` applet. #### Listing 14.7. The BorderButtonApplet applet.
    import java.awt.*;

    public class BorderButtonApplet extends java.applet.Applet {

         public void init() {
              setLayout(new BorderLayout());
              add(new Button("North"), "North");
              add(new Button("East"), "East");
              add(new Button("South"), "South");
              add(new Button("West"), "West");
              add(new Button("Centre"));
         }
    }
### The CardLayout Manager The `CardLayout` manager is an interesting layout in that it does not attempt to resize components to display them all in the container. Instead, it displays them one at a time in a Rolodex fashion. Listing 14.8 shows the `ThreePagesApplet` applet (the applet is also located on the CD-ROM that accompanies this book); Figure 14.13 shows this `CardLayout` container displaying its third control, a panel with some buttons and text. Figure 14.13. The `ThreePagesApplet` applet. #### Listing 14.8. The ThreePagesApplet applet.
    import java.awt.*;
    import java.awt.event.*;

    public class ThreePagesApplet extends java.applet.Applet implements MouseListener {

         Button page1Button;
         Label page2Label;
         TextArea page3Text;
         Button page3Top;
         Button page3Bottom;
         CardLayout layout;

         public void init() {
              setLayout(layout = new CardLayout());

              add(page1Button = new Button("Button page"), "page1Button");
              page1Button.addMouseListener(this);

              add(page2Label = new Label("Label page"), "page2Label");
              page2Label.addMouseListener(this);

              Panel panel = new Panel();
              panel.setLayout(new BorderLayout());

              panel.add(page3Text = new TextArea("Composite page"));
              page3Text.addMouseListener(this);

              panel.add(page3Top = new Button("Top button"), "North");
              page3Top.addMouseListener(this);

              panel.add(page3Bottom = new Button("Bottom button"), "South");
              page3Bottom.addMouseListener(this);

              add(panel, "panel");
         }

         public void mouseClicked(MouseEvent e) {
              layout.next(this);
         }

         public void mouseEntered(MouseEvent e) { }
         public void mouseExited(MouseEvent e) { }
         public void mousePressed(MouseEvent e) { }
         public void mouseReleased(MouseEvent e) { }

    }
When the user clicks any of the components in the applet, the `ThreePagesApplet` shows the next component in its layout. This is done by calling the `next()` method on the layout. The position methods for the `CardLayout` are listed here: `first(`Container`)` `next(`Container`)` `previous(`Container`)` `last(`Container`)` These methods move to the first, next, previous, and last components in the layout, respectively. TIP: All these methods require a container to be specified, because you can use the same layout manager object for more than one container at a time. Normally (unless you have a good reason not to), you create a separate layout manager for each container. Doing so allows you to modularize your code more easily. If, however, you want to minimize the total number of objects in a large application, sharing a layout manager between containers may be one of your solutions. When components are added to the container, you specify a string that the `CardLayout` manager uses to identify the control. Instead of calling position methods such as `next()` and `first()`, you can also jump to the component with the specified label by calling the `show(`Container`,` String`)` method on the layout. For example, if you want to jump to the second page in the `ThreePagesApplet`, you can use this call: `layout.show(this, "page2Label");` ### The GridLayout Manager The `GridLayout` manager is used to lay out components in a grid of evenly spaced cells. The default constructor creates a grid with one column per component, but you can use other constructors to specify the exact number of rows and columns you need as well as the spacing between the cells. As you can see in Figure 14.14, the `GridLayout` manager populates its cells from left to right and top to bottom. Listing 14.9 shows the code used to create this applet. This applet is also located on the CD-ROM that accompanies this book. Figure 14.14. The `GridButtonApplet` applet. #### Listing 14.9. The GridButtonApplet applet.
    import java.awt.*;

    public class GridButtonApplet extends java.applet.Applet {

         public void init() {
              setLayout(new GridLayout(3, 3));
              add(new Button("Cell 1"));
              add(new Button("Cell 2"));
              add(new Button("Cell 3"));
              add(new Button("Cell 4"));
              add(new Button("Cell 5"));
              add(new Button("Cell 6"));
              add(new Button("Cell 7"));
              add(new Button("Cell 8"));
              add(new Button("Cell 9"));
         }
    }
### The GridBagLayout Manager The `GridBagLayout` manager is the most complex and highly flexible layout manager in the AWT. Once you get used to it, you will use it all the time to develop form-like interfaces. However, it is difficult to learn and has many options. The basis of the `GridBagLayout` manager is a grid. Unlike the `GridLayout` manager, components in the `GridBagLayout` manager are not constrained to a single, regular cell. Each cell has a horizontal and vertical weight, which is used to determine the proportion of the space it takes up as a double-precision floating-point value between `0` (none of the available space) and `1` (all the available space). Also, each component can take up more than one cell. A number of other factors control the placement and sizing of components in a `GridBagLayout` manager; any or all of these factors are specified to the layout by means of a `GridBagConstraints` object, which is added to the layout at the same time as the component, and which specifies the factors for that component. The `GridBagConstraints` class contains the following data members: gridx, gridy: These variables specify the cell coordinates for the northwest corner of the component. You can use the default value `GridBagConstraints.RELATIVE` to specify a horizontal or vertical offset of `1` from the cell of the most recently added component. gridwidth, gridheight: These variables specify the number of horizontal and vertical cells the component is to take up. You can use `GridBagConstraints.REMAINDER` to take up all the remaining cells in a row or column. fill: If the component is smaller than its cell(s), this variable determines whether (and if so, how) to resize the component to fit into the available area. Valid arguments are `GridBagConstraints.NONE` (don't resize), `GridBagConstraints.HORIZONTAL` (make use of all the width), `GridBagConstraints.VERTICAL` (make use of all the height), and `GridBagConstraints.BOTH` (make use of all the space). anchor: If the component is smaller than its cell(s), this variable determines what corner or edge of the cell area to attach the component to. Valid arguments are `GridBagConstraints.CENTER` (the default), `GridBagConstraints.NORTH`, `GridBagConstraints.EAST`, `GridBagConstraints.SOUTH`, `GridBagConstraints.WEST`, `GridBagConstraints.NORTHWEST`, `GridBagConstraints.NORTHEAST`, `GridBagConstraints.SOUTHEAST`, and `GridBagConstraints.SOUTHWEST`. weightx, weighty: These variables are used to determine the weight of a cell, or to what extent excess space is added to the row or column. If you do not specify the weight of at least one row and column, the components are laid out in the center because specifying a weight of `0` (the default) adds any extra space at the edges. Insets: The `Insets` object specifies the distance in pixels between the edges of the component and the edges of its cell(s). ipadx, ipady:These variables control the horizontal and vertical padding between the component's edge and that of its cell(s). CAUTION: Although the variables `ipadx` and `ipady` are still supported in `GridBagConstraints` and by the layout manager, they are essentially superseded by the `Insets` variable and their use is no longer recommended. To add a component to a `GridBagLayout` manager, you must first set the data members just listed for a `GridBagConstraints` object and then add the component and the constraints to the container. The `GridBagSimpleWidgetApplet` in Listing 14.10 is an example of using a `GridBagLayout` manager to control the layout of the components created in `SimpleWidgetApplet`, earlier in this chapter (see Figure 14.15). `GridBagSimpleWidgetApplet` is also included on the CD-ROM that accompanies this book. Figure 14.15. The `GridBag- SimpleWidgetApplet` applet. #### Listing 14.10. The GridBagSimpleWidgetApplet applet.
    import java.awt.*;

    public class GridBagSimpleWidgetApplet extends java.applet.Applet {

         public void init() {

              GridBagConstraints gbc;
              setLayout(new GridBagLayout());

              Button button = new Button("Click me!");
              Checkbox checkbox = new Checkbox("Tick me!");
              Choice choice = new Choice();
              Label label = new Label("This just displays some text.");
              List list = new List();
              Scrollbar scrollbar = new Scrollbar();

              choice.add("Item 1");
              choice.add("Item 2");
              choice.add("Item 3");

              list.add("Item 1");
              list.add("Item 2");
              list.add("Item 3");

              gbc = new GridBagConstraints();
              gbc.gridx = gbc.gridy = 0;
              gbc.anchor = GridBagConstraints.SOUTH;
              gbc.weightx = gbc.weighty = 1.0;
              add(label, gbc);

              gbc = new GridBagConstraints();
              gbc.gridx = 0;
              gbc.fill = GridBagConstraints.BOTH;
              gbc.gridwidth = 2;
              gbc.gridheight = 3;
              gbc.weightx = 1.0;
              gbc.weighty = 3.0;
              add(list, gbc);

              gbc = new GridBagConstraints();
              gbc.gridy = 0;
              gbc.fill = GridBagConstraints.HORIZONTAL;
              gbc.anchor = GridBagConstraints.NORTH;
              gbc.weightx = gbc.weighty = 1.0;
              add(choice, gbc);

              gbc = new GridBagConstraints();
              gbc.gridy = 0;
              gbc.weightx = gbc.weighty = 1.0;
              add(button, gbc);

              gbc = new GridBagConstraints();
              gbc.gridy = 2;
              gbc.anchor = GridBagConstraints.NORTHEAST;
              gbc.weightx = gbc.weighty = 1.0;
              add(checkbox, gbc);

              gbc = new GridBagConstraints();
              gbc.gridy = 0;
              gbc.gridheight = GridBagConstraints.REMAINDER;
              gbc.fill = GridBagConstraints.VERTICAL;
              gbc.anchor = GridBagConstraints.EAST;
              gbc.weightx = gbc.weighty = 1.0;
              add(scrollbar, gbc);

         }

    }
### Custom Layouts If you still can't get the required layout behavior for your container, you can write your own layout manager. You must implement either the `LayoutManager` or `LayoutManager2` interface and supply the relevant methods. `LayoutManager2` is an interface that provides a minimal extension to `LayoutManager` for the purpose of assigning components to the layout based on a constraints object, such as a `GridBagConstraints` object for the `GridBagLayout` manager, or the `"North"` string object for the `BorderLayout` manager. ## Utility Classes Utility classes represent useful structures and resources, many of which are fundamental to graphics programming and can act as properties of widgets. The utility classes are grouped as follows: Vectors: `Dimension`, `Insets`, `Point`, `Polygon`, `Rectangle`, `Shape` Color: `Color`, `SystemColor` Resource: `Cursor`, `Font`, `FontMetrics`, `Graphics`, `Image`, `PrintGraphics`, `PrintJob`, `Toolkit` Resource-manipulation utilities `MediaTracker` Because the vector classes are primarily intended to store and retrieve position information, all their relevant fields are public. An `Insets` object is attached to a component to give an idea of the spacing between that component and the ones around it; `Insets` objects are handled by the layout manager. The color classes effectively encapsulate color to a depth of 16,777,216 colors, which is as many as most current operating systems support. The `SystemColor` class can be used to retrieve information about the current desktop colors, including text and window foreground and background colors. `Cursor` objects encapsulate the shape of the mouse pointer. They can be one of a number of predefined types, including the default cursor (usually an arrow), a text cursor for positioning the caret, a wait cursor to show that some uninterruptable task is in progress, a hand cursor to select relevant items (such as hypertext links), a crosshair cursor for graphics positioning, and various resizing cursors. You cannot define your own cursor shapes; the cursor shapes are determined from the shapes of the platform's cursors. Fonts and font metrics provide a way to describe fonts in Java. Every implementation of Java is guaranteed to have at least six fonts: Courier, Dialog, DialogInput, Helvetica, Times Roman, and ZapfDingbats. The actual font used by the platform depends on the specific platform. For example, Windows uses the MS Sans Serif font for the Dialog font. The `Toolkit` class encapsulates a windowing toolkit on the underlying platform. The `Toolkit` class is used to create peers (see "Peers," later in this chapter), return lists of fonts and current print jobs, and provide a way to request information such as screen resolution and size. ### Loading Images The `Image` class represents an image. There are two standard ways you can load an image into Java, depending on whether you are developing an applet or an application. For an applet, you can use the `getImage(`URL`)` method on the applet itself. For an application, you can use `Toolkit.getDefaultToolkit().getImage(`URL`)` independently. Images in GIF and JPEG format are currently supported by the AWT. When you call `getImage()`, it returns immediately with a valid image reference but loads the image in a separate thread. The `MediaTracker` class can be used to keep track of the process of loading images; for example, if you want to wait until an image is completely loaded before showing a frame, you can use the `MediaTracker` class as follows:
    Frame f = new Frame("MediaTracker test");
    MediaTracker mt = new MediaTracker(f);
    Image img = Toolkit.getDefaultToolkit().getImage(new URL("http://myserver.com/ myimage.gif"));
    mt.addImage(img, 0);
    mt.waitForAll();
    f.show();
### Printing The `PrintJob` class and `PrintGraphics` interface are used to provide the printing API. Printing in Java is very simple. First, you initiate a print job by calling `getPrintJob(`Frame`,` String`,` Properties`)` on a valid `Toolkit` object. The `Properties` object allows you to assign default values for the print job, which is specific to the platform (and sometimes to the printer). The `getPrintJob()` method displays a print dialog box to the user, so the returned `PrintJob` object will be complete with all the properties specified by the user. Next, you can call `getGraphics()` on the `PrintJob`. This method provides a `Graphics` object that implements the `PrintGraphics` interface, which you can pass to the `paint()` or `print()` method, or simply use graphics methods on. When you finish with the graphics context, you can flush it to the printer by calling `dispose()`. Each graphics context represents a page, so you can print multiple pages by making multiple calls to `getGraphics()`. You can determine page attributes and resolution with the `getPageDimensions()` and `getPageResolution()` methods on the `PrintJob`. Note that the initiation of a print job is an operation bound by security considerations: Untrusted applets are not allowed to print potentially subversive material on the printer of their choice. ## Peers The simple widgets (and the `Panel` and `Canvas` classes) in the AWT class library use a peer mechanism to provide the visual aspects of the component. A peer is an object created by the native windowing platform that the Java virtual machine manages. Peers have the same "look and feel" as other, nonJava components on the same platform. For example, a `Choice` list instantiated on a Motif desktop creates a raised component with a small 3D hyphen that pops up a 3D menu centered on the item currently selected, but on a Windows 95 desktop, the same `Choice` list creates a recessed component (like a text field) with a button showing a down arrow that pops up a flat menu below the button. The advantage of using a peer mechanism is that users of a particular windowing platform can always be provided with GUI components that resemble those they are used to, and it allows simple data transfer to and from the system clipboard, because the objects that are created are managed by the platform and not by the Java virtual machine. There are a number of disadvantages with peers, however. First, the area covered by the bounding box of peer-based components will always be filled with the background color of that component. If you want a background image to your application, for example, the image will always be covered by any peer-based components you add to the application. Second, the windowing platform requires additional overhead in the form of system resources to manage the peers that are created, and calls from the Java virtual machine to the operating system to synchronize operations on components with their peer correlates can add to overall processing time. There is an alternative to using peers, however. Instead of subclassing components from `Canvas` and `Panel`, you can instead derive them directly from `Component` and `Container`, writing `paint()` methods in the same way but without the overhead of maintaining a peer. For example, you can derive the `ImageButton` class (described in the preceding section on the `Canvas` class) from `Component` instead of from `Canvas`. This approach means that any painting that occurred on the `ImageButton`'s container would show through the button, unless you first chose to fill the clip rectangle with the button's background color. One other advantage to writing your own peerless components is that you have a great deal of control over the specifics of how the components look. You can also correct failings in the platform-specific implementations of peer classes that you otherwise have no control over in Java. For example, a peer-based `List` widget on a Windows 95 platform always has a black border. If the user wants a black background, he or she will not be able to see the border of their `List` widget. You can correct this possible problem in your peerless component by examining the system colors (described in "Utility Classes," earlier in this chapter) and drawing the border in an appropriate color. NOTE: Sun intends to distribute a larger set of peerless simple widget components (also called lightweight components) in the next version of the AWT. The next version of the AWT should also have, as standards, more complex rendering abilities and drag-and-drop functionality. ## Summary The Java AWT contains a collection of components and ways to lay them out that you can use as-is or extend to provide a complete and consistent multiplatform graphical user interface. Presenting information to the user and being able to respond to a wide variety of ways that users manipulate the application is a complex and challenging task; this task is made considerably easier by the encapsulation of function and display features in AWT objects. By using the AWT, you can create small applets as well as fully featured applications that run on a variety of hardware and software platforms.
| ©Copyright, Macmillan Computer Publishing. All rights reserved.