Translate

Tuesday, 1 November 2016

Chapter 21 -- Checkboxes, Choices, and Lists

Chapter 21

Checkboxes, Choices, and Lists



CONTENTS


This chapter covers the details of using the Checkbox, Choice, and List classes. It shows you how to create and use objects of these classes and how to create custom components that simplify the use of these GUI controls. When you finish this chapter, you will be able to effectively use checkboxes, radio buttons, choices, and lists in your Java window programs.

Using Checkboxes

Checkboxes are a common type of GUI control. They are typically used in form-like windows to simplify yes/no or true/false selections. The user checks a checkbox to indicate yes or true, and leaves it unchecked to indicate no or false. The name of the Checkbox class is somewhat misleading in that Checkbox objects are used to implement traditional checkboxes as well as radio buttons. Radio buttons are similar to traditional checkboxes in that they are in either an on or off state. They are different from traditional checkboxes in that only one radio button in a group may be on at a given time. They are aptly named after the buttons on a car radio, which can only be used to select a single radio channel at a given time. When Checkbox objects are used as radio buttons, they are associated with a CheckboxGroup object that enforces mutual exclusion among the Checkbox objects in its group.

The CheckboxPanel Class

Checkboxes, like menus, are easy to use but tedious to construct and organize. The CheckboxPanel class provides a more convenient approach to creating and organizing checkboxes. (See Listing 21.1.) Typically, checkboxes are created in groups and organized in a panel that is given a title. The CheckboxPanel class provides a constructor for quickly creating objects of this type. It also provides access methods for getting and setting the value of an individual checkbox within the panel, based on the checkbox's label.
Listing 21.1. The CheckboxPanel class source code. package jdg.ch21;

import java.awt.*;

public class CheckboxPanel extends Panel {
 public static int HORIZONTAL = 0;
 public static int VERTICAL = 1;
 public CheckboxPanel(String title,String labels[],int orientation) {
  super();
  int length = labels.length;
  if(orientation == HORIZONTAL) setLayout(new GridLayout(1,length+1));
  else setLayout(new GridLayout(length+1,1));
  add(new Label(title));
  for(int i=0;i<length;++i) add(new Checkbox(labels[i]));
 }
 public CheckboxPanel(String title,String labels[],boolean state[],
  int orientation) {
  super();
  int length = labels.length;
  if(orientation == HORIZONTAL) setLayout(new GridLayout(1,length+1));
  else setLayout(new GridLayout(length+1,1));
  add(new Label(title));
  for(int i=0;i<length;++i){
   Checkbox checkBox = new Checkbox(labels[i]);
   checkBox.setState(state[i]);
   add(checkBox);
 }
}
public boolean getState(String label) {
  Checkbox boxes[] = (Checkbox[])getComponents();
  for(int i=0;i<boxes.length;++i)
   if(label.equals(boxes[i].getLabel())) return boxes[i].getState();
  return false;
 }
 public void setState(String label,boolean state) {
  Checkbox boxes[] = (Checkbox[])getComponents();
  for(int i=0;i<boxes.length;++i)
   if(label.equals(boxes[i].getLabel())) boxes[i].setState(state);
  }
 }

Two CheckboxPanel constructors are provided. The first constructor uses a title string for the panel, an array of labels[] to be associated with checkboxes, and an orientation parameter that specifies whether the panel is to be organized in a vertical or horizontal fashion. A GridLayout object is used to organize the Label and Checkbox objects placed within the panel. The title Label is added at the top of vertical panels and on the left side of horizontal panels. Then the checkboxes are created, one at a time, and fill in the rest of the panel. The second constructor is similar to the first constructor, except that it uses an additional state[] array to set the initial state of the checkboxes that are added to the panel. The state of each checkbox is set using the setState() method of the Checkbox class. The getState() method takes the label of a checkbox as its parameter and searches the checkboxes contained in the panel for one whose label matches the specified label. It then returns the state of this checkbox. If no matching checkbox is found, it returns false. The setState() method is similar to the getState() method. It is used to update a checkbox with a given label.

Working with Radio Buttons

Radio buttons are created using the Checkbox class and are transformed from checkboxes into radio buttons when they are associated with a CheckboxGroup object. A CheckboxGroup can be assigned with the Checkbox constructor or using the setCheckboxGroup() method. Only one object in the checkbox group is allowed to be set at any given time.

The CheckboxGroupPanel Class

The CheckboxGroupPanel class extends the CheckboxPanel class to work with radio buttons. Its source code is shown in Listing 21.2.
Listing 21.2. The source code for the CheckboxGroupPanel class. package jdg.ch21;

import java.awt.*;

public class CheckboxGroupPanel extends CheckboxPanel {
 public CheckboxGroupPanel(String title,String labels[],int orientation) {
  super(title,labels,orientation);
  putInGroup();
 }
 public CheckboxGroupPanel(String title,String labels[],boolean state[],
  int orientation) {
  super(title,labels,state,orientation);
  putInGroup();
 }
 void putInGroup() {
  Component components[] = getComponents();
  int length = components.length;
  CheckboxGroup group = new CheckboxGroup();
  for(int i=1;i<length;++i){
   Checkbox checkBox = (Checkbox) components[i];
   checkBox.setCheckboxGroup(group);
  }
 }
}

The Checkbox panel constructors are overridden to place the checkboxes in the panel in a single group. If the second constructor is used, only one checkbox should be specified as being in the on state. The putInGroup() method uses the getComponents() method inherited from the Container class to create an array of the components contained in the panel. It creates a CheckboxGroup object and then indexes through the array, putting all checkboxes into this group using the setCheckboxGroup() method. The first component is skipped because it is the title of the panel.

The CheckboxApp Program

The CheckboxApp program illustrates the use of the CheckboxPanel and CheckboxGroupPanel classes. Its source code is shown in Listing 21.3.
Listing 21.3. The source code for the CheckboxApp program. import java.awt.*;
import jdg.ch20.MyMenu;
import jdg.ch20.MyMenuBar;
import jdg.ch20.MessageDialog;
import jdg.ch21.CheckboxPanel;
import jdg.ch21.CheckboxGroupPanel;

public class CheckboxApp extends Frame {
 MyMenuBar menuBar;
 MessageDialog dialog;
 CheckboxPanel checkboxPanel;
 CheckboxGroupPanel checkboxGroupPanel;
 public static void main(String args[]){
  CheckboxApp app = new CheckboxApp();
 }
 public CheckboxApp() {
  super("CheckboxApp");
  setup();
  pack();
  resize(minimumSize());
  show();
 }
 void setup() {
  setBackground(Color.white);
  setupMenuBar();
  setupCheckboxes();
 }
 void setupMenuBar(){
  Object menuItems[][] = {
   {"File","Exit"},
  };
  menuBar = new MyMenuBar(menuItems);
  setMenuBar(menuBar);
 }
 void setupCheckboxes(){
  setLayout(new GridLayout(1,2));
  String sports[] = {"Baseball","Basketball","Football","Hockey","Soccer"};
  checkboxPanel = new CheckboxPanel("What team sports do you like? ",
   sports,CheckboxPanel.VERTICAL);
  add(checkboxPanel);
  String ages[] = {"under 20","20 - 39","40 - 59","60 - 79","80 and over"};
  checkboxGroupPanel = new CheckboxGroupPanel("What is your age? ",
   ages,CheckboxPanel.VERTICAL);
  add(checkboxGroupPanel);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   if(event.target instanceof MessageDialog) return true;
   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(event.target instanceof Checkbox){
    String status;
    Checkbox checkbox = (Checkbox)event.target;
    if(checkbox.getState()) status = "You checked: ";
    else status = "You unchecked: ";
    String text[] = {status+checkbox.getLabel()+" "};
    String buttons[] = {"OK"};
    dialog = new MessageDialog(this,"Guess what?",true,text,buttons);
    dialog.show();
    return true;
   }
  }
  return false;
 }
}

When you execute the program, it displays the window shown in Figure 21.1. The left side of the window displays a CheckboxPanel object, and the right side displays a CheckboxPanelGroup object. Notice that traditional checkboxes are displayed on the left, and radio buttons are displayed on the right. Figure 21.1 : The CheckboxApp opening window. Click on the Basketball checkbox, as shown in Figure 21.2. The checkbox is checked and the dialog box shown in Figure 21.3 is displayed as the result of handling this event. Click on the Basketball checkbox again, and the dialog box shown in Figure 21.4 is displayed. The purpose of this type of dialog box is to illustrate checkbox event handling. It is not normally part of any application that uses checkboxes because the checkbox display indicates the state of a checkbox. Figure 21.2 : Checking Basketball. Figure 21.3 : You checked Basketball Figure 21.4 : You unchecked Basketball. Go ahead and check your favorite sports, and then turn your attention to the radio buttons. Select your age group, as shown in Figure 21.5. The program notifies you of your selection. Go ahead and select another age group, as shown in Figure 21.6. Notice that you can't select more than one age group at a given time. That's the idea behind radio buttons. Figure 21.5 : Select your age group Figure 21.6 : Now select a different age group The CheckboxApp program makes use of several custom components that you've built so far, including the MyMenu, MyMenuBar, MessageDialog, CheckboxPanel, and CheckboxGroupPanel classes. Try rewriting this program without using these classes and you'll find out how time- consuming and tedious it can be to write programs without custom classes. You should now be getting used to using this class-building approach to simplify your Java programming. The setupCheckboxes() method sets up the checkbox and radio button panels displayed in the previous figures. A GridLayout object is used to organize the main application window. The checkboxPanel variable is assigned the CheckboxPanel object that is created using the sports[] array, and the checkboxGroupPanel variable is assigned the CheckboxGroupPanel object that is created using the ages[] array. Both panels are then added to the Frame object being constructed. That's all the code required to create the GUI controls shown in Figure 21.1. The handleEvent() method looks to see if the target of an ACTION_EVENT is an instance of the Checkbox class. This will be true for both checkboxes and radio buttons. It then retrieves the checkbox from the event.target variable and uses the getState() method to obtain the state of the Checkbox object. Then it retrieves the label of the Checkbox object using the getLabel() method and passes this information to the user via a MessageDialog object.

Making Choices

The Choice class allows Motif-style choice lists to be used in Java window programs. These GUI controls are also supported in Windows 95 and NT programs. A choice list is a pull-down menu that allows a user to select a single item from a list. When a choice selection is made, an ACTION_EVENT is generated, and the program is then able to respond to that selection. Choices are like menus that are placed in the middle of a window.

The MyChoice Class

MyChoice is a short and sweet class that simplifies the construction of a Choice object. Its source code is shown in Listing 21.4.
Listing 21.4. The source code for the MyChoice class. package jdg.ch21;

import java.awt.*;

public class MyChoice extends Choice {
 public MyChoice(String labels[]) {
  super();
  int length = labels.length;
  for(int i=0;i<length;++i) {
   try {
    addItem(labels[i]);
   }catch (NullPointerException ex) {
    addItem("");
   }
  }
 }
}

Rather than constructing a Choice object and adding all of the items in the choice list, the MyChoice constructor takes an array of labels and adds them to the Choice object as it is constructed. The addItem() method of the Choice class throws the NullPointerException and is handled by adding a blank item to the choice list when a null pointer is encountered.

Selecting from Lists

The List class is a tad more sophisticated that the Choice class. It is similar in that it allows a user to select from a list of items that are displayed in a window component. It is different because it provides the capability to support multiple menu selections, to specify the size of the list window, and to dynamically update the list during program execution. The List class provides two constructors. The default constructor takes no parameters. The second constructor specifies the number of visible rows to be identified and whether multiple selections are allowed. The access methods supported by the List class are also more extensive than the Choice class. In particular, the delItem(), clear(), and replaceItem() methods allow List objects to be dynamically updated.

The MyList Class

The MyList class is very similar to the MyChoice class in that it enables a list to be constructed using an array of list items. (See Listing 21.5.) The MyList constructor also allows the number of rows displayed in the list and the multiple-selection parameter to be specified. The MyList constructor sets the foreground and background colors of the list box that is displayed. This is used to override any colors that might be set for the foreground and background of the list's container.
Listing 21.5. The source code for the MyList class. package jdg.ch21;

import java.awt.*;

public class MyList extends List {
 public MyList(int rows,boolean multiple,String labels[]) {
  super(rows,multiple);
  int length = labels.length;
  for(int i=0;i<length;++i) {
   try {
    addItem(labels[i]);
   }catch (NullPointerException ex) {
    addItem("");
   }
  }
  setBackground(Color.white);
  setForeground(Color.black);
 }
}

The ChoiceListApp Program

The ChoiceListApp program illustrates the use of the MyChoice and MyList classes. (See Listing 21.6.) It provides the capability to decide what you want to eat for your next meal and can be very handy when a moment of indecision arrives. The following source code lists only a basic set of menu items, but additional menu items can be added easily.
Listing 21.6. The source code for the ChoiceListApp program. import java.awt.*;
import jdg.ch20.MyMenu;
import jdg.ch20.MyMenuBar;
import jdg.ch21.MyChoice;
import jdg.ch21.MyList;

public class ChoiceListApp extends Frame {
 MyMenuBar menuBar;
 MyChoice mealChoice;
 MyList currentList;
 MyList mealList[];
 String meals[] = {"Breakfast","Lunch","Dinner"};
 String mealChoices[][] = {
  {"pancakes","eggs","bacon","ham","sausage","cereal",
   "toast","coffee","juice"},
  {"pizza","hamburger","hot dog","burrito","salad","fries",
   "chips","soda","milk"},
  {"spaghetti","carne asada","barbequed chicken","soup","salad",
   "bread","wine","beer","soda","milk"}
 };
 TextField text;
 public static void main(String args[]){
  ChoiceListApp app = new ChoiceListApp();
 }
 public ChoiceListApp() {
  super("ChoiceListApp");
  setup();
  pack();
  resize(275,175);
  show();
 }
 void setup() {
  setBackground(Color.white);
  setupMenuBar();
  setupChoice();
  setupLists();
  text = new TextField(40);
  add("North",new Label("Place your order:"));
  add("South",text);
  add("West",mealChoice);
  currentList = mealList[0];
  add("East",currentList);
 }
 void setupMenuBar(){
  Object menuItems[][] = {
   {"File","Exit"},
  };
  menuBar = new MyMenuBar(menuItems);
  setMenuBar(menuBar);
 }
 void setupChoice(){
  mealChoice = new MyChoice(meals);
 }
 void setupLists(){
  mealList = new MyList[meals.length];
  for(int i=0;i<meals.length;++i)
   mealList[i] = new MyList(5,true,mealChoices[i]);
 }
 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(event.target instanceof Choice){
    for(int i=0;i<meals.length;++i)
     if(meals[i].equals(event.arg)){
      remove(currentList);
      currentList = mealList[i];
      add("East",currentList);
      text.setText(meals[i]);
    }
   show();
   return true;
  }else if(event.target instanceof List){
   updateTextField();
   return true;
  }
 }else if(event.id==Event.LIST_SELECT || event.id==Event.LIST_DESELECT){
  updateTextField();
  return true;
 }
return false;
}
void updateTextField() {
 String order = mealChoice.getSelectedItem()+": ";
 String items[] = currentList.getSelectedItems();
 for(int i=0;i<items.length;++i) order += items[i]+" ";
 text.setText(order);
 }
}

Make sure that you have food on hand when you run the ChoiceListApp program. Its opening window is shown in Figure 21.7. Figure 21.7 : The ChoiceListApp opening window. The choice list shown on the left side of the window is used to select a meal. This selection determines what menu items are displayed in the list shown on the right side of the window. More than one item can be selected from the entrée list. The text field on the bottom of the screen identifies the selections that you have made. Go ahead and select Lunch from the choice list, as shown in Figure 21.8. Notice that the entrée list is updated with some typical lunch items. The text field tells you that you are now ordering lunch. Figure 21.8 : Selecting lunch. Go ahead and select some menu items from the entrée list. They are displayed in the text field, as shown in Figure 21.9. Figure 21.9 : Ordering lunch. Now select Dinner from the choice list and select some dinner entrées, as shown in Figure 21.10. The text field is updated to list your new selections. Figure 21.10 : Selecting another meal. The ChoiceListApp program declares several field variables. The menuBar variable is the now-standard variable used to identify the program's menu bar. The mealChoice variable is used to refer to the MyChoice object that displays the meals identified in the meals[] array. Two MyList variables are declared. The mealList[] array holds the three MyList objects used for breakfast, lunch, and dinner. These items are stored in the mealChoices[] array. The currentList variable points to the current menu entrée list being displayed. The text variable refers to the TextField object displayed at the bottom of the window. The main() method and ChoiceListApp() constructor follow the pattern that's been developed so far. The window is resized to make its display more visually appealing. A GridBagLayout layout would have been more appropriate for this type of application, but the overhead of implementing it would obscure the important points associated with the MyChoice and MyList classes. The setup() method sets up the background and menu bar and then invokes the setupChoice() and setupLists() methods to set up the MyChoice and MyList objects. The text field is initialized to be 40 characters wide, and then the user-interface objects are placed in the appropriate places in the ChoiceListApp window. The setupChoice() method constructs the mealChoice object using a simple, one-line constructor. The setupLists() method sets up the mealList object by indexing through the mealChoices[] array and setting up the individual MyList objects. The handleEvent() method looks for events of type ACTION_EVENT that are instances of the Choice class. It handles them by finding the element of the meals[] array that was selected and using it to select the next mealList[] element to be displayed as the currentList. The old list is removed, and a new currentList object is added. The text field is then set to the meals[] element that was selected. Note that this approach of removing the list and replacing it with a new one is less elegant than updating the list as it is displayed. The handleEvent() method processes ACTION_EVENT, which occurs when the user double-clicks on a list item. It invokes the updateTextField() method to update the text that is displayed in the text field at the bottom of the window's display. It also handles the LIST_SELECT and LIST_DESELECT events in the same fashion. The updateTextField() method retrieves the currently selected item in the MyChoice object referred to by mealChoice using the getSelectedItem() method of the Choice class. This String object is used to build the text display by identifying the currently selected meal. The getSelectedItems() method of the List class is used to return a list of all items selected in the currently displayed MyChoice object referred to by the currentList variable. These items are then added to the text field display.

Summary

This chapter shows you how to use the Checkbox, Choice, and List classes. It describes their available constructors and access methods and shows you how to use them as the basis for creating custom GUI components. Chapter 22, "Text and Fonts," shows you how to work with these features.




Chapter 20 -- Menus, Buttons, and Dialong Boxes

 



CONTENTS


This chapter covers the details of using the Menu, Button, and Dialog classes. It describes the available menu and button options and shows you how to quickly and easily construct menus and buttons for your window programs. The use of dialog boxes is explained, and a generic MessageDialog component is constructed and explained as part of an example. When you finish this chapter, you will be able to effectively use menus, buttons, and dialog boxes in your Java window programs.

Adding Menus to Windows

Java provides a rich set of menu-related classes to create and interact with pull-down menus. The MenuComponent class is the superclass of all menu-related classes. It extends the Object class. The getFont() and setFont() methods are the most useful methods provided by MenuComponent. Its two direct superclasses, MenuBar and MenuItem, provide most of the methods for creating and using menus. The CheckboxMenuItem class extends the MenuItem class and supports menu items that can be checked on or off. The Menu class extends the MenuItem class and implements a collection of MenuItem objects that can be assigned to a MenuBar object. You are probably somewhat familiar with menus from Chapters 18, "Opening Windows," and 19, "Organizing Window Programs." A program's application window is implemented by a Frame object. It can have one and only one MenuBar object, which is set using the setMenuBar() method. A menu bar is a collection of menus. Each menu is represented as a separate pull-down menu on the menu bar. Common examples are the File, Edit, and Help pull-down menus found in many window applications. The MenuBar class allows a special menu to be designated as a Help menu, but this feature is not implemented in Windows 95 or NT. A Menu object contains one or more MenuItem objects, which can be a normal user-selectable MenuItem object, a CheckboxMenuItem object, or another Menu object. Java supports tear-off menus, which are menus that can be removed from a menu bar. A tear-off menu is constructed in the same manner as a regular menu-you only need to set the boolean tear-off value in the Menu() constructor. Tear-off menus, however, are not implemented within Windows 95 or NT. The MenuItem class is the superclass of the Menu class. It allows a menu to be a menu item and is used in constructing cascading, multilevel menus. MenuItem is also the superclass of the CheckboxMenuItem class and provides the capability to implement menu items that can be checked or unchecked. If a MenuItem object is constructed directly with the MenuItem constructor, it becomes a normal menu item that is selected from a pull-down menu.

The MyMenu Class

The creation and organization of menu bars, menus, and menu items into a program's menu is a straightforward, but tedious, process. You have to create a menu bar, create and add menus to the menu bar, add menu items to the menus, and then add the menu bar to the program's application window. This usually involves the use of a large number of constructors and access methods. To illustrate the use of the menu-related classes and to simplify the menu-creation process, you'll create two classes, MyMenu and MyMenuBar, that can be used to quickly construct menus for Java programs. These classes implement multiple levels of menus, checkbox menu items, and menu-disabling options. The special Help menu and tear-off menus are not implemented, however, because they are transparent to Windows 95 and NT. The MyMenu class is used to construct menus using an array of objects consisting of String objects that represent menu labels, or arrays of objects that represent submenus. Menu labels can be either checkbox menu items or normal menu items and can be either initially enabled or disabled (grayed out). Checkbox menu items can be initially checked or unchecked. The first character of the label's text string is used to indicate what type of label it is. The character conventions are as follows:
+
A checkbox menu item that is initially checked and enabled.
#
A checkbox menu item that is initially checked and disabled.
-
A checkbox menu item that is initially unchecked and enabled. If the label consists of just -, it indicates a separator.
=
A checkbox menu item that is initially unchecked and disabled.
~
A normal menu item that is initially disabled.
  Any other character indicates a normal, enabled menu item. If the first character is !, it is ignored. This allows any menu item to begin with any character.
These conventions apply to menu options. Only the ~ and ! options are used with the menu's main label. Using these options greatly simplifies the process of a menu creation. The source code for the MyMenu class is shown in Listing 20.1.
Listing 20.1. The source code of the MyMenu class. package jdg.ch20;

import java.awt.*;

public class MyMenu extends Menu {
 public MyMenu(Object labels[]) {
  super((String)labels[0]);
  String menuName = (String) labels[0];
  char firstMenuChar = menuName.charAt(0);
  if(firstMenuChar == '~' || firstMenuChar =='!'){
   setLabel(menuName.substring(1));
   if(firstMenuChar == '~') disable();
  }
  for(int i=1;i<labels.length;++i) {
   if(labels[i] instanceof String){
    if("-".equals(labels[i])) addSeparator();
    else{
     String label = (String)labels[i];
     char firstChar = label.charAt(0);
     switch(firstChar){
     case '+':
      CheckboxMenuItem checkboxItem = new CheckboxMenuItem(label.substring(1));
      checkboxItem.setState(true);
      add(checkboxItem);
      break;
     case '#':
      checkboxItem = new CheckboxMenuItem(label.substring(1));
      checkboxItem.setState(true);
      checkboxItem.disable();
      add(checkboxItem);
      break;
     case '-':
      checkboxItem = new CheckboxMenuItem(label.substring(1));
      checkboxItem.setState(false);
      add(checkboxItem);
      break;
     case '=':
      checkboxItem = new CheckboxMenuItem(label.substring(1));
      checkboxItem.setState(false);
      checkboxItem.disable();
      add(checkboxItem);
      break;
     case '~':
      MenuItem menuItem = new MenuItem(label.substring(1));
      menuItem.disable();
      add(menuItem);
      break;
     case '!':
      add(label.substring(1));
      break;
     default:
      add(label);
     }
    }
   }else{
    add(new MyMenu((Object[])labels[i]));
   }
  }
 }
 public MenuItem getItem(String menuItem) {
  int numItems = countItems();
  for(int i=0;i<numItems;++i)
   if(menuItem.equals(getItem(i).getLabel())) return getItem(i);
  return null;
 }
}

The MyMenu class specifies that it is in the package jdg.ch20. Make sure that you place it in the jdg/ch20 directory and compile it. You'll be using it quite a bit in subsequent chapters. MyMenu contains no field variables. It consists of a single constructor and the getItem() access method. The getItem() method retrieves a menu item contained in the menu and based on the menu item's label. It uses the countItems() and getItem() methods of the Menu class to retrieve the menu items contained in a menu and the getLabel() method of the MenuItem class to match a menu item with the search string. The MyMenu constructor constructs a menu from an array of menu labels and nested menu arrays (representing submenus). For example, to construct a typical File menu, labeled File, with the New and Open menu items followed by a separator and an Exit menu item, you would use the following MyMenu constructor:
String fileMenuLabels[] = {"File","New","Open","-","Exit"};
MyMenu fileMenu = new MyMenu(fileLabelMenus);
The first object in the array must be a String object that is the main label associated with the menu. The following objects are either String objects identifying the labels of the menu items contained in the menu, separators, or second-level arrays representing submenus. For example, the following creates a multilevel menu:
String goMenuLabels[] = {"Go","Beginning","End","Previous","Next"};
String editMenuLabels[] = {"Edit","Copy","Cut","-","Paste","-",goMenuLabels};
MyMenu editLabel = new MyMenu(editMenuLabels);
Using the MyMenu class is much easier than constructing each of the individual menu items and adding them to a menu. Let's step through the MyMenu constructor to see how it works. It uses the super() class constructor call statement to construct a Menu object using the first label in the labels[] array. This label may contain the ~ or ! character as the first character. MyMenu() checks for these characters and readjusts the menu's label accordingly. If the first character of the menu's label is ~, MyMenu() will disable the entire menu using the disable() method of the MenuItem class. After setting up the menu's main label, MyMenu() iterates through the list of objects contained in labels[]. If the object is an instance of the String class, and is therefore a label, MyMenu() checks the first letter of the label and processes it accordingly. If the object is not an instance of the String class, MyMenu() calls itself again, passing the object to itself as another array of objects. It then adds the resulting MyMenu object to itself using the add() method of the Menu class. This allows submenus to be processed in a recursive fashion. MyMenu() processes the menu item labels by using a switch statement to check the first character of the label to see if it matches the +, #, -, =, ~, or ! character. If it does not match any of these characters, the label is added as a normal menu item. If the label equals -, a separator is added. If the first character is +, an enabled and checked CheckboxMenuItem object is added to the menu. The setState() method of the CheckboxMenuItem class is used to set the state of the menu item to checked. If the first character is #, a checked, but disabled, CheckboxMenuItem object is added. The disable() method of the MenuItem class is used to disable the menu item. The cases in which the first character of the label is - or = are processed in a similar manner, except that the CheckboxMenuItem object is initially unchecked. When the first character of the label is ~, a normal MenuItem object is added to the menu. The menu item is disabled. The ! character is an escape character that is used to create a normal menu item beginning with any of the special characters previously mentioned. When the first character of a label is !, the actual label generated begins with the subsequent character.

The MyMenuBar Class

The MyMenuBar class uses the MyMenu class presented in the previous section to quickly create an entire menu bar. Whereas the MyMenu class uses an array of labels and submenus to create a menu, the MyMenuBar class uses an array of these arrays to create the entire menu bar. For example, the following statements will construct a menu bar with File, Edit, and Help menus, each consisting of individual menu items: String menuBarLabels[] = {
 {"File","New","Open","-","~Save As","-","Exit"};
 {"Edit","Copy","Cut","-","~Paste"};
 {"Help","Index"};
};
MyMenuBar menuBar = new MyMenuBar(menuBarLabels);
Note that the Save As and Paste menu items are initially disabled. The source code of the MyMenuBar class is shown in Listing 20.2.
Listing 20.2. The source code of the MyMenuBar class. package jdg.ch20;

import java.awt.*;

public class MyMenuBar extends MenuBar {
 public MyMenuBar(Object labels[][]) {
  super();
  for(int i=0;i<labels.length;++i)
   add(new MyMenu(labels[i]));
 }
 public MyMenu getMenu(String menuName) {
  int numMenus = countMenus();
  for(int i=0;i<numMenus;++i)
   if(menuName.equals(getMenu(i).getLabel())) return((MyMenu)getMenu(i));
  return null;
 }
}

The MyMenuBar constructor simply iterates through the outer array and passes the first-level elements (which are themselves Object arrays) to the MyMenu constructor to construct MyMenu objects. These objects are then added to the MyMenuBar object being constructed using the add() method inherited from the MenuBar class. The getMenu() method retrieves a MyMenu object from a MyMenuBar object based on the label associated with the MyMenu object. It uses the CountMenus() and getMenu() methods of the MenuBar class to retrieve each MyMenu object contained in the menu bar. The getLabel() method of the MenuItem class is used to check the labels of the MyMenu objects against the search string.

The MenuApp Program

The MenuApp program illustrates the use of the MyMenuBar and MyMenu classes. Its source code is shown in Listing 20.3.
Listing 20.3. The source code of the MenuApp program. import java.awt.*;
import jdg.ch20.MyMenu;
import jdg.ch20.MyMenuBar;

public class MenuApp extends Frame {
 MyMenuBar menuBar;
 public static void main(String args[]){
  MenuApp app = new MenuApp();
 }
 public MenuApp() {
  super("Menu Madness");
  setup();
  pack();
  resize(400,400);
  show();
 }
 void setup() {
  setBackground(Color.white);
  setupMenuBar();
 }
 void setupMenuBar(){
  String gotoMenu[] = {"Go To","Beginning","End","-","Line Number"};
  Object menuItems[][] = {
   {"File","New","Open","-","~Save","~Save As","-","Exit"},
   {"Edit","Copy","Cut","-","~Paste"},
   {"Search","Find","~Find Next","~Find Previous","-", gotoMenu},
   {"View","-Hex","+Line Number","+Column Number"},
   {"Help","About Menu Madness"},
  };
  menuBar = new MyMenuBar(menuItems);
  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("Exit".equals(event.arg)){
     System.exit(0);
     return true;
    }else if("New".equals(event.arg) || "Open".equals(event.arg)){
     menuBar.getMenu("File").getItem("Save").enable();
     menuBar.getMenu("File").getItem("Save As").enable();
     return true;
    }else if("Copy".equals(event.arg) || "Cut".equals(event.arg)){
     menuBar.getMenu("Edit").getItem("Paste").enable();
     return true;
    }else if("Find".equals(event.arg)){
     menuBar.getMenu("Search").getItem("Find Next").enable();
     menuBar.getMenu("Search").getItem("Find Previous").enable();
     return true;
    }else if("Hex".equals(event.arg)){
     CheckboxMenuItem hexMenuItem =
      (CheckboxMenuItem) menuBar.getMenu("View").getItem("Hex");
     CheckboxMenuItem lineMenuItem =
      (CheckboxMenuItem) menuBar.getMenu("View").getItem("Line Number");
     CheckboxMenuItem colMenuItem =
      (CheckboxMenuItem) menuBar.getMenu("View").getItem("Column Number");
     boolean hexState = hexMenuItem.getState();
     lineMenuItem.setState(!hexState);
     colMenuItem.setState(!hexState);
     return true;
    }else if("Line Number".equals(event.arg) ||
     "Column Number".equals(event.arg)){
     CheckboxMenuItem hexMenuItem =
      (CheckboxMenuItem) menuBar.getMenu("View").getItem("Hex");
     CheckboxMenuItem lineMenuItem =
      (CheckboxMenuItem) menuBar.getMenu("View").getItem("Line Number");
     CheckboxMenuItem colMenuItem =
      (CheckboxMenuItem) menuBar.getMenu("View").getItem("Column Number");
     boolean lineState = lineMenuItem.getState();
     boolean columnState = colMenuItem.getState();
     hexMenuItem.setState(!(lineState | columnState));
     return true;
    }else if("About Menu Madness".equals(event.arg)){
     menuBar.getMenu("Help").disable();
     return true;
    }
   }
  }
  return false;
 }
}

MenuApp shows how the MyMenuBar and MyMenu classes are used to easily create a menu bar and to support the processing of menu-related events. When the program is executed, it displays a blank opening screen and a menu bar with five pull-down menus, as shown in Figure 20.1. Figure 20.1 : The MenuApp opening window. Click on the File menu and select the New menu item as shown in Figure 20.2. This will cause the Save and Save As menu items to become enabled. You can verify this by clicking on the File menu once again. Figure 20.2 : The File menu. Click on the Edit menu and select the Copy menu item as shown in Figure 20.3. This results in the Paste menu item becoming enabled. Figure 20.3 : The Edit menu. Click on the Search menu and then on the Go To menu item as shown in Figure 20.4. The Go To menu item is a second-level menu that is attached to the Search menu. Figure 20.4 : The Search menu. Click on the View menu and select the Hex checkbox menu item as shown in Figure 20.5. Notice that the Line Number and Column Number menu items that are initially checked become unchecked, as shown in Figure 20.6. Figure 20.5 : The View menu. Figure 20.6 : The View menu after checking Hex. Click on the Help menu and select About Menu Madness, as shown in Figure 20.7. This Help menu isn't much help at all because it is programmed to disable itself, as shown in Figure 20.8. Figure 20.7 : The Help Menu. Figure 20.8 : The Help menu disabled. You've completed the tour of the MenuApp program. Select Exit from the File menu to terminate the program's operation.

Inside MenuApp

The MenuApp class consists of a single field variable, menuBar, that is an object of class MyMenuBar. The MenuApp constructor creates a 400¥400 frame window with the title Menu Madness and invokes the setup() method to set up the background color and the menu bar. The setup() method invokes setupMenuBar() to actually perform the menu bar setup. The setupMenuBar() method creates a gotoMenu[] array as the labels of a submenu that will be attached to the Search menu. The menuItems[][] array is used to define the labels associated with the menu bar and its first-level menus. The gotoMenu[] array is included as an object in this array. Notice the use of the first-character conventions for disabling menu items and specifying menu items that are checkboxes. The menu bar is created, assigned to the menuBar variable, and set as the menu bar for the MenuApp frame. Creating the menu bar was a snap using the MyMenuBar class. However, creating the menu bar is only half the work. You also need to write event-handling code that acts on the menu items selected by the user. The event-handling code of MenuApp is used to illustrate the use of the various methods provided with menus and menu items. The WINDOWS_DESTROY event and the Exit menu item are handled by terminating the program's execution. The New and Open menu items cause the Save and Save As menu items to be enabled. The getMenu() method of MyMenuBar and the getItem() method of MyMenu are used to retrieve the Save and Save As MenuItem objects. The enable() method of the MenuItem class is used to enable these menu items. Note that the Save and Save As menu items, as well as some other menu items, are not handled. Selecting these menu items results in no action being performed by MenuApp. The Copy and Cut menu items are processed in a similar manner as the New and Open menu items. Selecting Copy or Cut menu items results in the Paste menu item being enabled. The Find menu item causes the Find Next and Find Previous menu items to be enabled. The Hex checkbox menu item is handled by using the getState() method to determine the state of its associated checkbox and then setting the checkbox state of the Line Number and Column Number checkbox menu items to the opposite state. The Line Number and Column Number menu items are set and reset independent of each other, but they are combined to determine the state of the Hex menu item. If both the Line Number and Column Number menu items are unchecked, the state of the Hex menu item is set to checked. Otherwise, the Hex menu item is set to unchecked. The handling of the About Menu Madness menu item shows how an entire menu can be disabled.

Working with Buttons

Buttons are one of the easiest GUI components to use in a Java window program. You create them using the Button constructor and add them to your program using the add() method of their container. After that, you just handle the events associated with them and, optionally, change their labels.

The ButtonBar Class

To do something a little bit creative with labels, you can design a ButtonBar class. This class creates a dialog box with a specified label and adds a list of buttons to the dialog box, in the form of a button bar. It also allows the buttons in the button bar to be arranged in a vertical or horizontal fashion. The source code for the ButtonBar class is shown in Listing 20.4.
Listing 20.4. The source code of the ButtonBar class. package jdg.ch20;

import java.awt.*;

public class ButtonBar extends Dialog {
 public static int HORIZONTAL = 0;
 public static int VERTICAL = 1;
 public ButtonBar(Frame parent,String title,String labels[],int orientation) {
  super(parent,title,false);
  int length = labels.length;
  if(orientation == HORIZONTAL) setLayout(new GridLayout(1,length));
  else setLayout(new GridLayout(length,1));
  for(int i=0;i<length;++i) add(new Button(labels[i]));
  pack();
  resize(minimumSize());
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY) show(false);
  return false;
 }
}

The HORIZONTAL and VERTICAL constants are used to specify the orientation of the button bar. The constructor uses the parent, title, labels[], and orientation parameters. The parent and title parameters are passed to the Dialog class constructor via the super() constructor call statement. The false value indicates that the button bar is not modal. The orientation parameter is used to determine the type of GridLayout associated with the button bar. After the orientation is specified, the Button objects, whose labels are specified by the labels[] array, are added to the button bar. The button bar dialog box is then packed and resized to the minimum size needed to contain the buttons. A single event is handled by the ButtonBar class. The WINDOWS_DESTROY event is generated when a user tries to close the button bar dialog box. When this happens, the show() method inherited from the Window class is used to hide the dialog box. The handleEvent() method returns the false value, allowing the event to be processed by the window containing the dialog box.

The ButtonApp Program

The ButtonApp program illustrates the operation of the ButtonBar class. Its source code is shown in Listing 20.5.
Listing 20.5. The source code of the ButtonApp program. import java.awt.*;
import jdg.ch20.MyMenu;
import jdg.ch20.MyMenuBar;
import jdg.ch20.ButtonBar;

public class ButtonApp extends Frame {
 MyMenuBar menuBar;
 ButtonBar buttonBar;
 public static void main(String args[]){
  ButtonApp app = new ButtonApp();
 }
 public ButtonApp() {
  super("ButtonApp");
  setup();
  pack();
  resize(400,400);
  show();
 }
 void setup() {
  setBackground(Color.white);
  setupMenuBar();
  String buttons[] = {"this","is","a","test"};
  buttonBar = new ButtonBar(this,"Button Bar",buttons,ButtonBar.HORIZONTAL);
 }
 void setupMenuBar(){
  Object menuItems[][] = {
   {"File","Exit"},
   {"View","-Button Bar"},
  };
  menuBar = new MyMenuBar(menuItems);
  setMenuBar(menuBar);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   if(event.target instanceof ButtonBar){
     CheckboxMenuItem buttonItem =
      (CheckboxMenuItem)menuBar.getMenu("View").getItem("Button Bar");
     buttonItem.setState(false);
     return true;
   }
   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("Button Bar".equals(event.arg)){
     CheckboxMenuItem buttonItem =
       (CheckboxMenuItem)menuBar.getMenu("View").getItem("Button Bar");
     boolean viewButtonBar = buttonItem.getState();
     buttonBar.show(viewButtonBar);
     return true;
    }
   }
  }
  return false;
 }
}

When you execute ButtonApp, it displays the opening window shown in Figure 20.9. Figure 20.9 : The ButtonApp opening window. Select Button Bar from the View menu, as shown in Figure 20.10. Figure 20.10 : Selecting Button Bar from the View menu. The button bar shown in Figure 20.11 is displayed on top of the main ButtonApp window. If you select the View menu once more, you will notice that the Button Bar menu item is implemented as a pull-down menu. It is checked or unchecked depending on whether the button bar is displayed. Figure 20.11 : The button bar. The ButtonApp program consists mostly of setup and event-handling software. It declares two field variables, menuBar and buttonBar, that are used to reference the program's menu bar and the button bar. The constructor follows the typical approach to constructing a frame window and creates a 400¥400 window with the ButtonApp title. The setup() method sets the background to white, creates a menu bar using the MyMenuBar class, and constructs a horizontal button bar using the ButtonBar class constructor introduced in the previous section. The rest of the program consists of event handling. The WINDOW_DESTROY event is handled a little differently than the typical approach. Remember that this event can be generated for the main application window or for the button bar dialog box. The event.target variable is checked to see if the event was generated for an instance of the ButtonBar class. If so, the checkbox associated with the Button Bar menu is unchecked. If the WINDOW_DESTROY event was generated for the main application window or the Exit menu item is selected, the program is terminated. If the Button Bar menu item is selected, the state of the checkbox associated with that menu item is used to determine whether the button bar should be displayed. The show() method inherited from the Component class is used to turn the button bar display on and off.

Using Dialog Boxes

The ButtonBar class illustrates some of the typical methods used with dialog boxes. Most of these methods are not specific to the Dialog class, which provides few methods of its own. Rather, they are inherited from the Window and Component classes, which are superclasses of the Dialog class. The Dialog class is used to construct a window that is displayed separately from the application menu. The window associated with a Dialog object is not allowed to contain a menu bar. It may be specified as being modal, meaning that it is displayed on top of the main application window until it is hidden or disposed of using the show() and dispose() methods. Most dialog boxes are not as elaborate as that made with the ButtonBar class. They are mainly used to display information to the user and get the user's response via a button click.

The MessageDialog Class

The MessageDialog class provides a custom component that implements the most common types of dialog boxes. It provides a convenient constructor for creating dialog boxes and partially handles the WINDOW_DESTROY event by hiding a dialog box from view. The source code is shown in Listing 20.6.
Listing 20.6. The source code of the MessageDialog class. package jdg.ch20;

import java.awt.*;

public class MessageDialog extends Dialog {
 public MessageDialog(Frame parent,String title,boolean modal,String text[],
  String buttons[]) {
  super(parent,title,modal);
  int textLines = text.length;
  int numButtons = buttons.length;
  Panel textPanel = new Panel();
  Panel buttonPanel = new Panel();
  textPanel.setLayout(new GridLayout(textLines,1));
  for(int i=0;i<textLines;++i) textPanel.add(new Label(text[i]));
  for(int i=0;i<numButtons;++i) buttonPanel.add(new Button(buttons[i]));
  add("North",textPanel);
  add("South",buttonPanel);
  setBackground(Color.lightGray);
  setForeground(Color.white);
  pack();
  resize(minimumSize());
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY) show(false);
  else if(event.id==Event.ACTION_EVENT && event.target instanceof Button)
   show(false);
  return false;
 }
}

The MessageDialog constructor uses the parent, title, modal, text[], and buttons[] parameters. The parent, title, and modal parameters are passed to the Dialog constructor of its parent class. Two Panel objects are created and assigned to textPanel and buttonPanel. The textPanel layout is specified as a GridLayout object and the buttonPanel layout is the default FlowLayout object. The text[] lines are arranged in a vertical grid in the textPanel. The buttons[] are laid out in a centered horizontal fashion within the buttonPanel. The layout for the MessageDialog object is BorderLayout, by default. The textPanel is added to the top of the dialog box and the buttonPanel is added to the bottom. The foreground and background colors are set to light gray and white. The dialog box is packed and resized to the minimum size needed to contain the two panels.

The MessageApp Program

The MessageApp program shows how the MessageDialog class can be used to implement traditional dialog box functions found in typical window programs. Its source code is shown in Listing 20.7.
Listing 20.7. The source code of the MessageApp program. import java.awt.*;
import jdg.ch20.MyMenu;
import jdg.ch20.MyMenuBar;
import jdg.ch20.MessageDialog;

public class MessageApp extends Frame {
 MyMenuBar menuBar;
 MessageDialog dialog;
 public static void main(String args[]){
  MessageApp app = new MessageApp();
 }
 public MessageApp() {
  super("MessageApp");
  setup();
  pack();
  resize(400,400);
  show();
 }
 void setup() {
  setBackground(Color.white);
  setupMenuBar();
 }
 void setupMenuBar(){
  Object menuItems[][] = {
   {"File","Exit"},
   {"View","Information","Confirmation","Selection"},
  };
  menuBar = new MyMenuBar(menuItems);
  setMenuBar(menuBar);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   if(event.target instanceof MessageDialog) return true;
   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("Information".equals(event.arg)){
     String text[] = {"Don't look now, but your shoelace is untied."};
     String buttons[] = {"OK"};
     dialog = new MessageDialog(this,"Information",true,text,buttons);
     dialog.show();
     return true;
    }else if("Confirmation".equals(event.arg)){
     String text[] = {"Do you really want to do this?"};
     String buttons[] = {"Yes","No","Cancel"};
     dialog = new MessageDialog(this,"Confirmation",true,text,buttons);
     dialog.show();
     return true;
    }else if("Selection".equals(event.arg)){
     String text[] = {"What direction do you want to go?",
      "North: cold", "South: warm", "East: humid", "West: arid"};
     String buttons[] = {"North","South","East","West"};
     dialog = new MessageDialog(this,"Selection",true,text,buttons);
     dialog.show();
     return true;
    }
   }
  }
  return false;
 }
}

The MessageApp opening window is shown in Figure 20.12. It supports the File and View pull-down menus. Figure 20.12 : The MessageApp opening windows. Select the Information menu item from the View pull-down menu, as shown in Figure 20.13. Figure 20.13 : Selecting Information from the View menu. A helpful Information dialog box is displayed, as shown in Figure 20.14. This type of dialog box is typically used to provide information to the user. When the dialog box is displayed, the user acknowledges the information by clicking on the OK button. Figure 20.14 : The Information dialog box. Selecting Confirmation from the View menu results in a Confirmation dialog box being displayed to the user, as shown in Figure 20.15. This type of dialog box requests confirmation from the user before attempting to perform an operation that may require the user's approval. If the user clicks the Yes button, the action is performed. If the user clicks No, the operation is not performed. If the user clicks Cancel, an entire series of actions leading up to the confirmation dialog box is aborted. Figure 20.15 : The Confirmation dialog box. Choosing the Selection menu item from the View menu results in a multiple-choice Selection dialog box displayed to the user. The user is allowed to pick one from several alternative paths of program execution. (See Figure 20.16.) Figure 20.16 : The Selection dialog box. The MessageApp constructor creates a 400¥400 window, titled MessageApp. It uses the MyMenuBar class to construct the program's menu bar. No special processing of note is performed in the application window's construction. The dialog boxes, previously shown, are created by the program's event-handling software. The handleEvent() method takes the message dialog boxes into account when processing the WINDOW_DESTROY message. It checks to see if the target of the event is a dialog box and, if so, continues with program execution. If the Information menu item is selected, a new MessageDialog object is created with the information shown in Figure 20.14 and the dialog box is displayed to the user using the show() method. The dialog box is modal. The Confirmation and Selection menu items are handled in a similar manner. They create the dialog boxes shown in Figures 20.15 and 20.16 using the MessageDialog() constructor.

The FileDialog Class

The FileDialog class is a custom subclass of the Dialog class. The FileDialog class uses the operating system's native dialog boxes to query the user for the name of a file to be loaded or saved. The getDirectory() and getFile() methods return the file's directory and filename. The setDirectory() and setFile() methods are used to set the state of the dialog box to a particular directory and file when it is initially opened. The getMode() method returns the LOAD or SAVE mode of the dialog box. The setFileNameFilter() and getFileNameFilter() methods are used to associate an object of the FileNameFilter interface with the dialog box. No classes that implement this interface are currently provided by the Java API. The TextEdit example in Chapter 18 illustrates the use of the FileDialog class in performing file load and save operations.

Summary

This chapter shows you how to use the Menu, Button, and Dialog classes. It describes the available menu and button options and shows you how to quickly and easily construct menus and buttons for your window programs using the MyMenu, MyMenuBar, and ButtonBar classes. The use of dialog boxes is also covered. You have created the MessageDialog class, which you can reuse in other programs. Chapter 21, "Checkboxes, Choices, and Lists," shows you how to work with those elements.