Translate

Monday, 17 October 2016

Chapter 19 -- Organizing Window Programs

 

Organizing Window Programs



CONTENTS


This chapter covers the basics of writing window programs. It shows you how window programs are structured and organized and identifies the basic approach to designing most window programs. It covers the details of the Frame class and explains how windows are opened and closed. The five basic window layouts are introduced and illustrated through a sample program. The finer points of window event handling are then described. Finally, a window program is created that introduces the most common window GUI controls. When you finish this chapter, you will have had a broad introduction to window programming and will be familiar with most of the common window GUI controls.

Designing Window Programs

The design of most window programs usually involves two basic steps: laying out the program's graphical user interface and providing the functionality that implements the interface. The first step addresses one of the most important features of window programs-its look and feel. Window programs are preferred to console programs when their look and feel are interesting, innovative, and help the user to accomplish a particular purpose. A program's look is determined by how it presents itself to users. It consists of all those characteristics that determine its appearance, such as window size, layout, background and foreground colors, menus, and GUI controls. A program's feel is determined by the availability of easy-to-use GUI controls and the contribution of these controls to the program's ultimate intended use. It is the result of the designer's ability to select and implement those GUI controls that enhance a program's capability to satisfy user expectations. The window's GUI design begins by creating an application window, using the Frame class, and determining the basic characteristics of the window such as its size, title, background and foreground colors, and general layout. Next a menu bar is added to the window, and the program's menus and menu items are added to the menu bar. The GUI controls that are to be used in the window are determined, designed, and attached to the window's panels and frame. At this point, you know what your program will look like and you can concentrate on what it will do. The first step in bringing your program's user interface to life is to add the event-handling software required to respond to events generated as the result of user interaction. The event-handling software will not immediately implement all user actions, but it should respond to them and provide hooks for eventual implementation of all user interface actions. The event-handling software is then fleshed out to provide all the functionality required of the application program. The program's design and implementation reaches an Alpha stage when all required user-interface functions have been implemented. The next stage of program development is to refine and test the program to make it more responsive to its intended purpose. A series of Beta versions of the program are developed that implement user feedback and fix any identified errors or deficiencies. Finally, the program is refined to handle unusual user inputs and to process errors and exceptions. Figure 19.1 provides an overview of the process of designing and implementing window programs. This chapter covers the basics of creating and organizing window programs and shows how to connect event-handling code to general window components. A window sampler program is provided that illustrates the basic use of common window GUI controls. Subsequent chapters explore the use of these GUI controls in more detail. Figure 19.1 : The process for window design and implementation.

Opening and Closing Windows

Opening and closing windows mark the beginning and end of any window program. The Frame class provides the basis by which these fundamental window operations are accomplished. A Frame object implements an application main window, inheriting many methods that enable it to do so from the Window, Container, and Component classes. To open an application window, a Frame object is created and its show() method is invoked. The show() method is inherited from the Window class. To close an application window, the WINDOW_DESTROY event must be handled. The window is disposed of using the dispose() method of the Window class, or more commonly by invoking the System.exit() method after performing any necessary program-termination processing. The Frame class and its ancestors provide a number of methods that control the way in which a window is displayed. The setBackground() and setForeground() methods inherited from the Component class are used to specify a window's background and foreground colors. The setFont() method, also inherited from Component, is used to specify the default font to be used with a window. The Frame class, itself, provides a number of methods that control a window's appearance. The setTitle() method allows a window's title to be changed. The setCursor() method allows the cursor to be changed while it is in the window's focus. The setMenuBar() method enables a menu bar to be attached to a window, and the setResizable() method toggles whether a window can or cannot be resized. The setIconImage() method allows the window's minimized icon to be changed. This method is not supported by all Java implementations and therefore should be avoided if compatibility is a concern. The FrameApp program shown in Listing 19.1 illustrates the window concepts covered so far and shows the effect of using the basic window controls identified in the previous paragraph.

Listing 19.1. The source code of the FrameApp program. import java.awt.*;

public class FrameApp extends Frame {
 String defaultTitle;
 MenuBar defaultMenuBar;
 MenuBar alternativeMenuBar;
 int cursors[] = {CROSSHAIR_CURSOR,DEFAULT_CURSOR,E_RESIZE_CURSOR,HAND_CURSOR,
  MOVE_CURSOR,NE_RESIZE_CURSOR,NW_RESIZE_CURSOR,N_RESIZE_CURSOR,
  SE_RESIZE_CURSOR,SW_RESIZE_CURSOR,S_RESIZE_CURSOR,TEXT_CURSOR,
  WAIT_CURSOR,W_RESIZE_CURSOR};
 Color colors[] = {Color.black,Color.blue,Color.cyan,Color.darkGray,Color.gray,
  Color.green,Color.lightGray,Color.magenta,Color.orange,Color.pink,Color.red,
  Color.white,Color.yellow};
 String fontNames[] = {"Helvetica","TimesRoman","Courier","Dialog",
  "DialogInput","ZapfDingbats"};
 int cursorIndex = 1;
 int backgroundColorIndex = 0;
 int foregroundColorIndex = 0;
 int fontIndex = 0;
 public static void main(String args[]){
  FrameApp app = new FrameApp();
 }
 public FrameApp() {
  super("Exploring Frames");
  defaultTitle = getTitle();
  setup();
  pack();
  resize(400,400);
  show();
 }
 void setup() {
  setupPanels();
  setupMenuBars();
  setFont(new Font(fontNames[fontIndex],Font.PLAIN,14));
 }
 void setupPanels() {
  Panel mainPanel = new Panel();
  mainPanel.setLayout(new GridLayout(4,1));
  Label label1 =
   new Label("Change these windows characteristics:",Label.CENTER);
  mainPanel.add(label1);
  Panel panel1 = new Panel();
  panel1.add(new Button("Title"));
  panel1.add(new Button("Menu Bar"));
  panel1.add(new Button("Resizable"));
  mainPanel.add(panel1);
  Label label2 = new Label("Check out these windows options:",Label.CENTER);
  mainPanel.add(label2);
  Panel panel2 = new Panel();
  panel2.add(new Button("Cursor"));
  panel2.add(new Button("Background"));
  panel2.add(new Button("Foreground"));
  panel2.add(new Button("Font"));
  mainPanel.add(panel2);
  add("South",mainPanel);
 }
 void setupMenuBars() {
  defaultMenuBar = new MenuBar();
  Menu fileMenu = new Menu("File");
  fileMenu.add(new MenuItem("Exit"));
  defaultMenuBar.add(fileMenu);
  setMenuBar(defaultMenuBar);
  alternativeMenuBar = new MenuBar();
  Menu otherMenu = new Menu("Program");
  otherMenu.add(new MenuItem("Quit"));
  alternativeMenuBar.add(otherMenu);
 }
 public void paint(Graphics g) {
  g.drawString("Sample Text",160,100);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   System.exit(0);
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof Button){
    if("Title".equals(event.arg)){
     if(defaultTitle.equals(getTitle()))
      setTitle("Here's an alternative title.");
     else setTitle(defaultTitle);
     return true;
    }else if("Menu Bar".equals(event.arg)){
     if(defaultMenuBar.equals(getMenuBar()))
      setMenuBar(alternativeMenuBar);
     else setMenuBar(defaultMenuBar);
     return true;
    }else if("Resizable".equals(event.arg)){
     setResizable(!isResizable());
     return true;
    }else if("Cursor".equals(event.arg)){
     ++cursorIndex;
     cursorIndex %= cursors.length;
     setCursor(cursors[cursorIndex]);
     return true;
    }else if("Background".equals(event.arg)){
     ++backgroundColorIndex;
     backgroundColorIndex %= colors.length;
     setBackground(colors[backgroundColorIndex]);
     repaint();
     return true;
    }else if("Foreground".equals(event.arg)){
     ++foregroundColorIndex;
     foregroundColorIndex %= colors.length;
     setForeground(colors[foregroundColorIndex]);
     repaint();
     return true;
    }else if("Font".equals(event.arg)){
     ++fontIndex;
     fontIndex %= fontNames.length;
     setFont(new Font(fontNames[fontIndex],Font.PLAIN,14));
     repaint();
     return true;
    }
   }else if(event.target instanceof MenuItem){
    if("Exit".equals(event.arg) || "Quit".equals(event.arg)){
     System.exit(0);
     return true;
    }
   }
  }
  return false;
 }
}

After you have compiled FrameApp, run it and check out the buttons and menus it provides. When you first launch FrameApp, it displays the text Sample Text followed by two rows of buttons, as shown in Figure 19.2. The buttons in the first row provide the capability to change the window's title, menu bar, and resizable properties. These buttons are toggles-the second time you click a button, the window's characteristic being changed is reverted to its initial default value. The buttons in the second row allow you to step through a sequence of values for the window's cursor, background and foreground colors, and text font. If you are using Windows 95, you will find that it does not support all cursors defined by Java. You will find out which cursors are not supported when you analyze this program's code. Figure 19.2 : The FrameApp initial display. Let's investigate each of the program's features. Click on the Title button and you will notice that the window's title text is changed from Exploring Frames to Here's an alternative title., as shown in Figure 19.3. Click on the button a second time and the title changes back to Exploring Frames. Figure 19.3 : Here's an alternative title. Click on the File menu, but don't select the Exit option. The File menu is replaced by the Program menu, shown in Figure 19.4, when you click on the Menu Bar button. Adding, deleting, and modifying menus are common operations for many window programs. Figure 19.4 : The Program menu. You will notice that your program window is initially resizable. This means that you can use the cursor at the window's edge to make the window larger or smaller. The cursor changes from a pointer to a resizable icon to let you know that the window can be resized. If you click on the Resizable button, the window is no longer capable of being resized. You can check this by placing your cursor at the window's boundary. Click on the Cursor button to change the cursor associated with your program's window. Step through the list of available cursors until you reach the crosshair cursor. Then click the cursor button one more time to return to the default cursor. Click the Background button and the program's background color is changed to blue, as shown in Figure 19.5. You can continue to click the Background button to look at other background colors. Notice that the color does not change in the panel used by the program's buttons. Now try clicking on the Foreground button a few times to change the window's foreground color. Figure 19.5 : Changing the window background. The Font button allows you to change the font used with the sample text display as shown in Figure 19.6. Cycle through the list of fonts. The last font in the list is the ZapfDingbats font. It is used to display special characters and symbols, as shown in Figure 19.6. Figure 19.6 : ZapDingbats characters. You have covered the program's features. Now let's look at its code to see how it works. The FrameApp class defines a number of variables. The defaultTitle variable is used to store the default window title so that it can be restored after it has been changed. The defaultMenuBar and alternativeMenuBar variables are used to store the File and Program menu bars. The cursors[] array stores the cursor constants that are defined for the Frame class. These constants are stored in an array to make it easier to cycle through them. The cursorIndex variable maintains an index to the current cursor being displayed. The colors[] array stores a subset of the colors defined by the Color class. These colors are used when the background and foreground colors are changed. The backgroundColorIndex and foregroundColorIndex variables are used to keep track of the current background and foreground colors. The fontNames[] array stores the names of the fonts known to Java. The fontIndex variable is used to point to the current window font. FrameApp has a simple standard main() method that creates a new FrameApp object. The FrameApp constructor invokes the superclass constructor call statement to set the window's title and then uses the getTitle() method inherited from Frame to store the title in the defaultTitle variable. It then invokes the setup() method to set up panels, buttons, menu bars, and the default window font. The pack() method and the resize() methods are used to organize the components of the window and adjust it to the desired size. The show() method then causes the window to be opened and displayed. The setup() method invokes the setupPanels() and setupMenuBars() methods to set up the program's panels, buttons, and menu bars. It invokes the setFont() method inherited from the Component class to set the default font to be used with the window. The setFont() method takes three arguments: the name of the font, the font style, and the size of the font in points. The font names are stored in the fontNames[] array. The font style constants are defined in the Font class. The setupPanels() method constructs a Panel object named mainPanel that is to hold the subpanels corresponding to the rows of buttons. The layout of the mainPanel is set to a GridLayout object of four rows and one column. Layouts are used to specify how objects are to be placed in a container. They are covered in the "Using Layouts" section in this chapter. A new Label object is created with the text Change these windows characteristics: and added to the mainPanel. The Label.CENTER constant specifies how the label should be aligned within its available space. A Panel object named panel1 is created to hold the Title, Menu Bar, and Resizable buttons that are subsequently created and added to the panel. The panel1 object is then added to the mainPanel. A second Label object named label2 is created to hold the text Check out these windows options:. The label is then added to the mainPanel. A second Panel object named panel2 is created. The Cursor, Background, Foreground, and Font buttons are then added to panel2. The panel2 object is then added to the mainPanel. The mainPanel is then added to the southern (bottom) region of the FrameApp object being constructed. The "Using Layouts" section in this chapter describes why the FrameApp object was organized with a mainPanel and two subpanels. The setupMenuBars() method creates a new MenuBar object and assigns it to the defaultMenuBar variable. It declares the fileMenu variable and assigns it a new Menu object with the File label. It creates a MenuItem object with the Exit label and adds it to the fileMenu. The defaultMenuBar is then set as the menu bar of the FrameApp object being constructed via the setMenuBar() method of the Frame class. Menus are covered in Chapter 20, "Menus, Buttons, and Dialog Boxes." For now, just remember that a Frame has a MenuBar that consists of one or more Menus that each have one or more MenuItems. The alternativeMenuBar is constructed in the same manner as the defaultMenuBar except that it contains an otherMenu with the File and Exit labels replaced by Program and Quit labels. The alternativeMenuBar is not set to the FrameApp using the setMenuBar() method. A Frame object can have only one menu bar. The paint() method is used to initially draw the window display and then to update it as the result of window operations. It simply displays the text Sample Text at pixel location (160,100) within the window Graphics object. The handleEvent() method is the last method to be covered. It is responsible for handling all of the user-generated events and provides most of the program's functionality. The handleEvent() method handles the WINDOWS_DESTROY event by invoking the System.exit() method to terminate the program's execution. It also handles the Button and MenuItem action events. The Exit and Quit menu items also result in the program's execution being terminated. When the Title button is clicked, handleEvent() uses the getTitle() method to determine whether the current window title equals the title stored in defaultTitle. If they are equal, the window's title is set to Here's an alternative title. If the current title differs from the value stored in defaultTitle, the window's title is set using defaultTitle. When the Menu Bar button is clicked, handleEvent() uses the getMenuBar() method to check whether the window's current menu bar is the same as the value of defaultMenuBar. If they are the same, the window's menu bar is set to the value of alternativeMenuBar. If they are different, the window's menu bar is set to the value of defaultMenuBar. When the Resizable button is clicked, handleEvent() uses the isResizable() method to determine whether the window is currently resizable and then sets it to the opposite value. When the Cursor button is clicked, handleEvent() cycles the cursorIndex to the next cursor value within the cursors[] array and sets the cursor to this value using the setCursor() method. When the Background button is clicked, handleEvent() cycles the backgroundIndex variable to the next color value within the color[] array and sets the background to this value using the setBackground() method. It then invokes the repaint() method of the Component class to cause the screen to be repainted. The Foreground button is handled in a similar manner. When the Font button is clicked, handleEvent() cycles the fontIndex to the next font name and creates a new 14-point plain font of that type. It then invokes the setFont() method of the Component class to change the current font. The repaint() method is used to cause the screen to be repainted.

Using Layouts

The FrameApp program uses a number of panels and layouts to organize the way that labels and buttons are presented in the application window. The organization of any object that is a subclass of the Container class is governed by a layout. The layout determines how objects of class Component are positioned when they are added via the add() method to the Container object. Five types of layouts are provided by Java: BorderLayout, CardLayout, FlowLayout, GridLayout, and GridBagLayout. Other, custom layouts can also be defined. The BorderLayout class is the default layout used by Frame objects. An object of the Component class is added to either the North, South, East, West, or Center of the component, as shown in Figure 19.7. Figure 19.7 : A BorderLayout example. In the FrameApp program, the mainPanel was added to the South region of the window. The remainder of the program window was used by the program's default Graphics object, which was placed in the North region of the window. No Component objects were added to the East, West, or Center regions. The FlowLayout class is the default layout used for Panel objects. If a container uses a FlowLayout, the container is filled left to right from top to bottom. An example of this layout is the two rows of buttons that were added to panel1 and panel2. The FlowLayout class causes each component to be centered in its container, by default. The GridLayout class organizes a container as a grid of n rows and m columns. Each grid cell is the same size, as shown in Figure 19.8. Figure 19.8 : A Gridlayout example. The GridLayout class is used with the mainPanel in the FrameApp program. A grid of four rows and one column is used to stack panel1 and panel2 with the two labels to produce the display shown in Figure 19.2. The CardLayout class organizes a container like a deck of cards. The first component in the container is initially displayed. Other components are then displayed using the next(), previous(), first(), last(), and show() methods of the CardLayout class. The CardLayout class is illustrated in Listing 19.2. The GridBagLayout class is the most complex and flexible of the layout classes. It is similar to the GridLayout class in that it organizes its components in a grid, but it is more flexible because it allows the rows and columns to have different sizes. In addition, components are allowed to span multiple rows and columns. The positioning of each component is controlled by the use of objects of class GridBagConstraints. The GridBagConstraints objects identify the preferred size of each component and specify constraints on how they should be laid out. You should refer to the API documentation for the GridBagLayout and GridBagConstraints classes to read the detailed description of the variables and methods of these classes. The GridBagLayout class is also demonstrated in the LayoutApp program in Listing 19.2. The LayoutApp program illustrates the use of each of the five predefined Java layouts. These layouts position buttons within panels to show how the various layouts are organized and displayed. A Panel pull-down menu is used to switch to each of the layout classes.

Listing 19.2. The source code of the LayoutApp program. import java.awt.*;

public class LayoutApp extends Frame {
 MenuBar menuBar;
 Panel panels[];
 Panel currentPanel;
 static int border=0;
 static int card=1;
 static int flow=2;
 static int grid=3;
 static int gridBag=4;
 Menu cardMenu;
 public static void main(String args[]){
  LayoutApp app = new LayoutApp();
 }
 public LayoutApp() {
  super("BorderLayout");
  setup();
  pack();
  resize(400,400);
  show();
 }
 void setup() {
  setupMenuBar();
  setupPanels();
 }
 void setupMenuBar() {
  menuBar = new MenuBar();
  Menu fileMenu = new Menu("File");
  fileMenu.add(new MenuItem("Exit"));
  menuBar.add(fileMenu);
  Menu panelMenu = new Menu("Panel");
  panelMenu.add(new MenuItem("BorderLayout"));
  panelMenu.add(new MenuItem("CardLayout"));
  panelMenu.add(new MenuItem("FlowLayout"));
  panelMenu.add(new MenuItem("GridLayout"));
  panelMenu.add(new MenuItem("GridBagLayout"));
  menuBar.add(panelMenu);
  cardMenu = new Menu("Card");
  cardMenu.add(new MenuItem("First"));
  cardMenu.add(new MenuItem("Last"));
  cardMenu.add(new MenuItem("Next"));
  cardMenu.add(new MenuItem("Previous"));
  setMenuBar(menuBar);
 }
 void setupPanels() {
  panels = new Panel[5];
  for(int i=0;i<5;++i) panels[i]=new Panel();
  panels[border].setLayout(new BorderLayout());
  panels[card].setLayout(new CardLayout());
  panels[flow].setLayout(new FlowLayout());
  panels[grid].setLayout(new GridLayout(2,3));
  GridBagLayout gridBagLayout = new GridBagLayout();
  panels[gridBag].setLayout(gridBagLayout);
  panels[border].add("North",new Button("North"));
  panels[border].add("South",new Button("South"));
  panels[border].add("East",new Button("East"));
  panels[border].add("West",new Button("West"));
  panels[border].add("Center",new Button("Center"));
  String cardButtons[] = {"First","Second","Third","Fourth","Last"};
  String flowButtons[] = {"One","Two","Three","Four","Five"};
  String gridButtons[] = {"(0,0)","(1,0)","(2,0)","(0,1)","(1,1)","(2,1)"};
  for(int i=0;i<cardButtons.length;++i)
   panels[card].add(new Button(cardButtons[i]));
  for(int i=0;i<flowButtons.length;++i)
   panels[flow].add(new Button(flowButtons[i]));
  for(int i=0;i<gridButtons.length;++i)
   panels[grid].add(new Button(gridButtons[i]));
  Button gridBagButtons[] = new Button[9];
  for(int i=0;i<9;++i) gridBagButtons[i] = new Button("Button"+i);
  int gridx[] = {0,1,2,0,2,0,1,1,0};
  int gridy[] = {0,0,0,1,1,2,2,3,4};
  int gridwidth[] = {1,1,1,2,1,1,1,2,3};
  int gridheight[] = {1,1,1,1,2,2,1,1,1};
  GridBagConstraints gridBagConstraints[] = new GridBagConstraints[9];
  for(int i=0;i<9;++i) {
   gridBagConstraints[i] = new GridBagConstraints();
   gridBagConstraints[i].fill=GridBagConstraints.BOTH;
   gridBagConstraints[i].gridx=gridx[i];
   gridBagConstraints[i].gridy=gridy[i];
   gridBagConstraints[i].gridwidth=gridwidth[i];
   gridBagConstraints[i].gridheight=gridheight[i];
   gridBagLayout.setConstraints(gridBagButtons[i],gridBagConstraints[i]);
   panels[gridBag].add(gridBagButtons[i]);
  }
  add("Center",panels[border]);
  currentPanel=panels[border];
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   System.exit(0);
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof MenuItem){
    if("Exit".equals(event.arg)){
     System.exit(0);
     return true;
    }else if("BorderLayout".equals(event.arg)){
     switchPanels(panels[border],"BorderLayout",false);
     return true;
    }else if("CardLayout".equals(event.arg)){
     switchPanels(panels[card],"CardLayout",true);
     return true;
    }else if("FlowLayout".equals(event.arg)){
     switchPanels(panels[flow],"FlowLayout",false);
     return true;
    }else if("GridLayout".equals(event.arg)){
     switchPanels(panels[grid],"GridLayout",false);
     return true;
    }else if("GridBagLayout".equals(event.arg)){
     switchPanels(panels[gridBag],"GridBagLayout",false);
     return true;
    }else if("First".equals(event.arg)){
     CardLayout currentLayout=(CardLayout)currentPanel.getLayout();
     currentLayout.first(currentPanel);
     return true;
    }else if("Last".equals(event.arg)){
     CardLayout currentLayout=(CardLayout)currentPanel.getLayout();
     currentLayout.last(currentPanel);
     return true;
    }else if("Next".equals(event.arg)){
     CardLayout currentLayout=(CardLayout)currentPanel.getLayout();
     currentLayout.next(currentPanel);
     return true;
    }else if("Previous".equals(event.arg)){
     CardLayout currentLayout=(CardLayout)currentPanel.getLayout();
     currentLayout.previous(currentPanel);
     return true;
    }
   }
  }
  return false;
 }
 void switchPanels(Panel newPanel,String newTitle,boolean setCardMenu) {
  remove(currentPanel);
  currentPanel=newPanel;
  add("Center",currentPanel);
  setTitle(newTitle);
  if(setCardMenu) menuBar.add(cardMenu);
  else menuBar.remove(cardMenu);
  show();
 }
}

When you compile and run LayoutApp, the opening window should look like the one shown in Figure 19.9. Figure 19.9 : The opening window of the LayoutApp program. The opening window illustrates the use of the BorderLayout class by displaying a panel that uses the BorderLayout. Notice that the window's title is BorderLayout. The title is updated when a new layout class is displayed. To switch layouts, use the Panel pull-down menu as shown in Figure 19.10. Figure 19.10 : The Panel menu. Select the CardLayout menu item from the Panel menu. The window displays a panel that uses a CardLayout object, and the window's title is updated to identify the new layout. An additional Card pull-down menu is added to the menu bar so that the different button components in the CardLayout object can be displayed. (See Figure 19.11.) There are five buttons, labeled First, Second, Third, Fourth, and Last. Use the Next menu item of the Card menu to step through these buttons. After you have reached the button labeled Last, use the Previous menu item to step back through the list of buttons. Next, try using the First and Last menu items to go to the first and last buttons in the panel governed by the CardLayout object. Figure 19.11 : A CardLayout example. Select the FlowLayout menu item from the Panel menu. The window displays five buttons, labeled One through Five, across the top of the window as shown in Figure 19.12. The Card pull-down menu is removed and the window's title is changed to FlowLayout. Figure 19.12 : A FlowLayout example. Select the GridLayout menu item from the Panel menu. The window displays six buttons in a rectangular grid that is two rows by three columns, with each button labeled with its x,y-coordinate, as shown in Figure 19.13. The window's title is changed to GridLayout. Figure 19.13 : A GridLayout example. Select the GridBagLayout menu item from the Panel menu. The window displays nine buttons arranged in a free-form manner in a rectangular grid, five rows by three columns, as shown in Figure 19.14. The window's title is changed to GridBagLayout. Figure 19.14 : A GridBagLayout example Now that you have an idea of what the LayoutApp program does, let's see how it works. The LayoutApp class declares several variables and constants. The menuBar variable provides the program's menu bar. It is updated by different class methods when it is initially set up and displayed and when the Card menu is added and removed. The cardMenu variable is also declared. The panels[] array consists of five panels that illustrate the five predefined Java layouts. It is indexed by the border, card, flow, grid, and gridBag constants. The currentPanel variable is used to keep track of the current panel being displayed. The main() function should seem to be pretty standard to you by now. It is typical of the main() function found in most window programs. The LayoutApp class invokes the superclass constructor with the BorderLayout title. BorderLayout is the layout of the first panel to be displayed and is the default layout for Frame objects. The setup() method is invoked to set up the menu bar and panels used in the program. The rest of the LayoutApp constructor is fairly standard. The pack(), resize(), and show() methods are used to organize, resize, open, and display the LayoutApp main window. The setupMenuBar() method creates a new menu bar and assigns it to the menuBar variable. A File menu is created with an Exit menu item and is then added to the object referenced by menuBar. A Panel menu is created and added to the menuBar in a similar fashion. The Panel menu is given buttons that identify each of the five layouts. A third Card menu is also created, but is not added to the menuBar. It is given the First, Last, Next, and Previous menu items. Finally, the object referenced by menuBar is set as the menu bar by invoking the setMenuBar() method of the Frame class. The setupPanels() method is the most complicated method in this program. It is where all of the five different panels are created and laid out. First, the panels[] array is allocated and then five new Panel objects are assigned to the array's elements. Then each of the panels is laid out. The panels[border] array element has its layout set to a BorderLayout object using the setLayout() method of the Container class. The panels[card] array element has its layout set to a CardLayout object. The panels[flow] array element has its layout set to a FlowLayout object. The panels[grid] array element has its layout set to a GridLayout object, two rows by three columns. The layout of the panels[gridBag] array element is separately created and assigned to the gridBagLayout variable. It is used later on when the panel is laid out with objects of class GridBagConstraints. After each of the panels has had its layout set, buttons are added to illustrate how the individual layouts are displayed. The panels[border] panel is given five buttons, labeled North, South, East, West, and Center. These buttons are added to the positions identified by their labels. The buttons for the panels[card], panels[flow], and panels[grid] panels are added by first creating an array of labels for each panel and then using a for statement to step through the arrays, creating buttons and adding them to their respective panels. This approach greatly simplifies the process of creating buttons and adding them to panels. The buttons for the panels[gridBag] panel are created a little differently by iterating from 0 through 8 and appending each number to the end of the "Button" string. The gridx[], gridy[], gridwidth[], and gridheight[] arrays are used to produce the objects of class GridBagConstraints that are used to lay out the buttons in the panels[gridBag] panel. The gridx[] array identifies the x-coordinate of each button within the 5-by-3 grid. The gridy[] array identifies they-coordinate. The gridwidth[] array identifies button widths in terms of number of grid cells. The gridheight[] array identifies the height of each button in grid cells. An array of GridBagConstraint objects is created and assigned to the gridBagConstraints variable. Each of the array's elements is then created and initialized using the gridx[], gridy[], gridwidth[], and gridheight[] arrays. The fill variable of each array element is set to the GridBagConstraints.BOTH constant, indicating that the buttons may grow both vertically and horizontally to fill the space assigned to them as the result of the way they are laid out. The setConstraints() method is used to associate each button with its gridBagConstraints[] element within the GridBagLayout object referenced by gridBagLayout. The last line in the for statement adds each button to the panels[gridBag] panel. Finally, the panels[border] panel is assigned as the initial panel to be displayed when the program starts up. The currentPanel variable is also initialized to panels[border]. The handleEvent() method handles the processing of user menu selections. The WINDOWS_DESTROY event and the Exit menu option result in immediate program termination. The menu items associated with each layout result in the appropriate panel being selected and displayed. This is accomplished using the switchPanels() method. The First, Last, Next, and Previous menu items are processed by getting the layout associated with the current panel and then invoking the first(), last(), next(), or previous() methods of the CardLayout class to select the appropriate button to be displayed within the panels[card] panel. The switchPanels() method is used to provide a common method of switching from one panel to another. It begins by using the remove() method of the Container class to remove the current panel from the LayoutApp main window. The current panel is set based on the Panel object passed to the method via the newPanel parameter. The new current panel is then added to the center of the main window, and the window's title is set based on the newTitle string parameter. The cardMenu is added or removed from the menu bar based on the boolean setCardMenu parameter. Finally, the new panel is displayed by invoking the show() method of the Window class.

Connecting Code to Events

Although the various graphical user interface subclasses of the Component class are what is seen by the user when he interacts with a window program, the event-handling software is what actually connects these components to the code that implements the program's behavior. Events are generated as the result of the user's interaction with the program's GUI components. These events are defined in the Event class and cover a large variety of user actions using the mouse and keyboard. Until now you've been handling events using monolithic handleEvent() methods. Although this approach works, it can sometimes become difficult to manage. An alternative approach is to handle events locally by each GUI component, which allows event handling to be more closely aligned with the components generating the event. Local component event handling is performed using the handleEvent(), action(), and other methods, such as mouseUp() and mouseDown(), that are inherited from the Component class. Consult the Component class API for a detailed list of these methods. In order to perform local event handling you must subclass these components (for example, Button, Checkbox, Scrollbar) and override their event-handling methods. The handleEvent() and action() methods are the most common methods used for event handling. You've already used the handleEvent() method quite extensively. The action() method is used to handle events that have the ACTION_EVENT type. These events are typical of GUI components such as menu items, buttons, lists, and checkboxes. The action() method is passed the event along with an object containing the value of the event's arg variable. This object describes the Component being acted upon. For example, clicking a button results in the button's label being assigned to the arg variable. The window sampler program presented in the next section shows how the action() method is used.

The Window Sampler Program

The SamplerApp program serves a dual purpose. It shows how each of the most common GUI components are displayed and accessed and also demonstrates local event-handling approaches for each of these components. The program displays Label, TextField, TextArea, Button, Canvas, Checkbox, Choice, List, and Scrollbar objects and shows how to handle events associated with these objects. The program's source code is shown in Listing 19.3.

Listing 19.3. The source code of the SamplerApp program. import java.awt.*;
import java.lang.System;

public class SamplerApp extends Frame {
 TextArea textArea;
 public static void main(String args[]){
  SamplerApp app = new SamplerApp();
 }
 public SamplerApp() {
  super("Windows Sampler");
  setup();
  pack();
  resize(400,400);
  show();
 }
 void setup() {
  setupMenuBars();
  setupPanels();
 }
 void setupMenuBars() {
  MenuBar menuBar = new MenuBar();
  Menu fileMenu = new Menu("File");
  fileMenu.add(new MenuItem("Exit"));
  menuBar.add(fileMenu);
  setMenuBar(menuBar);
 }
 void setupPanels() {
  Panel mainPanel = new Panel();
  mainPanel.setLayout(new GridLayout(3,3));
  Panel panels[][] = new Panel[3][3];
  for(int i=0;i<3;++i){
   for(int j=0;j<3;++j){
    panels[j][i] = new Panel();
    panels[j][i].setLayout(new FlowLayout(FlowLayout.LEFT));
   }
  }
  panels[0][0].add(new Label("Text Field:"));
  panels[0][0].add(new MyTextField("A text field.",15));
  panels[1][0].add(new Label("Text Area:"));
  textArea = new TextArea("A text area.",5,15);
  panels[1][0].add(textArea);
  panels[2][0].add(new Label("Button:"));
  panels[2][0].add(new MyButton("Blank Text Area",textArea));
  panels[0][1].add(new Label("Canvas:"));
  panels[0][1].add(new MyCanvas());
  String checkboxStrings[] = {"Checkboxes:","Java","Developer's","Guide"};
  panels[1][1].add(new MyCheckboxGroup(checkboxStrings));
  panels[2][1].add(new Label("Choices:"));
  String choiceStrings[] = {"Yes","No","Maybe"};
  panels[2][1].add(new MyChoice(choiceStrings,textArea));
  panels[0][2].add(new Label("List:"));
  String listStrings[] = {"Sleepy","Sneezy","Grumpy","Dopey","Doc",
   "Happy","Bashful"};
  panels[0][2].add(new MyList(listStrings,textArea));
  panels[1][2].setLayout(new BorderLayout());
  panels[1][2].add("Center",new Label("Horizontal Scrollbar:"));
  panels[1][2].add("South",new MyScrollbar(Scrollbar.HORIZONTAL,50,10,0,
   100,textArea));
  panels[2][2].setLayout(new BorderLayout());
  panels[2][2].add("North",new Label("Vertical Scrollbar:"));
  panels[2][2].add("East",new MyScrollbar(Scrollbar.VERTICAL,50,10,0,
   1000,textArea));
  for(int i=0;i<3;++i)
   for(int j=0;j<3;++j)
    mainPanel.add(panels[j][i]);
  add("Center",mainPanel);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   System.exit(0);
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof MenuItem){
    if("Exit".equals(event.arg)){
     System.exit(0);
     return true;
    }
   }
  }
  return false;
 }
}
class MyTextField extends TextField {
 public MyTextField(String text,int columns) {
  super(text,columns);
 }
 public boolean action(Event event,Object arg) {
  String text = getText();
  setText(text.toUpperCase());
  return true;
 }
}
class MyButton extends Button {
 TextArea textArea;
 public MyButton(String text,TextArea newTextArea) {
  super(text);
  textArea = newTextArea;
 }
 public boolean action(Event event,Object arg) {
  textArea.setText("");
  return true;
 }
}
class MyCanvas extends Canvas {
 int x = -1;
 int y = -1;
 int boxSize = 10;
 public MyCanvas() {
  super();
  resize(75,75);
  setBackground(Color.white);
  setForeground(Color.red);
  show();
 }
 public boolean mouseDown(Event event,int xClick,int yClick) {
  x = xClick;
  y = yClick;
  repaint();
  return true;
 }
 public void paint(Graphics g) {
  setBackground(Color.white);
  setForeground(Color.red);
  if(x>=0 && y>=0) g.fillRect(x,y,boxSize,boxSize);
 }
}
class MyCheckboxGroup extends Panel {
 String labelString;
 String checkboxLabels[];
 Checkbox checkboxes[];
 int numBoxes;
 TextField results;
 public MyCheckboxGroup(String strings[]) {
  super();
  labelString = strings[0];
  numBoxes = strings.length-1;
  checkboxLabels = new String[numBoxes];
  for(int i=0;i<numBoxes;++i)
   checkboxLabels[i] = strings[i+1];
  results = new TextField("",15);
  setupPanel();
  show();
 }
 void setupPanel() {
  setLayout(new GridLayout(numBoxes+2,1));
  add(new Label(labelString));
  checkboxes = new Checkbox[numBoxes];
  for(int i=0;i<numBoxes;++i){
   checkboxes[i] = new Checkbox(checkboxLabels[i]);
   add(checkboxes[i]);
  }
  add(results);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof Checkbox){
    String newResults = "";
    for(int i=0;i<numBoxes;++i)
     if(checkboxes[i].getState())
      newResults = newResults + " " +checkboxes[i].getLabel();
    results.setText(newResults);
   }
  }
  return false;
 }
}
class MyChoice extends Choice {
 TextArea text;
 public MyChoice(String strings[],TextArea textArea) {
  super();
  try {
   for(int i=0;i<strings.length;++i)
    addItem(strings[i]);
   text = textArea;
  }catch(NullPointerException ex){
   System.exit(0);
  }
 }
 public boolean action(Event event,Object arg) {
  text.setText((String)arg);
  return true;
 }
}
class MyList extends List {
 TextArea text;
 public MyList(String strings[],TextArea textArea) {
  super(3,false);
  for(int i=0;i<strings.length;++i)
   addItem(strings[i]);
  text = textArea;
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.ACTION_EVENT){
   text.setText("Double-clicked:\n "+event.arg.toString());
   return true;
  }else if(event.id==Event.LIST_SELECT){
   text.setText("Selected:\n "+
    getItem((new Integer(event.arg.toString())).intValue()));
   return true;
  }else if(event.id==Event.LIST_DESELECT){
   text.setText("Deselected:\n "+
    getItem((new Integer(event.arg.toString())).intValue()));
   return true;
  }
  return false;
 }
}
class MyScrollbar extends Scrollbar {
 TextArea text;
 public MyScrollbar(int orientation,int value,int visible,int min,int max,
  TextArea textArea) {
  super(orientation,value,visible,min,max);
  text=textArea;
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.SCROLL_LINE_UP){
   text.setText("Position: "+getValue());
   return true;
  }else if(event.id==Event.SCROLL_LINE_DOWN){
   text.setText("Position: "+getValue());
   return true;
  }else if(event.id==Event.SCROLL_PAGE_UP){
   text.setText("Position: "+getValue());
   return true;
  }else if(event.id==Event.SCROLL_PAGE_DOWN){
   text.setText("Position: "+getValue());
   return true;
  }else if(event.id==Event.SCROLL_ABSOLUTE){
   text.setText("Position: "+getValue());
   return true;
  }
  return false;
 }
}

After compiling and running the program, the main application window should be displayed, as shown in Figure 19.15. The program presents a number of GUI components at various locations within the window. Labels are used to identify these components. Figure 19.15 : The SamplerApp program display. The TextField object presented in the upper left corner of the window allows a user to type some text. When the user presses the Enter key from within the text field, an ACTION_EVENT is generated and the text is automatically converted to uppercase. The TextArea object allows the user to type in and edit text in a multiline text field. This object is used in the SamplerApp program to display the results of operations on other objects such as the Blank Text Area button, the choices list, the scrollable list, and the horizontal and vertical scrollbars. The Blank Text Area button causes all text displayed in the TextArea object to be erased. The Canvas object displays a red square at the point where the user clicks within the canvas. When the Checkbox objects are selected, they display the text of their labels in the underlying text field. The value selected from the Choice and List objects is displayed in the TextArea object. The horizontal and vertical scrollbars also display their scrollbar positions in the TextArea object. Play around with each of the GUI components to familiarize yourself with their operation before moving on to analyze the SamplerApp source code. The SamplerApp class has one field variable-the textArea variable that is used to hold the TextArea object written to by several GUI components. The main() method and SamplerApp constructor are defined in the standard manner. The setupMenuBars(), setupPanels(), and handleEvent() methods provide the primary processing for this class, with the bulk of the processing being performed in setupPanels(). The setupMenuBars() method creates a File menu with an Exit menu item. The handleEvent() method handles the WINDOW_DESTROY event and the Exit menu item. All other event handling is performed locally by the window components. The setupPanels() method creates a Panel with a 3-by-3 GridLayout and assigns it to mainPanel. It then declares and initializes a 3-by-3 panels[][] array to hold the nine subpanels of the mainPanel. The layout of elements of the panels[][] array is set to a left-justified FlowLayout. Subsequent code adds GUI components to each of the panels[][] elements. A TextField: label and an object of MyTextField are added to panels[0][0]. The MyTextField object is assigned a default value of "A text field." and is set to 15 columns. A TextArea: label and a 5-row by 15-column TextArea object with the default text of "A text area." are added to panels[1][0]. A Button: label and a MyButton object are added to panels[2][0]. The MyButton object is given the Blank Text Area label and is passed the name of a TextArea to be updated. A Canvas: label and a MyCanvas object are added to panels[0][1]. An array of strings is created and passed to the MyCheckBoxGroup() constructor. The resulting MyCheckBoxGroup object is added to panels[1][1]. A Choices: label is added to panels[2][1]. A MyChoice object is created using the choiceStrings[] array and the textArea variable. The object is also added to panels[2][1]. A List: label is added to panels[0][2]. A MyList object is created using the names of the Seven Dwarfs and the textArea variable and is added to panels[1][2]. The layouts for panels[1][2] and panels[2][2] are changed to a BorderLayout object. A Horizontal Scrollbar: label is added to the center of panels[1][2], and a MyScrollBar object is created and added to the South region of panels[1][2]. A Vertical Scrollbar: label is added to the North region of panels[2][2], and a MyScrollBar object is created and added to the East region of panels[2][2]. After the components of all the panels have been created and added to their respective panels, each of the elements of the panels[][] array is added to the mainPanel object. The mainPanel is then added to the center of the SamplerApp window. After the SamplerApp class is declared, seven new classes are declared that subclass the standard GUI components and provide custom display and event handling.

MyTextField

The TextField class provides the capability for the user to enter and edit a single line of text. The MyTextField class extends the TextField class and handles the event generated when the user presses the Enter key while editing within the text field. The MyTextField() constructor passes the text and columns parameters to the TextField constructor via the superclass constructor call statement. The text parameter identifies a string of text that is to be initially displayed within the text field. The columns parameter specifies the displayed width of the text field in character columns. The action() method handles the event occurring when the user presses the Enter key while editing the text field. It uses the getText() method inherited from the TextComponent class to retrieve the current text displayed within the text field, converts it to uppercase, and then sets the converted text in the text field using the setText() method inherited from TextComponent. The TextComponent class is the parent of both TextField and TextArea.

MyButton

The MyButton class extends the Button class. Its constructor takes two parameters: the string label to be displayed on the button and a reference to the TextArea object that is to be cleared when the button is clicked. The action() method handles the button click and uses the setText() method of TextComponent to set the text of the TextArea object to an empty string.

MyCanvas

The Canvas class provides the capability to add individual drawing components to a container. It is covered extensively in Chapter 23, "The Canvas." The MyCanvas class extends Canvas and provides a minimal drawing capability. It declares the x and y variables to record the last position in which the mouse is clicked within the canvas. Their default values are set to -1 to indicate that the mouse has not yet been clicked on the canvas. The boxSize variable specifies the size of the box to be displayed at the position of the last mouse click. The MyCanvas constructor sets the canvas size to an area of 75¥75 pixels with a white background and a red foreground drawing color. The show() method is used to cause the canvas to be initially displayed. The mouseDown() method overrides the method defined in the Component class. It provides the capability to handle the MOUSE_DOWN event generated when the user clicks a mouse button. Java assumes a one-button mouse to provide the widest compatibility. The mouseDown() method stores the position of the user's click and then invokes the repaint() method to repaint the canvas. The paint() method declared for MyCanvas checks to make sure that a click has occurred and fills a rectangle with the upper left corner at the point of the last click and with boxSize dimensions.

MyCheckBoxGroup

The MyCheckBoxGroup class extends the Panel class. It implements a custom panel consisting of a label, an array of Checkbox objects, and a TextField object. The results of clicking on any of the checkboxes are displayed in the results TextField. The MyCheckBoxGroup constructor takes an array of strings as its parameter. It sets the label string to the first string in the array and sets the labels of the checkboxes to the rest of the strings. The numBoxes variable specifies the number of Checkbox objects to be created. The results TextField is created as an empty 15-character TextField object. The setupPanel() method sets the layout of the panel to a GridLayout that is one column wide and has enough rows to accommodate the label, checkboxes, and text field. The label is created and added to the panel, followed by each of the checkboxes. The results TextField is then added to the end of the panel. The MyCheckBoxGroup handleEvent() method is used to handle any actions occurring within the panel and is capable of handling all events for all checkboxes. It handles checkbox events by using the getState() and getLabel() methods of the Checkbox class to query the checked status of each checkbox and to retrieve the labels associated with the checkboxes that are checked. It then displays these labels in the results TextField using the setText() method.

MyChoice

The Choice class is used to implement Motif option menus, which are free-standing pull-down menus that can be used to select a single value from a list. The MyChoice class extends the Choice class and provides the capability to display selected choices in a TextArea object. The MyChoice constructor takes an array of strings to be used as the choices and a TextArea object as its parameters. The addItem() method of the Choice class is used to add the choice strings to the list of choices. The constructor checks for a NullPointerException when the strings are added to the Choice list because the addItem() method throws this exception. The action() method handles the event generated when a user makes a selection from the list. The arg parameter contains the label of the selected choice. It is displayed in the TextArea object using the setText() method.

MyList

The List class implements scrollable lists from which a user can select one or more list items. The MyList class extends the List class and provides support for displaying the selected list items in a TextArea field. The MyList constructor takes an array of strings and the TextArea object as its parameters. The strings are used as the items of the list. The super(3,false) superclass constructor call statement invokes the List class constructor and specifies a three-row list with multiple list selections being disabled. Lists are covered in more detail in Chapter 21, "Checkboxes, Choices, and Lists." The addItem() method of the List class is used to add the strings as items of the list. The handleEvent() method handles the ACTION_EVENT occurring when a list item is double-clicked, the LIST_SELECT event that is generated when an unselected list item is clicked, and the LIST_DESELECT event that occurs when a selected list item is clicked and deselected. The Windows 95 implementation of Java does not correctly handle the LIST_DESELECT event. The handleEvent() method handles these events by writing the results of the action in the specified TextArea object.

MyScrollbar

The Scrollbar class encapsulates vertical and horizontal scrollbars. The MyScrollbar class extends Scrollbar and provides the capability to display the results of scrollbar operations using a TextArea object. (See Chapter 24, "Scrollbars," for more information on using scrollbars.) The MyScrollbar constructor takes a number of parameters that determine the characteristics of a scrollbar. These parameters are forwarded to the superclass constructor. A TextArea object is also passed as a parameter. The orientation parameter is set to the HORIZONTAL and VERTICAL constants of the Scrollbar class. These constants specify whether the scrollbar should be displayed horizontally or vertically. The min and max parameters specify a range of integer values that are associated with the scrollbar. The value parameter sets the initial position of the scrollbar between the min and max values. The visible parameter identifies the size of the visible portion of the scrollable area. This determines how the current scrollbar position is updated as the result of a page-up or page-down scrollbar operation. The handleEvent() method of the MyScrollbar class handles the SCROLL_LINE_UP, SCROLL_LINE_DOWN, SCROLL_PAGE_UP, SCROLL_PAGE_DOWN, and SCROLL_ABSOLUTE events. The SCROLL_LINE_UP and SCROLL_LINE_DOWN events are generated when the user clicks on the end arrows of the scrollbar. The SCROLL_PAGE_UP and SCROLL_PAGE_DOWN events are generated when the user clicks between the end arrows and the scrollbar position marker. The SCROLL_ABSOLUTE event is generated when the user moves the scrollbar position marker. The handleEvent() method handles these events by displaying the current scrollbar position in the designated TextArea object. The getValue() method of the Scrollbar class is used to obtain the current scrollbar position.















Summary

This chapter covers the basics of writing window programs. It shows how window programs are structured and organized and identifies the basic approach used to design most window programs. It covers the details of the Frame class and the five basic window layouts. The process of window event handling is described and illustrated through the SamplerApp program. SamplerApp also introduces the most common window GUI controls. Subsequent chapters investigate these components in more detail.





Chapter 18 ---Opening Windows

 

Opening Windows



CONTENTS


This chapter is the first of eight chapters that cover window programming. It introduces you to Java windows by way of several examples that illustrate different aspects of the classes and methods used to implement simple window applications. Its purpose is to quickly get you up to speed developing window-based programs. Subsequent chapters will fill in the details of the classes and methods that are introduced here. You should approach this chapter by trying to get a good general understanding of what is going on in the sample programs without dwelling on the details. You will learn the details when you study them in later chapters.

Hello Windows!

The first program you wrote in Chapter 4, "First Programs: Hello World! to BlackJack," is the HelloWorldApp program. It displays the traditional Hello World! text to the console window. The main purpose of the program was to show you how to develop a simple Java program that actually produced some noticeable effect. The same rationale applies to the HelloWindowsApp program that you'll develop shortly. The program shows you how to open an application window and write the text Hello Windows! to the window. The code for the HelloWindowsApp program is shown in Listing 18.1.
Listing 18.1. The source code of the HelloWindowsApp program. import java.awt.Frame;
import java.awt.Event;
import java.awt.Graphics;

public class HelloWindowsApp extends Frame {
 public static void main(String args[]){
  HelloWindowsApp app = new HelloWindowsApp();
 }
 public HelloWindowsApp() {
  super("Hello Windows!");
  pack();
  resize(200,200);
  show();
 }
 public void paint(Graphics g) {
  g.drawString("Hello Windows!",50,90);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   System.exit(0);
   return true;
  }else return false;
 }
}

When you compile and run the program it opens a small window in the upper-left corner of your desktop and displays the text Hello Windows! in the middle of the window. This program is no giant feat for mankind, but it's a large step for you. It marks your transition from console to window-based programs. Up to this point in the book, console programs served admirably in helping cover the different aspects of the Java language and many of the classes of the API. However, window-based programs and applets (which you learn about in Part VI, "Programming the Web with Applets and Scripts") are the primary areas of interest for most Java programmers. Figure 18.1 shows the window displayed by the HelloWindowsApp program. Figure 18.1 :The HelloWindowsApp program display. Let's take a look at HelloWindowsApp and find out what makes it work. You probably noticed right off the bat that we are now importing classes from the java.awt package. The Frame, Event, and Graphics classes shown in the import statements are classes that are fundamental to developing window programs. The Frame class is used to create Frame objects that implement application main windows. The Event class is used to process user-generated interface events, such as mouse clicks and keyboard input, and the Graphics class is used to update the screen display. The HelloWindowsApp class extends the Frame class. This is a typical approach to developing window programs. By subclassing Frame, your application class implements a main application window. You still use the same old main() method for implementing the entry point to your program. In HelloWindowsApp, the main() method simply creates a default object of class HelloWindowsApp. The HelloWindowsApp constructor uses the super() constructor call statement to invoke the Frame constructor with the string "Hello Windows!". The Frame constructor creates a new application window frame with the specified text as its title. The pack() method is used to pack the components of the window and set the window's size based upon the size of these components. It is inherited from the Window class, which is the superclass of Frame. The resize() method then resizes the window to a 200¥200 pixel dimension. The resize() method is inherited from the Component class by way of the Container, Window, and Frame classes. Finally, the show() method is used to cause the window to be displayed. It is inherited from the Window class. You might be wondering how the "Hello Windows!" text is actually displayed because there is no call from main() or HelloWindowsApp() to the paint() method. When a window or any other object that is in a subclass of Component is initially displayed or redisplayed as the result of the window being uncovered or brought to the foreground, the paint() method is invoked. It then paints the window according to the current application state. The paint() method used by HelloWindowsApp overrides the paint() method inherited from the Component class. It takes a Graphics object as a parameter. The Graphics class provides numerous easy-to-use methods for drawing on Graphics objects. The paint() method uses the drawString() method to display the text "Hello Windows!" at the screen coordinate 50,90 within the application window. Window coordinates are organized with the upper-left corner of the window being 0,0. The coordinates of the upper-right corner of the window are width,0, where width is the horizontal width of the window in pixels. The coordinates of the lower-left corner of the window are 0,height, where height is the vertical height of the window in pixels. Finally, the coordinates of the lower-right corner of the window are width,height. Figure 18.2 illustrates the window coordinate system. Figure 18.2 :Window coordinates. You probably have been wondering what the handleEvent() method is doing in the HelloWindowsApp class. The handleEvent() method provides a standard approach to event handling in Java applications. Events represent actions that occur during the course of program execution. Most events are generated as the result of user actions such as mouse clicks and keyboard actions. The handleEvent() method overrides the method inherited from the Component class. It takes an object of the Event class as a parameter and uses the methods and constants of the Event class to determine what event was passed to it and how it should be handled. The handleEvent() method returns a boolean value indicating whether or not the event has been handled. If a false value is returned, the event is propagated to the handleEvent() method of the parent of the class that is currently handling the event. If a true value is returned, no further event propagation takes place. The handleEvent() method of HelloWindowsApp checks the id variable of the event to see if it equals the constant Event.WINDOWS_DESTROY. The id variable and the WINDOWS_DESTROY constant both are defined in the API of the Event class. The id variable is used to identify what type of event occurred, and the Event class constants specify the event types. In this example, the event handler only handles the WINDOW_DESTROY event. This event occurs when the user closes the main application window by clicking the Close Window icon, as shown in Figure 18.3. Figure 18.3 :Terminating the HelloWindowsApp program. The WINDOW_DESTROY event is handled by invoking the exit() method of the System class to terminate the program. The true value is returned to indicate that the event was handled. You might be wondering what would happen if the event handler did not handle the WINDOW_DESTROY event. Try deleting the handleEvent() method and recompiling and rerunning HelloWindowsApp to see what happens when you try to terminate the application.

Going Round in Ovals: A Graphics Program

The HelloWindowsApp program provided a simple introduction to window programming. It illustrated many of the basics of writing a window program, but it didn't actually do all that much. The OvalApp program is also an introductory window program. It introduces more window programming classes and methods such as panels, buttons, layouts, additional event handling, and, of course, oval drawing. The source code of OvalApp is shown in Listing 18.2.
Listing 18.2. The source code of the OvalApp program. import java.awt.*;
import java.util.Random;

public class OvalApp extends Frame {
 int screenWidth = 400;
 int screenHeight = 400;
 Oval oval;
 public static void main(String args[]){
  OvalApp app = new OvalApp();
 }
 public OvalApp() {
  super("Let's Draw Ovals!");
  Panel buttons = new Panel();
  buttons.add(new Button("Next"));
  buttons.add(new Button("Quit"));
  add("South",buttons);
  oval = new Oval(screenWidth,screenHeight);
  pack();
  resize(screenWidth,screenHeight);
  show();
 }
 public void paint(Graphics g) {
  oval.paint(g);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   System.exit(0);
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof Button){
    if("Next".equals(event.arg)){
     oval.update();
     repaint();
     return true;
    }else if("Quit".equals(event.arg)){
     System.exit(0);
     return true;
    }
   }
  }
  return false;
 }
}

class Oval {
 int x, y, width, height, maxWidth, maxHeight;
 Color color;
 static Random r = new Random();
 public Oval(int w,int h) {
  super();
  maxWidth = w;
  maxHeight = h;
  update();
 }
 public void update() {
  x = Math.abs(r.nextInt() % (maxWidth-100));
  y = Math.abs(r.nextInt() % (maxHeight-100));
  width = (maxWidth - x)/3;
  height = (maxHeight - y)/3;
  int rgb[] = new int[3];
  for(int i=0;i<3;++i) rgb[i]=Math.abs(r.nextInt()%256);
  color = new Color(rgb[0],rgb[1],rgb[2]);
 }
 public void paint(Graphics g) {
  g.setColor(color);
  g.fillOval(x,y,width,height);
 }
}

When you run the OvalApp program it displays a window with the title Let's Draw Ovals!. A colored oval is displayed somewhere in the application window, as shown in Figure 18.4. Figure 18.4 : The OvalApp startup display The window has two buttons, labeled Next and Quit. When you click on the Next button, as shown in Figure 18.5, a different oval is displayed. Figure 18.5 : The Next button. You can continue to click on the Next button to cause different colored ovals to be displayed in different parts of the window, as shown in Figure 18.6. Figure 18.6 : A new oval is displayed. When you have thoroughly amused yourself by drawing ovals, you can click on the Quit button, as shown in Figure 18.7, and terminate the program's execution. Figure 18.7 : The Quit button. The OvalApp program is cute and entertaining, but doesn't perform any useful processing. However, it does provide a good example of some new window programming constructs. The program begins by doing an import of the relevant classes of the java.awt package. The program uses a large number of java.awt classes and, rather than listing each one individually, uses the more general package import statement to eliminate the trouble of typing each one in individually. The only other class that is included is the Random class of the java.util package. This class provides the capability to generate random numbers to be used in the oval drawing. The OvalApp class extends Frame in the same manner as the HelloWindowsApp. Three variables are defined for the OvalApp class. The screenWidth and screenHeight variables define the dimensions of the main window. The oval variable is used to refer to the current Oval object being displayed. The main() method simply creates a new object of class OvalApp. The OvalApp constructor sets the window title using the superclass constructor call statement, super(). It then creates a Panel object called buttons. Panels are window objects that are used as containers for other objects. They help to organize the way GUI controls are placed in windows. The buttons panel will be used to organize the Next and Quit buttons. The add() method is used with buttons to add two new objects of class Button to the panel. The Button constructor takes a string argument that is the label for the buttons. The add() method is then invoked for the OvalApp object being constructed to add the panel to the OvalApp application window. The OvalApp add() method is inherited from the Container class. The "South" string identifies where the panel should be added in the application window. The organization of objects within a window container is governed by the layout of the container. (Layouts are described in detail in Chapter 19, "Organizing Window Programs.") The default layout for Frame objects and their subclasses is BorderLayout. This type of layout organizes objects within a container by their North, South, East, and West borders. Objects can also be placed in the Center of a container. The add("South",buttons); method invocation adds the buttons panel to the southern (bottom) border of the OvalApp window. The buttons panel is also associated with a layout. Panels use a FlowLayout object, by default. The FlowLayout class organizes objects in a container in a left-to-right fashion, fitting as many objects as possible in a row before moving on to fill the next row. The oval variable is assigned a new object of class Oval that is parameterized by the screenWidth and screenHeight dimensions. These arguments are passed to the Oval constructor to make sure that an Oval object is created that is appropriate for the screen size of the OvalApp window. The Oval class is discussed after the OvalApp class's description is completed. After adding the buttons panel to the window and creating an Oval object, the pack() method is invoked to organize the components of the window and to determine the minimum preferred size of the window. The resize() method then adjusts the screen to the specified width and height dimensions. Finally, the show() method causes the window to be displayed. The paint() method for the OvalApp class simply passes the task of drawing the window to the Oval class. Notice that it passes on the Graphics object that it receives to the Oval paint() method. Without the Graphics object, Oval's paint() method would have nothing to draw on. The handleEvent() method for OvalApp doesn't fare as well as the paint() method. Instead of handing off its processing to other classes, it provides a central point for all program event handling. It processes the WINDOWS_DESTROY event in the same manner as the HelloWindowsApp program. It also checks for events of type ACTION_EVENT. Events of this type are typically generated by performing actions on GUI controls-for example, clicking on a button, selecting a menu item, or checking a checkbox. In this case, the method is checking whether the Next or Quit button was clicked. It does this by checking the target variable of the event to see if it is an instance of class Button. The target variable identifies the object that is the target of the event being processed. The event.arg variable is event specific. It typically provides a value that is used to identify the specific object from its class for which the event was generated. In the case of Button objects, it represents the label of the button. The arg variable is used to determine whether the button being clicked on is the Next button or the Quit button. When the Next button is clicked, the button-clicked event is handled by invoking the update() method of the oval variable and repainting the screen. The update() method causes the oval's color, position, and size parameters to be changed. The repaint() method results in the paint() method being reinvoked to redraw an object-in this case, the OvalApp window. You should never invoke paint() directly-it is under the control of the native windows implementation. Always access it by invoking repaint(). When the Quit button is clicked, the OvalApp program terminates in the same manner as when the WINDOW_DESTROY event occurs. The Oval class is used to randomly generate ovals of different colors, sizes, and positions and display them in the OvalApp window. It defines a number of variables that specify these parameters. The maxWidth and maxHeight variables store the dimensions of the application window. The width and height parameters store the actual width and height of an Oval object. The x and y parameters identify its position within the application window. The color parameter identifies the color in which the oval is to be drawn. The static r variable is an object of class Random that is used to generate random numbers that determine the oval's characteristics. The Oval constructor explicitly calls the default constructor call statement, sets the maxWidth and maxHeight variables, and invokes its update() method to randomly generate the values of the rest of the variables defined by the Oval class. The update() method sets the upper-left corner of the oval to a random value between (0,0) and (maxWidth=100,maxHeight=100). This keeps the oval from scrunching up against the window's borders. The width and height of the rectangle are set to one-third of the distance between the upper-left corner of the rectangle and the lower-right corner of the application window. The red, blue, and green color intensities of the rectangle are randomly generated as values between 0 and 255. In order for the full range of color values to be displayed, your screen must support 24-bit color in its current screen resolution; otherwise, the randomly generated color will be approximated by the next closest color supported by your video card. I rarely set my screen to 24-bit color and usually use 8-bit color to cut down on my video-display memory requirements. The paint() method of the Oval class uses the Graphics object passed to it by the OvalApp paint() method to display the oval. It sets the current color of the Graphics object based on the randomly generated color stored in the color variable. It then invokes the fillOval() method of the Graphics class to draw an oval that is filled with the current color. The fillOval() method takes the upper-left corner of the Oval and its width and height dimensions as parameters.

A Text Editor

Now let's extend your exploration of window applications by developing a primitive text editor. The TextEditApp program illustrates more window programming constructs. It introduces menus, dialog boxes, fonts, text processing, and window-based file I/O. It also builds on the event-handling skills that you've developed so far. The source code of the TextEditApp program is shown in Listing 18.3.
Listing 18.3. The source code of the TextEditApp program. import java.awt.*;
import java.io.*;

public class TextEditApp extends Frame {
 TextArea text;
 MenuBar menuBar;
 FileDialog openFile;
 FileDialog saveFile;
 public static void main(String args[]){
  TextEditApp app = new TextEditApp();
 }
 public TextEditApp() {
  super("Text Editor");
  setup();
  resize(text.minimumSize());
  pack();
  show();
 }
 void setup() {
  setFont(new Font("System",Font.PLAIN,12));
  setBackground(Color.white);
  text = new TextArea(25,80);
  add("Center",text);
  setupMenuBar();
  openFile = new FileDialog(this,"Open File",FileDialog.LOAD);
  saveFile = new FileDialog(this,"Save File As",FileDialog.SAVE);
 }
 void setupMenuBar(){
  menuBar = new MenuBar();
  Menu fileMenu = new Menu("File");
  fileMenu.add(new MenuItem("New"));
  fileMenu.add(new MenuItem("Open"));
  fileMenu.addSeparator();
  fileMenu.add(new MenuItem("Save As"));
  fileMenu.addSeparator();
  fileMenu.add(new MenuItem("Exit"));
  menuBar.add(fileMenu);
  setMenuBar(menuBar);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   System.exit(0);
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof MenuItem){
    if("New".equals(event.arg)){
     text.setText("");
     return true;
    }else if("Open".equals(event.arg)){
     openFile.show();
     String inFile = openFile.getFile();
     readFile(inFile);
     return true;
    }else if("Save As".equals(event.arg)){
     saveFile.show();
     String outFile = saveFile.getFile();
     writeFile(outFile);
     return true;
    }else if("Exit".equals(event.arg)){
     System.exit(0);
     return true;
    }
   }
  }
  return false;
 }
 public void readFile(String file) {
  DataInputStream inStream;
  try{
   inStream = new DataInputStream(new FileInputStream(file));
  }catch (IOException ex){
   notifyDialog("Error opening file");
   return;
  }
  try{
   String newText="";
   String line;
   while((line=inStream.readLine())!=null)
    newText=newText+line+"\n";
   text.setText(newText);
   inStream.close();
  }catch (IOException ex){
   notifyDialog("Error reading file");
  }
 }
 public void writeFile(String file) {
  DataOutputStream outStream;
  try{
   outStream = new DataOutputStream(new FileOutputStream(file));
  }catch (IOException ex){
   notifyDialog("Error opening file");
   return;
  }
  try{
   outStream.writeBytes(text.getText());
   outStream.close();
  }catch (IOException ex){
   notifyDialog("Error writing file");
  }
 }
 public void notifyDialog(String msg) {
  Notification notification = new Notification(this,msg);
  notification.show();
 }
}
class Notification extends Dialog {
 String msg;
 public Notification(Frame f,String s) {
  super(f,"Notification",true);
  msg = s;
 }
 public void show() {
  add("North",new Label(msg,Label.CENTER));
  Panel p = new Panel();
  p.add(new Button("OK"));
  add("South",p);
  setBackground(Color.gray);
  resize(160,100);
  super.show();
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   dispose();
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof Button){
    if("OK".equals(event.arg)){
     dispose();
     return true;
    }
   }
  }
  return false;
 }
}

The TextEditApp program provides quite a bit more functionality than the other window programs that you've written so far. After you've compiled it, run the program. It will begin by launching a blank text-editing window with the Text Editor title, as shown in Figure 18.8. Figure 18.8 : The TextEditApp opening window. Click in the editor window and begin experimenting with editing text. Type in whatever text comes to your mind. Experiment with tabs to see how they work. Try typing past the right margin to see how the horizontal scrollbars work. Select text with your cursor and use the keyboard copy, cut, and paste commands. Double-click on a word to select it, and then type another word over it. Try typing a few lines to cause the vertical scrollbars to come into play. Your screen should look somewhat similar to Figure 18.9. Figure 18.9 : Editing text When you have finished editing the text, check out the File pull-down menu shown in Figure 18.10. You'll notice that the File menu supports four commands and contains two separator lines that help organize these commands. Figure 18.10 : The File pull-down menu Save your text in the file test.txt. A File Save As dialog box appears to help you save your file. The Windows 95 implementation of Java displays a dialog box, as shown in Figure 18.11. If you are using Java on another operating system, the dialog box will look different. Figure 18.11 : Saving text to a file. After you have saved your file, select New from the File menu. A new text-editing buffer is displayed. Having cleared the buffer, select Open from the File menu and open the test.txt file that you just saved. (See Figure 18.12.) This file will be read in and loaded into the text-editing buffer. Figure 18.12 : Opening a file. If this seems like a lot of functionality for two pages of code, you're right. Java does most of the work for you. You just need to invoke the correct methods for the java.awt API classes. Let's explore the TextEditApp program to see how it works. You'll first notice that it imports the classes of the java.awt and java.io classes. The java.awt package provides the required window classes, and the java.io package provides needed file I/O classes. TextEditApp follows the lead of the other window programs developed earlier in this chapter and subclasses the Frame class. It declares four field variables: text, menuBar, openFile, and saveFile. The text variable is of class TextArea. It holds the object that implements the functionality of the text-editing buffer. The menuBar variable sets up the File menu. The openFile and saveFile variables support the file dialog boxes shown in Figures 18.11 and 18.12. The main() method simply creates an object of TextEditApp. The TextEditApp constructor sets the window title using the constructor of the Frame class and then invokes its setup() method to set up all the objects in the TextEditApp window. The resize() method is called to size the application window based on the minimum size required to implement the TextArea object assigned to text. The pack() and show() methods complete the TextEditApp window setup and display. The setup() method sets the font to be used in the TextArea object, changes the window's background color, and then sets up the TextArea object, the menu bar, and the file dialog boxes. The setFont() method sets the current font to a newly constructed font. It is inherited from the Component class. The Font class constructor used to create the new font uses the font name (System), text type (PLAIN), and point size (12) of the font as parameters. The Font class defines a number of font parameters that can be used in constructing new font variations. The setBackground() method sets the background color to white. It is inherited from the Component class. The Color class provides a number of predefined color constants. The text variable is assigned an object of class TextArea that is at least 25 rows by 80 columns. This object implements most of the text-editing capabilities of TextEditApp. It is then added to the center of the TextEditApp window. The setupMenuBar() method is called to set up the File menu. The openFile and saveFile FileDialog variables are assigned newly created FileDialog objects. The FileDialog constructor takes three arguments. The first is the window that "owns" the dialog box. This is the window to which the file dialog box is attached. The window is attached to the TextEditApp object created by the main() method, so the this argument is supplied to identify the current TextEditApp object. The second parameter is the text string to be displayed at the top of the dialog box. Refer to Figures 18.11 and 18.12 to see how the text string is displayed. The third parameter is either the FileDialog.LOAD or the FileDialog.SAVE constant, indicating whether a file is to be loaded from disk or saved to disk. The setupMenuBar() method shows how menu bars are created and added to a window. The menuBar variable is assigned an object of class MenuBar. A Menu object is created with the File label and assigned to the fileMenu variable. Menu items and separators are then added to the menu. A Menu object represents a single pull-down menu. A MenuItem object is a pull-down menu selection. A MenuBar object is a collection of Menu objects that is attached to a window. The MenuItem objects are assigned a label when they are created. They are added to the fileMenu in a top-down fashion. The addSeparator() method is used to add a separator line to a menu. Refer to Figure 18.10 to see how the resulting File menu is displayed. The fileMenu is added to the menuBar object using the add() method of the MenuBar class. The resulting menu bar is added to the TextEditApp window using the setMenuBar() method inherited from the Frame class. At this point you might have noticed that the TextEditApp class has no paint() method. The TextArea object takes care of drawing itself and does not require an external paint() method. TextArea is a remarkably self-contained and easy-to-use class. Without TextArea, implementing TextEditApp would be considerably more difficult. The handleEvent() method handles more events than in previous examples. It handles the WINDOW_DESTROY event in the usual manner. This editor is just a prototype-a complete text editor would prompt the user to save any changes before terminating the application. All other events processed are of type ACTION_EVENT and have as their target objects of class MenuItem. In other words, these events process selections from the File menu. MenuItem objects are distinguished by their labels as supplied to the arg variable of an event. The handling of the New menu item invokes the setText() method of the TextArea class to delete all text in the text variable. The handling of the Open menu item invokes the show() method for the openFile FileDialog, causing the dialog box to be displayed. The getFile() method is invoked to get the name of the file selected for opening by the user. The returned string is then passed to the readFile() method of TextEditApp. The handling of the Save As menu item is similar to that of the Open menu item. The saveFile FileDialog is displayed. A filename is returned and then passed to the writeFile() method of the TextEditApp class. The Exit menu item is handled in the same way as the WINDOW_DESTROY event. Again, a complete editor would prompt the user to save changes before exiting. The readFile() method reads the file whose name was retrieved via the openFile FileDialog. It first opens the file by creating a new FileInputStream object using the filename and then filtering this stream as a DataInputStream object. The resulting input stream is then assigned to the inStream variable. The try statement has a catch clause that checks for the occurrence of an IOException. If an exception occurs, the notifyDialog is used to warn the user. It displays a dialog box as shown in Figure 18.13. You can cause this dialog box to be generated by trying to open a non-existent file. Try it! Figure 18.13 : The Notification dialog box. After the file has been opened, its text is read in a line at a time and appended to the newText variable. This results in the input being placed in one long string. The setText() method of the TextArea class is used to move the text into the TextArea object assigned to the text variable. Any I/O errors occurring while the file is being read result in the display of a notification dialog box with the Error reading file message. The writeFile() method operates in a similar but reverse manner than the readFile() method. It uses the filename retrieved via the saveFile FileDialog to create a FileOutputStream object. It then filters this stream as a DataOutputStream and assigns it to the outStream variable. If any errors occur in the creation of these streams, a dialog box is displayed with the message Error opening file. After opening the file, the writeBytes() method of the DataOutputStream class is invoked to write the data stored in the TextArea object assigned to the text variable to the output stream. The close() method is then invoked to close the output stream. Any write errors result in the display of an error-notification dialog box. The notifyDialog() method supports the generation and display of dialog boxes by creating new instances of the Notification class and passing them the error message. It then invokes their show() method to display them. The Notification class actually implements the dialog boxes that are displayed when an error occurs by extending the Dialog class. The Dialog class, like the Frame class, extends the Window class. It provides a different set of methods to display dialog boxes as opposed to main application windows. It has one variable, msg, that stores the message to be displayed in the dialog box. Its constructor takes two arguments: the application window object to which it is attached and the message to be displayed. It invokes the Dialog class constructor using the superclass constructor call statement and passes it the Frame object, the string "Notification" to be used as the title of the dialog box, and the true boolean value, which determines whether the dialog box will be modal. A modal dialog box is one that must be closed before the user can return to the main application window. (Java currently does not implement this blocking feature of modal dialog boxes.) The constructor then saves the error message argument in the msg variable. The show() method of the Notification class causes the dialog box to be displayed. It first creates an object of class Label with the contents of the msg variable being the text assigned to the Label object. The Label.CENTER constant is used to center the text in the Label object. The new label is then added to the North end of the dialog box. Dialog objects, like Frame objects, use a BorderLayout object by default. A Panel object is then created, and the OK button is created and added to the panel. The panel is added to the South end of the dialog box. The single button was placed in the panel to cause the button into be displayed in its default dimensions. Instead of the button being stretched to fit the dialog box, the panel in which the button resides is stretched. To see this, try directly inserting the button to the dialog box without using a panel. The background color of the dialog box is set to gray using the setBackground() method inherited from the Component class and the Color class constants. The window is resized to 160¥100 pixels and then displayed using the show() method of the Dialog class. The super keyword is used to indicate that the show() method of the superclass (Dialog) should be used instead of the show() method of the current class (Notification). The Notification class handles its own events by overriding the handleEvent() method. The WINDOW_DESTROY event is generated for a dialog box when the user tries to directly close the dialog box without using any of the dialog box buttons. This event is different from the event generated for the main application window. The Notification event handler handles the WINDOW_DESTROY event by invoking the dispose() method inherited from the Window class. The dispose() method causes the dialog window to be closed and its resources to be returned to the system. The handleEvent() method handles the OK button click by looking for an event with an id set to ACTION_EVENT, checking its target to make sure that it is an instance of the Button class and checking its arg variable to make sure that it is the OK button. It then invokes the dispose() method to close the dialog box.

Summary

This chapter introduces you to window programming by way of several sample programs that illustrate the classes and methods used to implement simple window applications. You should now be comfortable with analyzing window-based programs. Subsequent chapters fill in the details of the classes and methods introduced here. They will help you to become proficient in developing your own window programs. Chapter 19 lays the framework for developing window programs by expanding on the basics introduced here.