11.5 A Complete GUI
We've
looked separately at components, containers, layout management, and
event handling, so now it is time to tie these pieces together and
add the additional details required to create a complete graphical
user interface. Example 11-15 lists
ScribbleApp.java, a simple paint-style
application, pictured in Figure 11-13.
Figure 11-13. The ScribbleApp application

This application
relies on the scribbling capabilities of the
ScribblePane class of Example 11-13. It places a ScribblePane
instance within a JFrame container to create the
main application window and then adds a JMenuBar
and two JToolBar components to allow the user to
control the application. ScribbleApp uses a
JColorChooser to let the user select a drawing
color and a JOptionPane to display a confirmation
dialog when the user asks to quit. You should pay particular
attention to how these five Swing components are used; most
full-featured applications use them in similar ways. Note that Example 11-15 is a complete application; it is designed to
be run standalone, not to be viewed using the
ShowBean program. However, the
ScribbleApp class is designed as a subclass of
JFrame, so that other applications can instantiate
ScribbleApp windows of their own if they so
choose.This example also introduces the
javax.swing.Action interface, which is a
subinterface of java.awt.event.ActionListener. Any
Action object can be used as an
ActionListener to respond to an
ActionEvent generated by a component. What the
Action interface adds to the
ActionListener interface is the ability to
associate arbitrary properties with an Action
object. The Action interface also defines standard
property names that can specify the name, icon, and description of
the action performed by the listener. Action
objects are particularly convenient to use because they can be added
directly to JMenu and JToolBar
components; the components use the action name and/or icon to
automatically create appropriate menu items or toolbar buttons to
represent the action. (In Java 1.3, Action objects
can also be passed directly to the constructor methods of components
such as JButton.) Action objects can also be
enabled or disabled. When an action is disabled, any component that
has been created to represent it is also disabled, preventing the
user from attempting to perform the action.
Example 11-15. ScribbleApp.java
package je3.gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
/**
* This JFrame subclass is a simple "paint" application.
**/
public class ScribbleApp extends JFrame {
/**
* The main method instantiates an instance of the class, sets it size,
* and makes it visible on the screen
**/
public static void main(String[ ] args) {
// Set the look-and-feel for the application.
// LookAndFeelPrefs is defined elsewhere in this package.
LookAndFeelPrefs.setPreferredLookAndFeel(ScribbleApp.class);
ScribbleApp scribble = new ScribbleApp( );
// Handle window close requests
scribble.addWindowListener(new WindowAdapter( ) {
public void windowClosing(WindowEvent e) { System.exit(0); }
});
// Set the window size and make it visible.
scribble.setSize(500, 300);
scribble.setVisible(true);
}
// The scribble application relies on the ScribblePane
component developed
// earlier. This field holds the ScribblePane instance it uses.
ScribblePane scribblePane;
/**
* This constructor creates the GUI for this application.
**/
public ScribbleApp( ) {
super("Scribble"); // Call superclass constructor and set window title
// All content of a JFrame (except for the menubar) goes in the
// Frame's internal "content pane", not in the frame itself.
// The same is true for JDialog and similar top-level containers.
Container contentPane = this.getContentPane( );
// Specify a layout manager for the content pane
contentPane.setLayout(new BorderLayout( ));
// Create the main scribble pane component, give it a border and
// a background color, and add it to the content pane
scribblePane = new ScribblePane( );
scribblePane.setBorder(new BevelBorder(BevelBorder.LOWERED));
scribblePane.setBackground(Color.white);
contentPane.add(scribblePane, BorderLayout.CENTER);
// Create a menubar and add it to this window. Note that JFrame
// handles menus specially and has a special method for adding them
// outside of the content pane.
JMenuBar menubar = new JMenuBar( ); // Create a menubar
this.setJMenuBar(menubar); // Display it in the JFrame
// Create menus and add to the menubar
JMenu filemenu = new JMenu("File");
JMenu colormenu = new JMenu("Color");
menubar.add(filemenu);
menubar.add(colormenu);
// Create some Action objects for use in the menus and toolbars.
// An Action combines a menu title and/or icon with an ActionListener.
// These Action classes are defined as inner classes below.
Action clear = new ClearAction( );
Action quit = new QuitAction( );
Action black = new ColorAction(Color.black);
Action red = new ColorAction(Color.red);
Action blue = new ColorAction(Color.blue);
Action select = new SelectColorAction( );
// Populate the menus using Action objects
filemenu.add(clear);
filemenu.add(quit);
colormenu.add(black);
colormenu.add(red);
colormenu.add(blue);
colormenu.add(select);
// Add a submenu for selecting a look-and-feel.
// The LookAndFeelPrefs utility class is later in the chapter.
colormenu.add(new JSeparator( ));
colormenu.add(LookAndFeelPrefs.createLookAndFeelMenu(ScribbleApp.class,
new ActionListener( ) {
public void actionPerformed(ActionEvent e) {
SwingUtilities.updateComponentTreeUI(ScribbleApp.this);
}
}));
// Now create a toolbar, add actions to it, and add it to the
// top of the frame (where it appears underneath the menubar)
JToolBar toolbar = new JToolBar( );
toolbar.add(clear);
toolbar.add(select);
toolbar.add(quit);
contentPane.add(toolbar, BorderLayout.NORTH);
// Create another toolbar for use as a color palette, and add to
// the left side of the window.
JToolBar palette = new JToolBar( );
palette.add(black);
palette.add(red);
palette.add(blue);
palette.setOrientation(SwingConstants.VERTICAL);
contentPane.add(palette, BorderLayout.WEST);
}
/** This inner class defines the "clear" action that clears the scribble
*/class ClearAction extends AbstractAction {
public ClearAction( ) {
super("Clear"); // Specify the name of the action
}
public void actionPerformed(ActionEvent e) { scribblePane.clear( ); }
}
/** This inner class defines the "quit" action to quit the program */
class QuitAction extends AbstractAction {
public QuitAction( ) { super("Quit"); }
public void actionPerformed(ActionEvent e) {
// Use JOptionPane to confirm that the user really wants to quit
int response =
JOptionPane.showConfirmDialog(ScribbleApp.this,"Really Quit?");
if (response == JOptionPane.YES_OPTION) System.exit(0);
}
}
/**
* This inner class defines an Action that sets the current drawing color
* of the ScribblePane2 component. Note that actions of this type have
* icons rather than labels
**/
class ColorAction extends AbstractAction {
Color color;
public ColorAction(Color color) {
this.color = color;
putValue(Action.SMALL_ICON, new ColorIcon(color)); // specify icon
}
public void actionPerformed(ActionEvent e) {
scribblePane.setForeground(color); // Set current drawing color
}
}
/**
* This inner class implements Icon to draw a solid 16x16 block of the
* specified color. Most icons are instances of ImageIcon, but since
* we're only using solid colors here, it is easier to implement this
* custom Icon type
**/
static class ColorIcon implements Icon {
Color color;
public ColorIcon(Color color) { this.color = color; }
// These two methods specify the size of the icon
public int getIconHeight( ) { return 16; }
public int getIconWidth( ) { return 16; }
// This method draws the icon
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(color);
g.fillRect(x, y, 16, 16);
}
}
/**
* This inner class defines an Action that uses JColorChooser to allow
* the user to select a drawing color
**/
class SelectColorAction extends AbstractAction {
public SelectColorAction( ) { super("Select Color..."); }
public void actionPerformed(ActionEvent e) {
Color color = JColorChooser.showDialog(ScribbleApp.this,
"Select Drawing Color",
scribblePane.getForeground( ));
if (color != null) scribblePane.setForeground(color);
}
}
}