Main Page

Previous Next

Instant Dialogs

The JOptionPane class in the javax.swing package defines a number of static methods that will create and display standard modal dialogs for you. The simplest dialog you can create this way is a message dialog rather like our About message dialog. The following methods produce message dialogs:

Dial-a-Dialog Methods

Description

showMessageDialog
(Component parent, Object message)

This method displays a modal dialog with the default title "Message". The first argument is the parent for the dialog. The Frame object containing the component will be used to position the dialog. If the first argument is null, a default Frame object will be created and that will be used to position the dialog centrally on the screen.

The second argument specifies what is to be displayed in addition to the default OK button. This can be a String object specifying the message or an Icon object defining an icon to be displayed. It can also be a Component, in which case the component will be displayed. If some other type of object is passed as the second argument, its toString() method will be called, and the String object that is returned will be displayed. You will usually get a default icon for an information message along with your message.

You can pass multiple items to be displayed by passing an array of type Object[] for the second argument. Each array element will be processed as above according to its type and they will be arranged in a vertical stack. Clever, eh?

showMessageDialog
(Component parent, Object message, String title, int messageType)

This displays a dialog as above, but with the title specified by the third argument. The fourth argument, messageType, can be:

ERROR_MESSAGE
INFORMATION_MESSAGE
WARNING_MESSAGE
QUESTION_MESSAGE
PLAIN_MESSAGE

These determine the style of the message constrained by the current look and feel. This will usually include a default icon, such as a question mark for QUESTION_MESSAGE.

showMessageDialog
(Component parent, Object message, String title, int messageType, Icon icon)

This displays a dialog as above except that the icon will be what you pass as the fifth argument. Specifying a null argument for the icon will produce a dialog the same as the previous version of the method.

We could have used one of these for the About dialog instead of all that messing about with inner classes. Let's see how.

Try It Out – An Easy About Dialog

Delete the inner class, AboutDialog, from SketchFrame – we won't need that any longer. Change the implementation of the actionPerformed() method in the SketchFrame class to:

  public void actionPerformed(ActionEvent e) {
    if(e.getSource() == aboutItem) {
      // Create about dialog with the menu item as parent
      JOptionPane.showMessageDialog(this,                          // Parent
                            "Sketcher Copyright Ivor Horton 2001", // Message
                            "About Sketcher",                      // Title
                            JOptionPane.INFORMATION_MESSAGE);      // Message type
    }
  }

Compile SketchFrame again and run Sketcher. When you click on the Help/About menu item, you should get something like the following:

The pretty little icon comes for free.

How It Works

All the work is done by the static showMessageDialog() method in the JOptionPane class. What you get is controlled by the arguments that you supply, and the Swing look-and-feel in use. By default this will correspond to what is usual for your system. On a PC running Windows, we get the icon you see because we specified the message type as INFORMATION_MESSAGE. You can try plugging in the other message types to see what you get.

Input Dialogs

JOptionPane also has four static methods that you can use to create standard modal input dialogs:

showInputDialog(Object message)

This method displays a default modal input dialog with a text field for input. The message you pass as the argument is set as the caption for the input field and the default also supplies an OK button, a Cancel button and Input as a title. For example, if you pass the message "Enter Input:" as the argument in the following statement,

String input = JOptionPane.showInputDialog("Enter Input:");

the dialog shown will be displayed:

When you click on the OK button, whatever you entered in the text field will be returned and stored in input – "This is the input" in this case. If you click on the Cancel button, null will be returned. Note that this is not the same as no input. If you click on OK without entering anything in the text field, a reference to an empty String object will be returned.

showInputDialog(Component parent, Object message)

This produces the same dialog as the previous method, but with the component you specify as the first argument as the parent of the dialog.

showInputDialog(Component parent, Object message,

String title, int messageType)

In this case the title of the dialog is supplied by the third argument, and the style of the message is determined by the fourth argument. The values for the fourth argument can be any of those discussed earlier in the context of message dialogs. For instance, you could display the dialog shown with the following statement:

String input = JOptionPane.showInputDialog(null, "Enter Input:",
                                 "Dialog for Input", JOptionPane.WARNING_MESSAGE);

The data that you enter in the text field is returned by the showInputDialog() method when the OK button is pressed as before.

showInputDialog(Component parent, Object message,
                String title, int messageType,
                Icon icon, Object[] selections,
                object initialSelection)

This version of the method provides a list of choices in a drop-down list box. The items from which to choose are passed as the sixth argument as an array and they can be of any class type. The initial selection to be displayed when the dialog is first displayed is specified by the seventh argument. Whatever is chosen when the OK button is clicked will be returned as type Object, and, if the Cancel button is clicked, null will be returned. You can specify your own icon to replace the default icon by passing a reference of type Icon as the fifth argument. The following statements display the dialog shown:

String[] choices = {"Money", "Health", "Happiness", "This", "That", "The Other"};
String input = (String)JOptionPane.showInputDialog(null, "Choose now...",
                                           "The Choice of a Lifetime",
                                           JOptionPane.QUESTION_MESSAGE,
                                           null,            // Use default icon
                                           choices,         // Array of choices
                                           choices[1]);     // Initial choice

Note that you have to cast the reference returned by this version of the showInputDialog() method to the type of choice value you have used. Here we are using type String, but the selections could be type Icon, or whatever you want.

Using a Dialog to Create Text Elements

It would be good if our Sketcher program also provided a means of adding text to a picture – after all, you might want to put your name to a sketch. A dialog is the obvious way to provide the mechanism for entering the text when we create text elements. We can use one of the showInputDialog() methods for this, but first we need to add a Text menu item to the Elements menu, and we will need a class to represent text elements, the Element.Text class of course, with Element as a base class. Let's start with the Element.Text class.

Text is a little tricky. For one thing we can't treat it just like another element. There is no object that implements the Shape interface that represents a text string so, unless we want to define one, we can't use the draw()method for a graphics context to display text. We have to use the drawString() method. We'll also have to figure out the bounding rectangle for the text on screen for ourselves. With Shape objects you could rely on the getBounds() method supplied by the 2D shape classes in java.awt.geom, but with text you're on your own.

Ideally we want to avoid treating text elements as a special case. Having many tests for types while we're drawing a sketch in the paint() method for the view generates considerable processing overhead that we would be better off without. One way around this is to make every element draw itself. We could implement a polymorphic method in each element class, draw() say, and pass a Graphics2D object to it. Each shape or text element could then figure out how to draw itself. The paint() method in the view class would not need to worry about what type of element was being drawn at all.

Try It Out – Defining the Element.Text Class

Let's see how that works out. We can start by adding an abstract method, draw() to the Element class definition:

public abstract class Element {
  public Element(Color color) {  
    this.color = color;  
  }

  public   Color getColor() { 
    return color;  
  }
  
  public abstract java.awt.Rectangle getBounds();
  public abstract void modify(Point start, Point last);
  public abstract void draw(Graphics2D g2D);

  protected Color color;              // Color of a shape

  // Plus definitions for our shape classes...
}

Note that we have deleted the getShape() method as we won't be needing it further. You can remove it from all the nested classes of the Element class. The draw() method now needs to be implemented in each of the nested classes to the Element class, but because each of the current classes has a Shape member, it will be essentially the same in each. The version for the Element.Line class will be:

    public void draw(Graphics2D g2D) {
      g2D.setPaint(color);                      // Set the line color
      g2D.draw(line);                           // Draw the line
    }

This just sets the color and passes the Shape object that is a member of the class to the draw()method for the Graphics2D object. To implement the draw() method for the other element classes, just replace the argument to the g2D.draw() method call, with the name of the Shape member of the class, and update the comments. You also need to make the Graphics2D class accessible so add an import statement for it to Element.java:

import java.awt.Graphics2D;

You can now change the implementation of the paint() method in the SketchView class to:

public void paint(Graphics g) {
  Graphics2D g2D = (Graphics2D)g;                 // Get a 2D device context
  Iterator elements = theApp.getModel().getIterator();
  while(elements.hasNext())                       // Go through the list
    ((Element)elements.next()).draw(g2D);         // Get the next element to draw 
                                                  // itself
}

It's now quite a lot shorter as the element sets its own color in the graphics context and we don't need to store the reference to the element within the loop. We are ready to get back to the Element.Text class.

We must not forget to update the mouseDragged() method in the MouseHandler class in SketchView to use the new mechanism for drawing elements:

  public void mouseDragged(MouseEvent e) {
    last = e.getPoint();                                // Save cursor position
    if(button1Down)      {
      if(tempElement == null)                           // Is there an element?
        tempElement = createElement(start, last);       // No, so create one
      else  {
        tempElement.draw(g2D);                          // Yes – draw to erase it
        tempElement.modify(start, last);                // Now modify it
      }
      tempElement.draw(g2D);                          // and draw it
    }
  }

We can define the Element.Text class quite easily now. We will need five arguments for the constructor though – the font to be used for the string, the string to be displayed, the position where it is to be displayed, its color, and its bounding rectangle. To get a bounding rectangle for a string, we need access to a graphics context, so it will be easier to create the bounding rectangle before we create a text element.

Here's the class definition:

class Element {
  // Code that defines the base class...
  // Definitions for the other shape classes...

  // Class defining text element
  public static class Text extends Element  {
    public Text(Font font, String text, Point position, Color color,
                                        java.awt.Rectangle bounds) {
      super(color);
      this.font = font;
      this.position = position;
      this.text = text;
      this.bounds = bounds;
      this.bounds.setLocation(position.x, position.y – (int)bounds.getHeight());
    }

    public java.awt.Rectangle getBounds() {
      return bounds;
    }

    public void draw(Graphics2D g2D)  {
      g2D.setPaint(color);
      Font oldFont = g2D.getFont();                // Save the old font
      g2D.setFont(font);                          // Set the new font
      g2D.drawString(text, position.x, position.y);
      g2D.setFont(oldFont);                        // Restore the old font
    }

    public void modify(Point start, Point last) {
      // No code is required here, but we must supply a definition
    }

    private Font font;                             // The font to be used
    private String text;                           // Text to be displayed
    private Point position;                        // Position of the text
   java.awt.Rectangle bounds;                      // The bounding rectangle
  }
}

How It Works

In the constructor we pass the color to the superclass constructor, and store the other argument values in data members of the Element.Text class. When we create the bounding rectangle to pass to the constructor, the default reference point for the top-left corner of the rectangle will be (0, 0). This is not what we want for our text object so we have to modify it.

Click To expand

In the constructor, we adjust the coordinates of the top-left corner of the bounding rectangle relative to the point, position, where the string will be drawn. Remember that the point coordinates passed to the drawString() method correspond to the bottom-left corner of the first character in the string.

The code to draw text is not difficult – it's just different from drawing shapes. It amounts to calling setPaint() to set the text color, setting the font in the graphics context by calling its setFont()method and finally calling drawString() to display the text.

To work with text, we could do with a menu item and a toolbar button to set the element type to text, so let's deal with that next.

Try It Out – Adding the Text Menu Item

We'll add a textAction member to the SketchFrame class, so amend the declaration for the TypeAction members to:

private TypeAction lineAction, rectangleAction, circleAction,
JavaScript Editor
 Java Tutorials Free JavaScript Editor