Search the Catalog
Learning Java

Learning Java

By Pat Niemeyer & Jonathan Knudsen
May 2000
1-56592-718-4, Order Number: 7184
720 pages, $34.95, Includes CD-ROM

Chapter 14
Using Swing Components

In this chapter:
Buttons and Labels
Checkboxes and Radio Buttons
Lists and Combo Boxes
Borders
Menus
The PopupMenu Class
The JScrollPane Class
The JSplitPane Class
The JTabbedPane Class
Scrollbars and Sliders
Dialogs

In the previous chapter, we discussed a number of concepts, including how Java's user interface facility is put together and how the larger pieces work. You should understand what components and containers are, how you use them to create a display, what events are, how components use them to communicate with the rest of your application, and what layout managers are.

Now that we're through with the general concepts and background, we'll get to the fun stuff: how to do things with Swing. We will cover most of the components that the Swing package supplies, how to use these components in applets and applications, and how to build your own components. We will have lots of code and lots of pretty examples to look at.

There's more material than fits in a single chapter. In this chapter, we'll cover all the basic user interface components. In the next chapter, we'll cover some of the more involved topics: text components, trees, tables, and creating your own components.

Buttons and Labels

We'll start with the simplest components: buttons and labels. Frankly, there isn't much to say about them. If you've seen one button, you've seen them all; and you've already seen buttons in the applications in Chapter 2, A First Application (HelloJava3 and HelloJava4). A button generates an ActionEvent when the user presses it. To receive these events, your program registers an ActionListener, which must implement the actionPerformed( ) method. The argument passed to actionPerformed( ) is the event itself.

There's one more thing worth saying about buttons, which applies to any component that generates an action event. Java lets us specify an "action command" string for buttons (and other components, like menu items, that can generate action events). The action command is less interesting than it sounds. It is just a String that serves to identify the component that sent the event. By default, the action command of a JButton is the same as its label; it is included in action events, so you can use it to figure out which button an event came from.

To get the action command from an action event, call the event's getActionCommand( ) method. The following code checks whether the user pressed the Yes button:

public void actionPerformed(ActionEvent e){ 
    if (e.getActionCommand(  ).equals("Yes") { 
        //the user pressed "Yes"; do something 
        ... 
    } 
}

You can change the action command by calling the button's setActionCommand( ) method. The following code changes button myButton's action command to "confirm":

myButton.setActionCommand("confirm");

It's a good idea to get used to setting action commands explicitly; this helps to prevent your code from breaking when you or some other developer "internationalizes" it, or otherwise changes the button's label. If you rely on the button's label, your code will stop working as soon as that label changes; a French user might see the label Oui rather than Yes. By setting the action command, you eliminate one source of bugs; for example, the button myButton in the previous example will always generate the action command confirm, regardless of what its label says.

Swing buttons can have an image in addition to a label. The JButton class includes constructors that accept an Icon object, which knows how to draw itself. You can create buttons with captions, images, or both. A handy class called ImageIcon takes care of loading an image for you and can be used to easily add an image to a button. The following example shows how this works:

//file: PictureButton.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
 
public class PictureButton extends JFrame {
 
  public PictureButton(  ) {
    super("PictureButton v1.0");
    setSize(200, 200);
    setLocation(200, 200);
    
    Icon icon = new ImageIcon("rhino.gif");
    JButton button = new JButton(icon);
    button.addActionListener(new ActionListener(  ) {
      public void actionPerformed(ActionEvent ae) {
        System.out.println("Urp!");
      }
    });
    
    Container content = getContentPane(  );
    content.setLayout(new FlowLayout(  ));
    content.add(button);
  }
 
  public static void main(String[] args) {
    JFrame f = new PictureButton(  );
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent we) { System.exit(0); }
    });
    f.setVisible(true);
  }
}

The example creates an ImageIcon from the rhino.gif file. Then a JButton is created from the ImageIcon. The whole thing is displayed in a JFrame. This example also shows the idiom of using an anonymous inner class as an ActionListener.

There's even less to be said about JLabel components. They're just text strings or images housed in a component. There aren't any special events associated with labels; about all you can do is specify the text's alignment, which controls the position of the text within the label's display area. As with buttons, JLabels can be created with Icons if you want to create a picture label. The following code creates some labels with different options:

// default alignment (CENTER)
JLabel label1 = new JLabel("Lions");
 
// left aligned
JLabel label2 = new JLabel("Tigers", SwingConstants.LEFT);
 
//label with no text, default alignment
JLabel label3 = new JLabel(  );
 
// create image icon 
Icon icon = new ImageIcon("rhino.gif");
 
// create image label 
JLabel label4 = new JLabel(icon);
 
// assigning text to label3
label3.setText("and Bears");
 
// set alignment 
label3.setHorizontalAlignment(SwingConstants.RIGHT);

The alignment constants are defined in the SwingConstants interface.

Now we've built several labels, using a variety of constructors and several of the class's methods. To display the labels, just add them to a container by calling the container's add( ) method.

The other characteristics you might like to set on labels, such as changing their font or color, are accomplished using the methods of the Component class, JLabel's distant ancestor. For example, you can call setFont( ) and setColor( ) on a label, as with any other component.

Given that labels are so simple, why do we need them at all? Why not just draw a text string directly on the container object? Remember that a JLabel is a JComponent. That's important; it means that labels have the normal complement of methods for setting fonts and colors that we mentioned earlier, as well as the ability to be managed sensibly by a layout manager. Therefore, they're much more flexible than a text string drawn at an absolute location within a container.

Speaking of layouts--if you use the setText( ) method to change the text of your label, the label's preferred size may change. But the label's container will automatically lay out its components when this happens, so you don't have to worry about it.

Swing can interpret HTML-formatted text in JLabel and JButton labels. The following example shows how to create a button with HTML-formatted text:

JButton button = new JButton(
  "<html>"
  + "S<font size=-1>MALL<font size=+0> "
  + "C<font size=-1>APITALS");

Checkboxes and Radio Buttons

A checkbox is a labeled toggle switch. Each time the user clicks it, its state toggles between checked and unchecked. Swing implements the checkbox as a special kind of button. Radio buttons are similar to checkboxes, but they are usually arranged in groups. Click on one radio button in the group, and the others automatically turn off. They are named for the preset buttons on old car radios.

Checkboxes and radio buttons are represented by instances of JCheckBox and JRadioButton, respectively. Radio buttons can be tethered together using an instance of another class called ButtonGroup. By now you're probably well into the swing of things (no pun intended) and could easily master these classes on your own. We'll use an example to illustrate a different way of dealing with the state of components and to show off a few more things about containers.

A JCheckBox sends ItemEvents when it's pushed. Since a checkbox is a kind of button, it also fires ActionEvents when it becomes checked. For something like a checkbox, we might want to be lazy and check on the state of the buttons only at some later time, such as when the user commits an action. It's like filling out a form; you can change your choices until you submit the form.

The following application, DriveThrough, lets us check off selections on a fast food menu, as shown in Figure 14-1. DriveThrough prints the results when we press the Place Order button. Therefore, we can ignore all the events generated by our checkboxes and radio buttons and listen only for the action events generated by the regular button.

//file: DriveThrough.java
import java.awt.*;
import java.awt.event.*;
 
import javax.swing.*;
 
public class DriveThrough {
  public static void main(String[] args) {
    JFrame f = new JFrame("Lister v1.0");
    f.setSize(300, 150);
    f.setLocation(200, 200);
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent we) { System.exit(0); }
    });
 
    JPanel entreePanel = new JPanel(  );
    final ButtonGroup entreeGroup = new ButtonGroup(  );
    JRadioButton radioButton;
    entreePanel.add(radioButton = new JRadioButton("Beef"));
    radioButton.setActionCommand("Beef");
    entreeGroup.add(radioButton);
    entreePanel.add(radioButton = new JRadioButton("Chicken"));
    radioButton.setActionCommand("Chicken");
    entreeGroup.add(radioButton);
    entreePanel.add(radioButton = new JRadioButton("Veggie", true));
    radioButton.setActionCommand("Veggie");
    entreeGroup.add(radioButton);
    
    final JPanel condimentsPanel = new JPanel(  );
    condimentsPanel.add(new JCheckBox("Ketchup"));
    condimentsPanel.add(new JCheckBox("Mustard"));
    condimentsPanel.add(new JCheckBox("Pickles"));
    
    JPanel orderPanel = new JPanel(  );
    JButton orderButton = new JButton("Place Order");
    orderPanel.add(orderButton);
    
    Container content = f.getContentPane(  );
    content.setLayout(new GridLayout(3, 1));
    content.add(entreePanel);
    content.add(condimentsPanel);
    content.add(orderPanel);
    
    orderButton.addActionListener(new ActionListener(  ) {
      public void actionPerformed(ActionEvent ae) {
        String entree = 
          entreeGroup.getSelection().getActionCommand(  );
        System.out.println(entree + " sandwich");
        Component[] components = condimentsPanel.getComponents(  );
        for (int i = 0; i < components.length; i++) {
          JCheckBox cb = (JCheckBox)components[i];
          if (cb.isSelected(  ))
            System.out.println("With " + cb.getText(  ));
        }
      }
    });
    
    f.setVisible(true);
  }
} 
Figure 14-1. The DriveThrough application

 

DriveThrough lays out three panels. The radio buttons in the entreePanel are tied together through a ButtonGroup object. We add( ) the buttons to a ButtonGroup to make them mutually exclusive. The ButtonGroup object is an odd animal. One expects it to be a container or a component, but it isn't; it's simply a helper object that allows only one RadioButton to be selected at a time.

In this example, the button group forces you to choose a beef, chicken, or veggie entree, but not more than one. The condiment choices, which are JCheckBoxes, aren't in a button group, so you can request any combination of ketchup, mustard, and pickles on your sandwich.

When the Place Order button is pushed, we receive an ActionEvent in the actionPerformed( ) method of our inner ActionListener. At this point, we gather the information in the radio buttons and checkboxes and print it. actionPerformed( ) simply reads the state of the various buttons. We could have saved references to the buttons in a number of ways; this example demonstrates two. First, we find out which entree was selected. To do so, we call the ButtonGroup's getSelection( ) method. This returns a ButtonModel, upon which we immediately call getActionCommand( ). This returns the action command as we set it when we created the radio buttons. The action commands for the buttons are the entrée names, which is exactly what we need.

To find out which condiments were selected, we use a more complicated procedure. The problem is that condiments aren't mutually exclusive, so we don't have the convenience of a ButtonGroup. Instead, we ask the condiments JPanel for a list of its components. The getComponents( ) method returns an array of references to the container's child components. We'll use this to loop over the components and print the results. We cast each element of the array back to JCheckBox and call its isSelected( ) method to see if the checkbox is on or off. If we were dealing with different types of components in the array, we could determine each component's type with the instanceof operator.

Lists and Combo Boxes

JLists and JComboBoxes are a step up on the evolutionary chain from JButtons and JLabels. Lists let the user choose from a group of alternatives. They can be configured to force the user to choose a single selection or to allow multiple choices. Usually, only a small group of choices are displayed at a time; a scrollbar lets the user move to the choices that aren't visible. The user can select an item by clicking on it. He or she can expand the selection to a range of items by holding down Shift and clicking on another item. To make discontinuous selections, the user can hold down the Control key instead of the Shift key.

A combo box is a cross-breed between a text field and a list. It displays a single line of text (possibly with an image) and a downward pointing arrow at one side. If you click on the arrow, the combo box opens up and displays a list of choices. You can select a single choice by clicking on it. After a selection is made, the combo box closes up; the list disappears and the new selection is shown in the text field.

Like every other component in Swing, lists and combo boxes have data models that are distinct from visual components. The list also has a selection model that controls how selections may be made on the list data.

Lists and combo boxes are similar because they have similar data models. Each is simply an array of acceptable choices. This similarity is reflected in Swing, of course: the type of a JComboBox's data model is a subclass of the type used for a JList's data model. The next example demonstrates this relationship.

The following example creates a window with a combo box, a list, and a button. The combo box and the list use the same data model. When you press the button, the program writes out the current set of selected items in the list. Figure 14-2 shows the example; the code itself follows.

Figure 14-2. A combo box and a list using the same data model

 

/file: Lister.java
import java.awt.*; 
import java.awt.event.*;
import javax.swing.*;
 
public class Lister {
  public static void main(String[] args) {
    JFrame f = new JFrame("Lister v1.0");
    f.setSize(200, 200);
    f.setLocation(200, 200);
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent we) { System.exit(0); }
    });
    
    // create a combo box
    String [] items = { "uno", "due", "tre", "quattro", "cinque",
                        "sei", "sette", "otto", "nove", "deici",
                        "undici", "dodici" };
    JComboBox comboBox = new JComboBox(items);
    comboBox.setEditable(true);
 
    // create a list with the same data model
    final JList list = new JList(comboBox.getModel(  ));
    
    // create a button; when it's pressed, print out
    // the selection in the list
    JButton button = new JButton("Per favore");
    button.addActionListener(new ActionListener(  ) {
      public void actionPerformed(ActionEvent ae) {
        Object[] selection = list.getSelectedValues(  );
        System.out.println("-----");
        for (int i = 0; i < selection.length; i++)
          System.out.println(selection[i]);
      }
    });
    
    // put the controls the content pane
    Container c = f.getContentPane(  );
    JPanel comboPanel = new JPanel(  );
    comboPanel.add(comboBox);
    c.add(comboPanel, BorderLayout.NORTH);
    c.add(new JScrollPane(list), BorderLayout.CENTER);
    c.add(button, BorderLayout.SOUTH);
 
    f.setVisible(true);
  }    
}

The combo box is created from an array of strings. This is a convenience--behind the scenes, the JComboBox constructor creates a data model from the strings you supply and sets the JComboBox to use that data model. The list is created using the data model of the combo box. This works because JList expects to use a ListModel for its data model, and the ComboBoxModel used by the JComboBox is a subclass of ListModel.

The button's action event handler simply prints out the selected items in the list, which are retrieved with a call to getSelectedValues( ). This method actually returns an object array, not a string array. List and combo box items, like many other things in Swing, are not limited to text. You can use images, or drawings, or some combination of text and images.

You might expect that selecting one item in the combo box would select the same item in the list. In Swing components, selection is controlled by a selection model. The combo box and the list have distinct selection models; after all, you can select only one item from the combo box, while it's possible to select multiple items from the list. Thus, while the two components share a data model, they have separate selection models.

We've made the combo box editable. By default, it would not be editable: the user could choose only one of the items in the drop-down list. With an editable combo box, the user can type in a selection, as if it were a text field. Non-editable combo boxes are useful if you just want to offer a limited set of choices; editable combo boxes are handy when you want to accept any input but offer some common choices.

There's a great class tucked away in the last example that deserves some recognition. It's JScrollPane. In Lister, you'll notice we created one when we added the List to the main window.

JScrollPane simply wraps itself around another Component and provides scrollbars as necessary. The scrollbars show up if the contained Component's preferred size (as returned by getPreferredSize( )) is greater than the size of the JScrollPane itself. In the previous example, the scrollbars show up whenever the size of the List exceeds the available space.

You can use JScrollPane to wrap any Component, including components with drawings or images or complex user interface panels. We'll discuss JScrollPane in more detail later in this chapter, and we'll use it frequently with the text components in the next chapter.

Borders

Any Swing component can have a decorative border. JComponent includes a method called setBorder( ); all you have to do is call setBorder( ), passing it an appropriate implementation of the Border interface.

Swing provides many useful Border implementations in the javax.swing.border package. You could create an instance of one of these classes and pass it to a component's setBorder( ) method, but there's an even simpler technique.

The BorderFactory class can create any kind of border for you using static "factory" methods. Creating and setting a component's border, then, is simple:

JLabel labelTwo = new JLabel("I have an etched border.");
labelTwo.setBorder(BorderFactory.createEtchedBorder(  ));

Every component has a setBorder( ) method, from simple labels and buttons right up to the fancy text and table components we'll cover in the next chapter.

BorderFactory is convenient, but it does not offer every option of every border type. For example, if you want to create a raised EtchedBorder instead of the default lowered border, you'll need to use EtchedBorder's constructor rather than a method in BorderFactory, like this:

JLabel labelTwo = new JLabel("I have a raised etched border.");
labelTwo.setBorder( new EtchedBorder(EtchedBorder.RAISED) );

The Border implementation classes are listed and briefly described here:

BevelBorder
This border draws raised or lowered beveled edges, giving an illusion of depth.

SoftBevelBorder
This border is similar to BevelBorder, but thinner.

EmptyBorder
Doesn't do any drawing, but does take up space. You can use it to give a component a little breathing room in a crowded user interface.

EtchedBorder
A lowered etched border gives the appearance of a rectangle that has been chiseled into a piece of stone. A raised etched border looks like it is standing out from the surface of the screen.

LineBorder
Draws a simple rectangle around a component. You can specify the color and width of the line in LineBorder's constructor.

MatteBorder
A souped-up version of LineBorder. You can create a MatteBorder with a certain color and specify the size of the border on the left, top, right, and bottom of the component. MatteBorder also allows you to pass in an Icon that will be used to draw the border. This could be an image (ImageIcon) or any other implementation of the Icon interface.

TitledBorder
A regular border with a title. TitledBorder doesn't actually draw a border; it just draws a title in conjunction with another border object. You can specify the locations of the title, its justification, and its font. This border type is particularly useful for grouping different sets of controls in a complicated interface.

CompoundBorder
A border that contains two other borders. This is especially handy if you want to enclose a component in an EmptyBorder and then put something decorative around it, like an EtchedBorder or a MatteBorder.

The following example shows off some different border types. It's only a sampler, though; many more border types are available. Furthermore, the example only encloses labels with borders. You can put a border around any component in Swing. The example is shown in Figure 14-3; the source code follows.

Figure 14-3. A bevy of borders

 

//file: Borders.java
import java.awt.*; 
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
 
public class Borders {
  public static void main(String[] args) {
    // create a JFrame to hold everything
    JFrame f = new JFrame("Borders");
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent we) { System.exit(0); }
    });
    f.setSize(300, 300);
    f.setLocation(200, 200);
    
    // Create labels with borders.
    int center = SwingConstants.CENTER;
    JLabel labelOne = new JLabel("raised BevelBorder", center);
    labelOne.setBorder(
        BorderFactory.createBevelBorder(BevelBorder.RAISED));
    JLabel labelTwo = new JLabel("EtchedBorder", center);
    labelTwo.setBorder(BorderFactory.createEtchedBorder(  ));
    JLabel labelThree = new JLabel("MatteBorder", center);
    labelThree.setBorder(
        BorderFactory.createMatteBorder(10, 10, 10, 10, Color.pink));
    JLabel labelFour = new JLabel("TitledBorder", center);
    Border etch = BorderFactory.createEtchedBorder(  );
    labelFour.setBorder(
        BorderFactory.createTitledBorder(etch, "Title"));
    JLabel labelFive = new JLabel("TitledBorder", center);
    Border low = BorderFactory.createLoweredBevelBorder(  );
    labelFive.setBorder(
        BorderFactory.createTitledBorder(low, "Title",
        TitledBorder.RIGHT, TitledBorder.BOTTOM));
    JLabel labelSix = new JLabel("CompoundBorder", center);
    Border one = BorderFactory.createEtchedBorder(  );
    Border two =
        BorderFactory.createMatteBorder(4, 4, 4, 4, Color.blue);
    labelSix.setBorder(BorderFactory.createCompoundBorder(one, two));
    
    // add components to the content pane
    Container c = f.getContentPane(  );
    c.setLayout(new GridLayout(3, 2));
    c.add(labelOne);
    c.add(labelTwo);
    c.add(labelThree);
    c.add(labelFour);
    c.add(labelFive);
    c.add(labelSix);
 
    f.setVisible(true);
  }    
}

Menus

A JMenu is a standard pull-down menu with a fixed name. Menus can hold other menus as submenu items, enabling you to implement complex menu structures. In Swing, menus are first-class components, just like everything else. You can place them wherever a component would go. Another class, JMenuBar, holds menus in a horizontal bar. Menu bars are real components, too, so you can place them wherever you want in a container: top, bottom, or middle. But in the middle of a container, it usually makes more sense to use a JComboBox rather than some kind of menu.

Menu items may have associated images and shortcut keys; there are even menu items that look like checkboxes and radio buttons. Menu items are really a kind of button. Like buttons, menu items fire action events when they are selected. You can respond to menu items by registering action listeners with them.

There are two ways to use the keyboard with menus. The first is called mnemonics. A mnemonic is one character in the menu name. If you hold down the Alt key and type a menu's mnemonic, the menu will drop down, just as if you had clicked on it with the mouse. Menu items may also have mnemonics. Once a menu is dropped down, you can select individual items in the same way.

Menu items may also have accelerators. An accelerator is a key combination that selects the menu item, whether or not the menu that contains it is showing. A common example is the accelerator Ctrl-C, which is frequently used as a shortcut for the Copy item in the Edit menu.

The following example demonstrates several different features of menus. It creates a menu bar with three different menus. The first, Utensils, contains several menu items, a submenu, a separator, and a Quit item that includes both a mnemonic and an accelerator. The second menu, Spices, contains menu items that look and act like checkboxes. Finally, the Cheese menu demonstrates how radio button menu items can be used.

This application is shown in Figure 14-4 with one of its menus dropped down. Choosing Quit from the menu (or pressing Ctrl-Q) removes the window. Give it a try.

//file: DinnerMenu.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
 
public class DinnerMenu extends JFrame {
  
  public DinnerMenu(  ) {
    super("DinnerMenu v1.0");
    setSize(200, 200);
    setLocation(200, 200);
    
    // create the Utensils menu
    JMenu utensils = new JMenu("Utensils");
    utensils.setMnemonic(KeyEvent.VK_U);
    utensils.add(new JMenuItem("Fork"));
    utensils.add(new JMenuItem("Knife"));
    utensils.add(new JMenuItem("Spoon"));
    JMenu hybrid = new JMenu("Hybrid");
    hybrid.add(new JMenuItem("Spork"));
    hybrid.add(new JMenuItem("Spife"));
    hybrid.add(new JMenuItem("Knork"));
    utensils.add(hybrid);
    utensils.addSeparator(  );
 
    // do some fancy stuff with the Quit item
    JMenuItem quitItem = new JMenuItem("Quit");
    quitItem.setMnemonic(KeyEvent.VK_Q);
    quitItem.setAccelerator(
        KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK));
    quitItem.addActionListener(new ActionListener(  ) {
      public void actionPerformed(ActionEvent e) { System.exit(0); }
    });
    utensils.add(quitItem);
    
    // create the Spices menu
    JMenu spices = new JMenu("Spices");
    spices.setMnemonic(KeyEvent.VK_S);
    spices.add(new JCheckBoxMenuItem("Thyme"));
    spices.add(new JCheckBoxMenuItem("Rosemary"));
    spices.add(new JCheckBoxMenuItem("Oregano", true));
    spices.add(new JCheckBoxMenuItem("Fennel"));
    
    // create the Cheese menu
    JMenu cheese = new JMenu("Cheese");
    cheese.setMnemonic(KeyEvent.VK_C);
    ButtonGroup group = new ButtonGroup(  );
    JRadioButtonMenuItem rbmi;
    rbmi = new JRadioButtonMenuItem("Regular", true);
    group.add(rbmi);
    cheese.add(rbmi);
    rbmi = new JRadioButtonMenuItem("Extra");
    group.add(rbmi);
    cheese.add(rbmi);
    rbmi = new JRadioButtonMenuItem("Blue");
    group.add(rbmi);
    cheese.add(rbmi);
    
    // create a menu bar and use it in this JFrame
    JMenuBar menuBar = new JMenuBar(  );
    menuBar.add(utensils);
    menuBar.add(spices);
    menuBar.add(cheese);
    setJMenuBar(menuBar);
  }
 
  public static void main(String[] args) {
    JFrame f = new DinnerMenu(  );
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent we) { System.exit(0); }
    });
    f.setVisible(true);
  }
}
Figure 14-4. The DinnerMenu application

 

Yes, we know. Quit doesn't belong in the Utensils menu. If it's driving you crazy, you can go back and add a File menu as an exercise when we're through.

Creating menus is pretty simple work. You create a JMenu object, specifying the menu's title.[1] Then you just add JMenuItems to the JMenu. You can also add JMenus to a JMenu; they show up as submenus. This is shown in the creation of the Utensils menu:

JMenu utensils = new JMenu("Utensils");
utensils.setMnemonic(KeyEvent.VK_U);
utensils.add(new JMenuItem("Fork"));
utensils.add(new JMenuItem("Knife"));
utensils.add(new JMenuItem("Spoon"));
JMenu hybrid = new JMenu("Hybrid");
hybrid.add(new JMenuItem("Spork"));
hybrid.add(new JMenuItem("Spife"));
hybrid.add(new JMenuItem("Knork"));
utensils.add(hybrid);

In the second line, we set the mnemonic for this menu using a constant defined in the KeyEvent class.

You can add those pretty separator lines with a single call:

utensils.addSeparator(  );

The Quit menu item has some bells and whistles we should explain. First, we create the menu item and set its mnemonic, just as we did before for the Utensils menu:

JMenuItem quitItem = new JMenuItem("Quit");
quitItem.setMnemonic(KeyEvent.VK_Q);

Now we want to create an accelerator for the menu item. We do this with the help of a class called KeyStroke:

quitItem.setAccelerator(
    KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK));

Finally, to actually do something in response to the menu item, we register an action listener:

quitItem.addActionListener(new ActionListener(  ) {
    public void actionPerformed(ActionEvent e) { System.exit(0); }
});

Our action listener exits the application when the Quit item is selected.

Creating the Spices menu is just as easy, except that we use JCheckBoxMenuItems instead of regular JMenuItems. The result is a menu full of items that behave like checkboxes.

The next menu, Cheese, is a little more tricky. We want the items to be radio buttons, but we need to place them in a ButtonGroup to ensure they are mutually exclusive. Each item, then, is created, added to the button group, and added to the menu itself.

The final step is to place the menus we've just created in a JMenuBar. This is simply a component that lays out menus in a horizontal bar. We have two options for adding it to our JFrame. Since the JMenuBar is a real component, we could add it to the content pane of the JFrame. Instead, we use a convenience method called setJMenuBar( ), which automatically places the JMenuBar at the top of the frame's content pane. This saves us the trouble of altering the layout or size of the content pane; it is adjusted to coexist peacefully with the menu bar.

The PopupMenu Class

One of Swing's nifty components is JPopupMenu, a menu that automatically appears when you press the appropriate mouse button inside of a component. (On a Windows system, for example, clicking the right mouse button invokes a popup menu.) Which button you press depends on the platform you're using; fortunately, you don't have to care--Swing figures it out for you.

The care and feeding of JPopupMenu is basically the same as any other menu. You use a different constructor (JPopupMenu( )) to create it, but otherwise, you build a menu and add elements to it the same way. The big difference is you don't need to attach it to a JMenuBar. Instead, just pop up the menu whenever you need it.

The following example, PopupColorMenu, contains three buttons. You can use a JPopupMenu to set the color of each button or the frame itself, depending on where you press the mouse. Figure 14-5 shows the example in action; the user is preparing to change the color of the bottom button.

Figure 14-5. The PopupColorMenu application

 

//file: PopUpColorMenu.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
 
public class PopUpColorMenu extends JFrame 
                            implements ActionListener {
  JPopupMenu colorMenu;
  Component selectedComponent;
  
  public PopUpColorMenu(  ) {
    super("PopUpColorMenu v1.0");
    setSize(100, 200);
    setLocation(200, 200);
    addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent e) { System.exit(0); }
    });
    
    MouseListener mouseListener = new MouseAdapter(  ) {
      public void mousePressed(MouseEvent e) { checkPopup(e); }
      public void mouseClicked(MouseEvent e) { checkPopup(e); }
      public void mouseReleased(MouseEvent e) { checkPopup(e); }
      private void checkPopup(MouseEvent e) {
        if (e.isPopupTrigger(  )) {
          selectedComponent = e.getComponent(  );
          colorMenu.show(e.getComponent(), e.getX(), e.getY(  ));
        }
      }
    };
 
    final Container content = getContentPane(  );
    content.setLayout(new FlowLayout(  ));
    JButton button = new JButton("Uno");
    button.addMouseListener(mouseListener);
    content.add(button);
    button = new JButton("Due");
    button.addMouseListener(mouseListener);
    content.add(button);
    button = new JButton("Tre");
    button.addMouseListener(mouseListener);
    content.add(button);
    
    colorMenu = new JPopupMenu("Color");
    colorMenu.add(makeMenuItem("Red"));
    colorMenu.add(makeMenuItem("Green"));
    colorMenu.add(makeMenuItem("Blue"));
    
    getContentPane(  ).addMouseListener(mouseListener);
 
    setVisible(true);
  }
  
  public void actionPerformed(ActionEvent e) {
    String color = e.getActionCommand(  );
    if (color.equals("Red"))
      selectedComponent.setBackground(Color.red);
    else if (color.equals("Green"))
      selectedComponent.setBackground(Color.green);
    else if (color.equals("Blue"))
      selectedComponent.setBackground(Color.blue);
  }
  
  private JMenuItem makeMenuItem(String label) {
    JMenuItem item = new JMenuItem(label);
    item.addActionListener( this );
    return item;
  }
 
  public static void main(String[] args) {
    new PopUpColorMenu(  );
  }
}

Because the popup menu is triggered by mouse events, we need to register a MouseListener for any of the components to which it applies. In this example, all three buttons and the content pane of the frame are eligible for the color popup menu. Therefore, we add a mouse event listener for all of these components explicitly. The same instance of an anonymous inner MouseAdapter subclass is used in each case. In this class, we override the mousePressed( ), mouse-Released( ), and mouseClicked( ) methods to display the popup menu when we get an appropriate event. How do we know what an "appropriate event" is? Fortunately, we don't need to worry about the specifics of our user's platform; we just need to call the event's isPopupTrigger( ) method. If this method returns true, we know the user has done whatever normally displays a popup menu on his or her system.

Once we know that the user wants to raise a popup menu, we display the popup menu by calling its show( ) method with the mouse event coordinates as arguments.

If we wanted to provide different menus for different types of components or the background, we'd create different mouse listeners for each different kind of component. The mouse listeners would invoke different kinds of popup menus as appropriate.

The only thing left is to handle the action events from the popup menu items. We use a helper method called makeMenuItem( ) to register the PopUpColorMenu window as an action listener for every item we add. The example implements ActionListener and has the required actionPerformed( ) method. This method reads the action command from the event, which is equal to the selected menu item's label by default. It then sets the background color of the selected component appropriately.

The JScrollPane Class

We used JScrollPane earlier in this chapter without explaining much about it. In this section we'll remedy the situation.

A JScrollPane is a container that can hold one component. Said another way, a JScrollPane wraps another component. By default, if the wrapped component is larger than the JScrollPane itself, the JScrollPane supplies scrollbars. JScrollPane handles the events from the scrollbars and displays the appropriate portion of the contained component.

Technically, JScrollPane is a Container, but it's a funny one. It has its own layout manager, which can't be changed. It can accommodate only one component at a time. This seems like a big limitation, but it isn't. If you want to put a lot of stuff in a JScrollPane, just put your components into a JPanel, with whatever layout manager you like, and put that panel into the JScrollPane.

When you create a JScrollPane, you can specify the conditions under which its scrollbars will be displayed. This is called the scrollbar display policy; a separate policy is used for the horizontal and vertical scrollbars. The following constants can be used to specify the policy for each of the scrollbars:

HORIZONTAL_SCROLLBAR_AS_NEEDED
Displays a scrollbar only if the wrapped component doesn't fit.

HORIZONTAL_SCROLLBAR_ALWAYS
Always shows a scrollbar, regardless of the contained component's size.

HORIZONTAL_SCROLLBAR_NEVER
Never shows a scrollbar, even if the contained component won't fit. If you use this policy, you should provide some other way to manipulate the JScrollPane.

VERTICAL_SCROLLBAR_AS_NEEDED
Displays a scrollbar only if the wrapped component doesn't fit.

VERTICAL_SCROLLBAR_ALWAYS
Always shows a scrollbar, regardless of the contained component's size.

VERTICAL_SCROLLBAR_NEVER
Never shows a scrollbar, even if the contained component won't fit. If you use this policy, you should provide some other way to manipulate the JScrollPane.

By default, the policies are HORIZONTAL_SCROLLBAR_AS_NEEDED and VERTICAL_SCROLLBAR_AS_NEEDED.

Here's an example that uses a JScrollPane to display a large image. The application itself is very simple; all we do is place the image in an ImageComponent, wrap a JScrollPane around it, and put the JScrollPane in a JFrame's content pane. Here's the code:

//file: ScrollPaneFrame.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
 
public class ScrollPaneFrame {
  public static void main(String[] args) {
    String filename = "Piazza di Spagna.jpg";
    if (args.length > 0)
      filename = args[0];
    
    JFrame f = new JFrame("ScrollPaneFrame v1.0");
    f.setSize(300, 300);
    f.setLocation(200, 200);
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent e) { System.exit(0); }
    });
 
    Image image = Toolkit.getDefaultToolkit(  ).getImage(filename);
    f.getContentPane(  ).add(
      new JScrollPane(new ImageComponent(image)));
    f.setVisible(true);
  }
}

And here's the ImageComponent. It waits for the image to load, using a MediaTracker, and sets its size to the size of the image. It also provides a paint( ) method to draw the image. This takes a single call to drawImage( ). The first argument is the image itself; the next two are the coordinates of the image relative to the ImageComponent; and the last is a reference to the ImageComponent itself (this), which serves as an image observer. (We'll discuss image observers in Chapter 18, Working with Images and Other Media; for the time being, take this on faith.)

//file: ImageComponent.java
import java.awt.*;
import javax.swing.*;
 
public class ImageComponent extends JComponent {
  Image image;
  Dimension size;
  
  public ImageComponent(Image image) {
    this.image = image;
    MediaTracker mt = new MediaTracker(this);
    mt.addImage(image, 0);
    try {
      mt.waitForAll(  );
    }
    catch (InterruptedException e) {
      // error ...
    };
 
    size = new Dimension (image.getWidth(null),
                          image.getHeight(null));
    setSize(size);
  }
  
  public void paint(Graphics g) {
    g.drawImage(image, 0, 0, this);
  }
  
  public Dimension getPreferredSize(  ) {
    return size;
  }
}

Finally, ImageComponent provides a getPreferredSize( ) method, overriding the method it inherits from Component. This method simply returns the image's size, which is a Dimension object. When you're using JScrollPane, it's important for the object you're scrolling to provide a reliable indication of its size. Figure 14-6 shows the ScrollPaneFrame with the ImageComponent.

Figure 14-6. The ScrollPaneFrame application

 

The JSplitPane Class

A split pane is a special container that holds two components, each in its own sub-pane. A splitter bar adjusts the sizes of the two sub-panes. In a document viewer, you could use a split pane to show a table of contents next to a full document.

The following example capitalizes on the ImageComponent class from the previous example. It displays two ImageComponents, wrapped in JScrollPanes, in either side of a JSplitPane. You can drag the splitter bar back and forth to adjust the sizes of the two contained components.

//file: SplitPaneFrame.java
import java.awt.*; 
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
 
public class SplitPaneFrame {
  public static void main(String[] args) {
    String fileOne = "Piazza di Spagna.jpg";
    String fileTwo = "L1-Light.jpg";
    
    if (args.length > 0) fileOne = args[0];
    if (args.length > 1) fileTwo = args[1];
  
    // create a JFrame to hold everything
    JFrame f = new JFrame("SplitPaneFrame");
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent we) { System.exit(0); }
    });
    f.setSize(300, 200);
    f.setLocation(200, 200);
    
    Image leftImage = Toolkit.getDefaultToolkit(  ).getImage(fileOne);
    Component left =
      new JScrollPane(new ImageComponent(leftImage));
    Image rightImage = Toolkit.getDefaultToolkit(  ).getImage(fileTwo);
    Component right = 
      new JScrollPane(new ImageComponent(rightImage));
    JSplitPane split = 
      new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
    split.setDividerLocation(100);
    f.getContentPane(  ).add(split);
 
    f.setVisible(true);
  }
}

This example is shown in Figure 14-7.

Figure 14-7. Using a split pane

 

The JTabbedPane Class

If you've ever dealt with the System control panel in Windows, you already know what a JTabbedPane is. It's a container with labeled tabs. When you click on a tab, a new set of controls is shown in the body of the JTabbedPane. In Swing, JTabbedPane is simply a specialized container.

Each tab has a name. To add a tab to the JTabbedPane, simply call addTab( ). You'll need to specify the name of the tab as well as a component that supplies the tab's contents. Typically, it's a container holding other components.

Even though the JTabbedPane only shows one set of components at a time, be aware that all the components on all the pages are in memory at one time. If you have components that hog processor time or memory, try to put them into some "sleep" state when they are not showing.

The following example shows how to create a JTabbedPane. It adds standard Swing components to a first tab, named Controls. The second tab is filled with an instance of ImageComponent, which was presented earlier in this chapter.

//file: TabbedPaneFrame.java
import java.awt.*; 
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
 
public class TabbedPaneFrame {
  public static void main(String[] args) {
    // create a JFrame to hold everything
    JFrame f = new JFrame("TabbedPaneFrame");
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent we) { System.exit(0); }
    });
    f.setSize(200, 200);
    f.setLocation(200, 200);
 
    JTabbedPane tabby = new JTabbedPane(  );
    
    // create a controls pane
    JPanel controls = new JPanel(  );
    controls.add(new JLabel("Service:"));
    JList list = new JList(
        new String[] { "Web server", "FTP server" });
    list.setBorder(BorderFactory.createEtchedBorder(  ));
    controls.add(list);
    controls.add(new JButton("Start"));
    
    // create an image pane
    String filename = "Piazza di Spagna.jpg";
    Image image = Toolkit.getDefaultToolkit(  ).getImage(filename);
    JComponent picture = new JScrollPane(new ImageComponent(image));
    
    tabby.addTab("Controls", controls);
    tabby.addTab("Picture", picture);
    
    f.getContentPane(  ).add(tabby);
    f.setVisible(true);
  }
}

The code is not especially fancy, but the result is an impressive-looking user interface. The first tab is a JPanel that contains some other components, including a JList with an etched border. The second tab simply contains an ImageComponent wrapped in a JScrollPane. The running example is shown in Figure 14-8.

Figure 14-8. Using a tabbed pane

 

Scrollbars and Sliders

JScrollPane is such a handy component that you may not ever need to use scrollbars by themselves. In fact, if you ever do find yourself using a scrollbar by itself, chances are you really want to use another component called a slider.

There's not much point in describing the appearance and functionality of scrollbars and sliders. Instead, let's jump right in with an example that includes both components. Figure 14-9 shows a simple example with both a scrollbar and a slider.

Figure 14-9. Using a scrollbar and a slider

 

Here is the source code for this example:

//file: Slippery.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
 
public class Slippery extends JFrame {
  
  public Slippery(  ) {
    super("Slippery v1.0");
    setSize(220, 160);
    setLocation(200, 200);
    
    Container content = getContentPane(  );
    
    JPanel main = new JPanel(new GridLayout(2, 1));
    JPanel scrollBarPanel = new JPanel(  );
    final JScrollBar scrollBar =
        new JScrollBar(JScrollBar.HORIZONTAL, 0, 48, 0, 255);
    int height = scrollBar.getPreferredSize(  ).height;
    scrollBar.setPreferredSize(new Dimension(175, height));
    scrollBarPanel.add(scrollBar);
    main.add(scrollBarPanel);
    
    JPanel sliderPanel = new JPanel(  );
    final JSlider slider =
        new JSlider(JSlider.HORIZONTAL, 0, 255, 128);
    slider.setMajorTickSpacing(48);
    slider.setMinorTickSpacing(16);
    slider.setPaintTicks(true);
    sliderPanel.add(slider);
    main.add(sliderPanel);
    
    content.add(main, BorderLayout.CENTER);
    
    final JLabel statusLabel =
        new JLabel("Welcome to Slippery v1.0");
    content.add(statusLabel, BorderLayout.SOUTH);
    
    // wire up the event handlers
    scrollBar.addAdjustmentListener(new AdjustmentListener(  ) {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        statusLabel.setText("JScrollBar's current value = "
                            + scrollBar.getValue(  ));
      }
    });
    
    slider.addChangeListener(new ChangeListener(  ) {
      public void stateChanged(ChangeEvent e) {
        statusLabel.setText("JSlider's current value = "
                            + slider.getValue(  ));
      }
    });
  }
 
  public static void main(String[] args) {
    JFrame f = new Slippery(  );
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent e) { System.exit(0); }
    });
    f.setVisible(true);
  }
}

All we've really done here is added a JScrollBar and a JSlider to our main window. If the user adjusts either of these components, the current value of the component is displayed in a JLabel at the bottom of the window.

The JScrollBar and JSlider are both created by specifying an orientation, either HORIZONTAL or VERTICAL. You can also specify the minimum and maximum values for the components, as well as the initial value. The JScrollBar supports one additional parameter, the extent. The extent simply refers to what range of values is represented by the slider within the scroll bar. For example, in a scrollbar that runs from 0 to 255, an extent of 128 means that the slider will be half the width of the scrollable area of the scrollbar.

JSlider supports the idea of tick marks, which are lines drawn at certain values along the slider's length. Major tick marks are slightly larger than minor tick marks. To draw tick marks, just specify an interval for major and minor tick marks, and then paint the tick marks:

    slider.setMajorTickSpacing(48);
    slider.setMinorTickSpacing(16);
    slider.setPaintTicks(true);

JSlider also supports labeling the ticks with text strings, using the setLabel-Table( ) method.

Responding to events from the two components is straightforward. The JScrollBar sends out AdjustmentEvents every time something happens; the JSlider fires off ChangeEvents when its value changes. In our simple example, we display the new value of the changed component in the JLabel at the bottom of the window.

Dialogs

A dialog is another standard feature of user interfaces. Dialogs are frequently used to present information to the user ("Your fruit salad is ready.") or to ask a question ("Shall I bring the car around?"). Dialogs are used so commonly in GUI applications that Swing includes a handy set of pre-built dialogs. These are accessible from static methods in the JOptionPane class. Many variations are possible; JOptionPane groups them into four basic types:

message dialog
Displays a message to the user, usually accompanied by an OK button.

confirmation dialog
Ask a question and displays answer buttons, usually Yes, No, and Cancel.

input dialog
Asks the user to type in a string.

option dialogs
The most general type--you pass it your own components, which are displayed in the dialog.

A confirmation dialog is shown in Figure 14-10.

Figure 14-10. Using a confirmation dialog

 

Let's look at examples of each kind of dialog. The following code produces a message dialog:

JOptionPane.showMessageDialog(f, "You have mail.");

The first parameter to showMessageDialog( ) is the parent component (in this case f, an existing JFrame). The dialog will be centered on the parent component. If you pass null for the parent component, the dialog is centered in your screen. The dialogs that JOptionPane displays are modal, which means they block other input to your application while they are showing.

Here's a slightly fancier message dialog. We've specified a title for the dialog and a message type, which affects the icon that is displayed:

JOptionPane.showMessageDialog(f, "You are low on memory.",
        "Apocalyptic message", JOptionPane.WARNING_MESSAGE);

Here's how to display the confirmation dialog shown in Figure 14-10:

int result = JOptionPane.showConfirmDialog(null,
        "Do you want to remove Windows now?");

In this case, we've passed null for the parent component. Special values are returned from showConfirmDialog( ) to indicate which button was pressed. There's a full example below that shows how to use this return value.

Sometimes you need to ask the user to type some input. The following code puts up a dialog requesting the user's name:

String name = JOptionPane.showInputDialog(null,
        "Please enter your name.");

Whatever the user types is returned as a String, or null if the user presses the Cancel button.

The most general type of dialog is the option dialog. You supply an array of objects that you wish to be displayed; JOptionPane takes care of formatting them and displaying the dialog. The following example displays a text label, a JTextField, and a JPasswordField. (Text components are described in the next chapter.)

JTextField userField = new JTextField(  );
JPasswordField passField = new JPasswordField(  );
String message = "Please enter your user name and password.";
result = JOptionPane.showOptionDialog(f,
    new Object[] { message, userField, passField },
    "Login", JOptionPane.OK_CANCEL_OPTION,
    JOptionPane.QUESTION_MESSAGE,
    null, null, null);

We've also specified a dialog title ("Login") in the call to showOptionDialog( ). We want OK and Cancel buttons, so we pass OK_CANCEL_OPTION as the dialog type. The QUESTION_MESSAGE argument indicates we'd like to see the question mark icon. The last three items are optional: an Icon, an array of different choices, and a current selection. Since the icon parameter is null, a default is used. If the array of choices and the current selection parameters were not null, JOptionPane might try to display the choices in a list or combo box.

The following application includes all the examples we've covered:

import javax.swing.*;
 
public class ExerciseOptions {
  public static void main(String[] args) {
    JFrame f = new JFrame("ExerciseOptions v1.0");
    f.setSize(200, 200);
    f.setLocation(200, 200);
    f.setVisible(true);
    
    JOptionPane.showMessageDialog(f, "You have mail.");
    JOptionPane.showMessageDialog(f, "You are low on memory.",
        "Apocalyptic message", JOptionPane.WARNING_MESSAGE);
    
    int result = JOptionPane.showConfirmDialog(null,
        "Do you want to remove Windows now?");
    switch (result) {
      case JOptionPane.YES_OPTION:
        System.out.println("Yes"); break;
      case JOptionPane.NO_OPTION:
        System.out.println("No"); break;
      case JOptionPane.CANCEL_OPTION:
        System.out.println("Cancel"); break;
      case JOptionPane.CLOSED_OPTION:
        System.out.println("Closed"); break;
    }
    
    String name = JOptionPane.showInputDialog(null,
        "Please enter your name.");
    System.out.println(name);
    
    JTextField userField = new JTextField(  );
    JPasswordField passField = new JPasswordField(  );
    String message = "Please enter your user name and password.";
    result = JOptionPane.showOptionDialog(f,
        new Object[] { message, userField, passField },
        "Login", JOptionPane.OK_CANCEL_OPTION,
        JOptionPane.QUESTION_MESSAGE,
        null, null, null);
    if (result == JOptionPane.OK_OPTION)
      System.out.println(userField.getText(  ) +
          " " +  new String(passField.getPassword(  )));
    
    System.exit(0);
  }
}

File Selection Dialog

A JFileChooser is a standard file-selection box. As with other Swing components, JFileChooser is implemented in pure Java, so it looks and acts the same on different platforms.

Selecting files all day can be pretty boring without a greater purpose, so we'll exercise the JFileChooser in a mini-editor application. Editor provides a text area in which we can load and work with files. (The JFileChooser created by Editor is shown in Figure 14-11.) We'll stop just shy of the capability to save and let you fill in the blanks (with a few caveats):

Figure 14-11. Using a JFileChooser

 

import java.awt.*;
import java.awt.event.*;
import java.io.*;
 
import javax.swing.*;
 
public class Editor
    extends JFrame
    implements ActionListener {
  public static void main(String[] s) { new Editor(  ); }
 
  private JEditorPane textPane = new JEditorPane(  );
  
  public Editor(  ) {
    super("Editor v1.0");
    addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent e) { System.exit(0); }
    });
    Container content = getContentPane(  );
    content.add(new JScrollPane(textPane), BorderLayout.CENTER);
    JMenu menu = new JMenu("File");
    menu.add(makeMenuItem("Open"));
    menu.add(makeMenuItem("Save"));
    menu.add(makeMenuItem("Quit"));
    JMenuBar menuBar = new JMenuBar(  );
    menuBar.add(menu);
    setJMenuBar(menuBar);
    setSize(300, 300);
    setLocation(200, 200);
    setVisible(true);
  }
  
  public void actionPerformed(ActionEvent e) {
    String command = e.getActionCommand(  );
    if (command.equals("Quit")) System.exit(0);
    else if (command.equals("Open")) loadFile(  );
    else if (command.equals("Save")) saveFile(  );
  }
  
  private void loadFile (  ) {
    JFileChooser chooser = new JFileChooser(  );
    int result = chooser.showOpenDialog(this);
    if (result == JFileChooser.CANCEL_OPTION) return;
    try {
      File file = chooser.getSelectedFile(  );
      java.net.URL url = file.toURL(  );
      textPane.setPage(url);
    }
    catch (Exception e) {
      textPane.setText("Could not load file: " + e);
    }
  }
  
  private void saveFile(  ) {
    JFileChooser chooser = new JFileChooser(  );
    chooser.showSaveDialog(this);
    // Save file data...
  }
  
  private JMenuItem makeMenuItem( String name ) {
    JMenuItem m = new JMenuItem( name );
    m.addActionListener( this );
    return m;
  }
}

Editor is a JFrame that lays itself out with a JEditorPane (which will be covered in the next chapter) and a pull-down menu. From the pull-down File menu, we can Open, Save, or Quit. The actionPerformed( ) method catches the events associated with these menu selections and takes the appropriate action.

The interesting parts of Editor are the private methods loadFile( ) and saveFile( ).loadFile( ) creates a new JFileChooser and calls its showOpen-Dialog( ) method.

A JFileChooser does its work when the showOpenDialog( ) method is called. This method blocks the caller until the dialog completes its job, at which time the file chooser disappears. After that, we can retrieve the designated file with the getFile( ) method. In loadFile( ), we convert the selected File to a URL and pass it to the JEditorPane, which displays the selected file. As you'll learn in the next chapter, JEditorPane can display HTML and RTF files.

You can fill out the unfinished saveFile( ) method if you wish, but it would be prudent to add the standard safety precautions. For example, you could use one of the confirmation dialogs we just looked at to prompt the user before overwriting an existing file.

The Color Chooser

Swing is chock full of goodies. JColorChooser is yet another ready-made dialog supplied with Swing; it allows your users to choose colors. The following very brief example shows how easy it is to use JColorChooser:

import java.awt.*;
import java.awt.event.*;
 
import javax.swing.*;
 
public class LocalColor {
  public static void main(String[] args) {
    final JFrame f = new JFrame("LocalColor v1.0");
    f.addWindowListener(new WindowAdapter(  ) {
      public void windowClosing(WindowEvent e) { System.exit(0); }
    });
    f.setSize(200, 200);
    f.setLocation(200, 200);
    final Container content = f.getContentPane(  );
    content.setLayout(new GridBagLayout(  ));
    JButton button = new JButton("Change color...");
    content.add(button);
    
    button.addActionListener(new ActionListener(  ) {
      public void actionPerformed(ActionEvent e) {
        Color c = JColorChooser.showDialog(f,
            "Choose a color", content.getBackground(  ));
        if (c != null) content.setBackground(c);
      }
    });
    
    f.setVisible(true);
  }
}

This examples shows a frame window with a single button. When you click on the button, a color chooser pops up. After you select a color, it becomes the background color of the frame window.

Basically all we have to do is call JColorChooser's static method showDialog( ). In this example, we've specified a parent component, a dialog title, and an initial color value. But you can get away with just specifying a parent component. Whatever color the user chooses is returned; if the user presses the Cancel button, null is returned.


1. Like the text of JButtons and JLabels, menu labels can contain simple HTML.

Back to: Learning Java


O'Reilly Home | O'Reilly Bookstores | How to Order | O'Reilly Contacts
International | About O'Reilly | Affiliated Companies

© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com