Chapter 5

JFC

JFC is a collection of packages that provide GUI and graphics components:

awt = peer-based controls & event handling
swing = peerless controls
accessibility API
2D Graphics API
drag-and-drop API
These notes will focus on building GUIs using swing controls and awt event handling.

There are two types of GUI components (buttons, menus, windows, etc.): peer-based and peerless. A peer-based component delegates commands to an associated system component. Using peer-based components can lead to subtle but annoying differences in the look and feel of a GUI from one platform to another. A peerless component is just a picture of a control in a window. The behavior of the control is completely determined by the Java VM. JComponent is the base class of all peerless components. JFrame and JDialolg are the only peer-based swing components:

GUI Components

We can view a graphical user interface (GUI) as a tree of GUI components. Parent nodes are containers that hold their children. Leaf nodes are views and controls:

A container is a GUI component that holds other GUI components, including other containers! The two main types of containers are windows and panels. A window has a border, a panel doesn't. Panels are useful for grouping components within a container.

The two main types of windows are dialog boxes and frames. If we view a GUI as a tree, then the root of the tree would usually be a frame. In other words, a frame is usually the main window of the application. Dialog windows are child windows that appear when user input is required or when a message needs to be displayed to the user.

Menu bars, menus, toolbars, applets, and control panels are examples of special purpose windows.

There are two types of controls. Input controls receive user input, while output controls display program output.

Input Controls
   Button (JButton)
   Combo Box (JComboBox)
   List (JList)
   Menu (JMenu)
   Slider (JSlider)
   Text Field (JTextField)
   Text (JTextArea)
   Tree (JTree)
   Color Chooser (JColorChooser)
   File Chooser (JFileChooser)
   Table (JTable)

Output Controls
   Label (JLabel)
   Progress Bar (JProgressBar)
   Tool Tip (JToolTip)

GraphicsDemo

The basic application window is moveable, resizable, has a title bar, and minimize, maximize, and close buttons:

Structurally, an application window is a JPanel contained in a JFrame:

GraphicsDemo.java begins by importing the most commonly used JFC packages:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
The GraphicsDemo class extends MainJFrame, which is simply a closeable JFrame (we will describe this class in detail, later). The constructor sets the title, then adds a graphics panel to the content pane of the JFrame: public class GraphicsDemo extends util.MainJFrame {
   public GraphicsDemo() {
      setTitle("Graphics Demo");
      Container contentPane = getContentPane();
      JPanel panel = new GraphicsPanel();
      contentPane.add(panel);
   }
The main() method creates a GraphicsDemo instance, then calls its show() method. This method causes the window to display itself on the desktop and begin listening for user input events such as mouse and keyboard events:    public static void main(String[] args) {
      JFrame demo = new GraphicsDemo();
      demo.show();
   }
} // GraphicsDemo
The GraphicsPanel extends Swing's JPanel class. The programmer can implement a paintComponent() method to draw in the panel: class GraphicsPanel extends JPanel {
   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      // graphics code goes here
   }
}
JFrame Structure

A JFrame consists of four panes: root pane, layered pane, content pane, and glass pane:

Components are added to the content pane, although we will usually add a JPanel to the content pane and then add controls to the JPanel.

Graphical Contexts

A graphical context is an instance of the Graphics class. We can think of a graphical context as an abstraction of a graphics device. It provides a blank canvas (i.e., a bitmap) and tools for drawing curves, shapes, and text:

class Graphics {
   void drawLine(...) { ... }
   void drawOval(...) { ... }
   void drawString(...) { ... }
   // etc.
}
Each time a component, c, needs to be redrawn, a graphical context for c is created and passed to the component's update method: Graphics g = c.getGraphics();
c.update(g);
Component.update() erases the background of the window, then calls: c.paintComponent(g); We can force a call to Component.update() from within the program by calling: c.repaint(); The paintComponent() method is already defined for controls and containers. Programmers redefine paintComponent() for components to add text, images, and shapes to the graphical context.

Client Area

The client area is the region of a container excluding the borders and title bar. This is where client drawings actually appear. Points in the client area are identified by their coordinates in a coordinate system in which the positive x-axis runs across the top of the container and the positive y-axis runs down the left side of the container. The unit of measurement is the PIXEL.

Fonts

A font face name, or font name, has the form "FAMILY STYLE". For example:

"Helvetica Bold" We create a font object using the Font constructor: Font(FAMILY, STYLE, SIZE) FontDemo

FontPanel Class

class FontPanel extends JPanel {
   // Pre-defined logical font names (new and old):
   String[] fonts = {
      "SanSerif", "Serif", "Monospaced", "Dialog", "DialogInput",
      "Helvetica", "Courier", "ZapfDingbats"
   };
   // A few pre-defined styles:
   int[] styles = {
      Font.PLAIN, Font.ITALIC, Font.BOLD, Font.BOLD + Font.ITALIC
   };

   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      Font font;
      FontMetrics fm;
      int row = 10;
      int points = 10;
      for(int i = 0; i < fonts.length; i++) {
         int col = 1;
         int height = 0;
         for(int j = 0; j < styles.length; j++) {
            // use next font to display its name:
            font = new Font(fonts[i], styles[j], points);
            g.setFont(font);
            g.drawString(fonts[i], col, row);
            // update height and row:
            fm = g.getFontMetrics(font);
            height = Math.max(height, fm.getHeight());
            col += (fm.stringWidth(fonts[i]) + 2);
         }
         row += height + 2; // = next row
      }
   }
}

FontDemo Class public class FontDemo extends util.MainJFrame {

   public FontDemo() {
      setTitle("Font Demo");
      Container contentPane = getContentPane();
      contentPane.add(new FontPanel());
   }

   public static void main(String[] args) {
      FontDemo demo = new FontDemo();
      demo.show();
   }
}

Color

ColorDemo

ColorPanel Class

class ColorPanel extends JPanel {
   String[] message = {
      "Mercury", "Venus", "Earth", "Mars", "Jupiter",
      "Saturn", "Neptune", "Uranus", "Pluto"
   };
   java.util.Random generator = new java.util.Random();

   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      int column = 10, row = 10;
      int red = 255, green = 0, blue = 0;
      for(int i = 0; i < message.length; i++) {
         g.setColor(new Color(red, green, blue));
         red = generator.nextInt(256);
         green = generator.nextInt(256);
         blue = generator.nextInt(256);
         g.drawString(message[i], row, column);
         column += 20;
      }
   }
}

ColorDemo Class public class ColorDemo extends util.MainJFrame {
   public ColorDemo() {
      setTitle("Color Demo");
      // setBackground(Color.yellow);
      Container contentPane = getContentPane();
      contentPane.add(new ColorPanel());
   }

   public static void main(String[] args) {
      ColorDemo demo = new ColorDemo();
      demo.show();
   }
}

Shapes

A graphical context provides methods for drawing rectangles:

Here is the listing for this image:

class DrawDemoPanel extends JPanel {
   public DrawDemoPanel() {
      setBackground(Color.white);
   }
   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      int corner = 20;
      int size = 300;
      int dec = 20;
      g.setColor(Color.gray);
      g.fill3DRect(corner, corner, size, size, true);
      corner += dec;
      size -= 2 * dec;
      g.fill3DRect(corner, corner, size, size, false);
      g.setColor(Color.green);
      corner += dec;
      size -= 2 * dec;
      g.fillRect(corner, corner, size, size);
      g.setColor(Color.red);
      corner += dec;
      size -= 2 * dec;
      g.drawRect(corner, corner, size, size);
   }
}
A graphical context provides methods for drawing ovals:

Here is a listing for this image:

class DrawDemoPanel extends JPanel {
   public DrawDemoPanel() {
      setBackground(Color.white);
   }
   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.blue);
      g.fillOval(10, 10, getWidth() - 20, getHeight() - 20);
      g.setColor(Color.red);
      g.fillOval(40, 40, getWidth() - 80, getHeight() - 80);
      g.setColor(Color.yellow);
      g.fillOval(70, 70, getWidth() - 140, getHeight() - 140);
   }
}
Metrics

Metrics Demo

MetricPanel Class

public class MetricPanel extends JPanel {
   // client area metrics:
   protected int xUpperLeft, yUpperLeft, xLowerRight, yLowerRight;
   protected int clientHeight, clientWidth, xCenter, yCenter;
   // screen metrics:
   protected int screenHeight, screenWidth;
   protected int xScreenCenter, yScreenCenter;
   protected boolean drawAxes = false;
   public void setDrawAxes(boolean flag) {
      drawAxes = flag;
   }

   protected void updateMetrics() {
      Dimension d = getSize();
      Insets in = getInsets();
      clientWidth = d.width - in.right - in.left;
      clientHeight = d.height - in.bottom - in.top;
      xUpperLeft = in.right;
      yUpperLeft = in.top;
      xLowerRight = xUpperLeft + clientWidth;
      yLowerRight = yUpperLeft + clientHeight;
      xCenter = xUpperLeft + clientWidth/2;
      yCenter = yUpperLeft + clientHeight/2;

      Toolkit tk = Toolkit.getDefaultToolkit();
      d = tk.getScreenSize();
      screenHeight = d.height;
      screenWidth = d.width;
      xScreenCenter = screenWidth/2;
      yScreenCenter = screenHeight/2;
   }

   public void drawAxes(Graphics g) {
      g.setColor(Color.black);
      g.drawLine(xCenter, yUpperLeft, xCenter, yLowerRight);
      g.drawLine(xUpperLeft, yCenter, xLowerRight, yCenter);
   }

   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      updateMetrics();
      if (drawAxes) {
         drawAxes(g);
         g.setColor(Color.black);
         g.drawRect(xUpperLeft + 1, yUpperLeft + 1,
            clientWidth - 2, clientHeight - 2);
         g.drawOval(xCenter - 5, yCenter - 5, 10, 10);
         g.fillOval(xUpperLeft, yUpperLeft, 5, 5);
         g.fillOval(xLowerRight - 5, yLowerRight - 5, 5, 5);
      }
   }
}

GraphicsDemo Class public class MetricsDemo extends util.MainJFrame {
   public MetricsDemo() {
      setTitle("Metrics Demo");
      Container contentPane = getContentPane();
      MetricPanel mp = new MetricPanel();
      mp.setDrawAxes(true);
      contentPane.add(mp);
   }

   public static void main(String[] args) {
      JFrame demo = new MetricsDemo();
      demo.show();
   }
}

Graphing

Functors

interface Functor {
   double apply(double x);
}

class SineFunctor implements Functor {
   public double apply(double x) {
      return Math.sin(x);
   }
}

class SquareFunctor implements Functor {
   public double apply(double x) {
      return x * x;
   }
}

GraphPanel Class class GraphPanel extends MetricPanel {
   Functor fun = null;
   private int xRatio = 100;
   private int yRatio = 100;
   public void setRatio(int x, int y) {
      xRatio = x;
      yRatio = y;
   }
   public void setFunctor(Functor f) {
      fun = f;
   }

   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      drawAxes(g);
      if (fun != null) {
         for(int xc = 0; xc < clientWidth; xc++) {
            double x = (double)(xc - xCenter)/(double)xRatio;
            double y = fun.apply(x);
            int yc = yCenter - (int)(y * yRatio);
            g.fillOval(xc - 1, yc - 1, 3, 3);
         }
      }
   }
}

GraphDemo Class public class GraphDemo extends util.MainJFrame {
   public GraphDemo() {
      setTitle("Graph Demo");
      Container contentPane = getContentPane();
      GraphPanel gp = new GraphPanel();
      gp.setFunctor(new SineFunctor());
      gp.setRatio(30, 100);
      contentPane.add(gp);
   }
   public static void main(String[] args) {
      JFrame demo = new GraphDemo();
      demo.show();
   }
}
PolyDemo

PolyPanel Class

class PolyPanel extends MetricPanel {
   private int red = 255, green = 0, blue = 0;
   private int sides = 5;
   public void setSides(int s) {
      sides = s;
   }
   public void setColor(int r, int g, int b) {
      red = r;
      green = g;
      blue = b;
   }

   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      updateMetrics();
      g.setColor(new Color(red, green, blue));
      Polygon p = new Polygon();
      double radius = Math.min(clientHeight, clientWidth)/2;
      double angle = 2 * Math.PI/sides;
      for(int i = 0; i < sides; i++)
         p.addPoint(
            (int)(xCenter + radius * Math.cos(i * angle)),
            (int)(yCenter + radius * Math.sin(i * angle)));
      g.drawPolygon(p);
      g.setColor(Color.black);
      String s = "# sides = " + sides;
      FontMetrics fm = g.getFontMetrics();
      int w = fm.stringWidth(s);
      int h = fm.getHeight();
      g.drawString(s, xCenter - w/2, yCenter - h/2);
   }
}

PolyDemo Class public class PolyDemo extends util.MainJFrame {
   public PolyDemo() {
      setTitle("Poly Demo");
      Container contentPane = getContentPane();
      PolyPanel pp = new PolyPanel();
      pp.setColor(255, 0, 255);
      pp.setSides(9);
      contentPane.add(pp);
   }
   public static void main(String[] args) {
      JFrame demo = new PolyDemo();
      demo.show();
   }
}
Event Handling

Activating a control-- pushing a button, adjusting a scroll bar, typing in a text box, selecting an item from a list -fires a semantic event. The control is the source of the event. Semantic events are actually sequences of low-level events. For example, the adjusting-the-scroll-bar event is a sequence of mouse events: moving the mouse over the scroll bar's thumb tab, holding down the left mouse button, moving the mouse up or down, then releasing the left mouse button. Semantic and low-level events are represented by objects in Java:

AWTEvent
   semantic events
      ActionEvent (button press, etc.)
      AdjustmentEvent (fired by adjustable components)
      ItemEvent (fired by selectable components)
      TextEvent (fired by text components)
   ComponentEvent (i.e., low-level events)
      WindowEvent
      FocusEvent
      InputEvent
         MouseEvent
         KeyEvent
A listener is an object that contains one or more event handlers. An event handler is a method that gets called when a component fires a certain kind of event. EventListener
   ActionListener
   AdjustmentListener
   ComponentListener
   ContainerListener
   FocusListener
   ItemListener
   KeyListener
   MouseListener
   MouseMotionListener
   TextListener
   WindowListener
These are all interfaces: interface ActionListener extends EventListener {
    public void actionPerformed(ActionEvent e);
}
Button Demo

ButtonPanel Class

class ButtonPanel extends JPanel implements ActionListener {

   public ButtonPanel() {
      JButton rb = new JButton("Red");
      JButton bb = new JButton("Blue");
      JButton gb = new JButton("Green");
      rb.addActionListener(this);
      gb.addActionListener(this);
      bb.addActionListener(this);
      add(rb);
      add(bb);
      add(gb);
   }

   public void actionPerformed(ActionEvent ae) {
      String cmmd = ae.getActionCommand();
      if (cmmd.equals("Green")) {
         setBackground(Color.green);
      } else if (cmmd.equals("Blue")) {
         setBackground(Color.blue);
      } else {
         setBackground(Color.red);
      }
      repaint();
   }
}

ButtonDemo Class public class ButtonDemo extends util.MainJFrame {
   public ButtonDemo() {
      setTitle("Button Demo");
      Container contentPane = getContentPane();
      contentPane.add(new ButtonPanel());
   }
   public static void main(String[] args) {
      JFrame demo = new ButtonDemo();
      demo.show();
   }
}
Mouse Drawing

MousePanel Class

class MousePanel extends JPanel {
   private java.util.List drawing = new java.util.Vector();
   private int brushWidth = 3;
   private int red = 255, green = 0, blue = 0;
   class MouseMotionHandler implements MouseMotionListener {...}
   class MouseEventHandler extends MouseAdapter {...}

   public MousePanel() {
      addMouseListener(new MouseEventHandler());
      addMouseMotionListener(new MouseMotionHandler());
   }

   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(new Color(red, green, blue));
      for(int i = 0; i < drawing.size(); i++) {
         Point p = (Point)drawing.get(i);
         g.fillOval(p.x, p.y, brushWidth, brushWidth);
      }
   }
}

Mouse Listeners class MouseMotionHandler implements MouseMotionListener {
   public void mouseDragged(MouseEvent e) {
      setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
      Point p = e.getPoint();
      drawing.add(p);
      // to prevent flicker, only repaint a small rectangle
      repaint(p.x, p.y, brushWidth, brushWidth);
   }
}

class MouseEventHandler extends MouseAdapter {
   public void mouseClicked(MouseEvent e) {
      if (e.getClickCount() > 1) {
         drawing.clear();
         repaint(); // repaint entire window
      }
   }
   public void mouseReleased(MouseEvent e) {
      setCursor(Cursor.getDefaultCursor());
   }
}

Draw Class public class Draw extends util.MainJFrame {
   public Draw() {
      setTitle("Draw");
      Container contentPane = getContentPane();
      contentPane.add(new MousePanel());
   }
   public static void main(String[] args) {
      JFrame demo = new Draw();
      demo.show();
   }
}
util.MainJFrame package util;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class MainJFrame extends JFrame {

   protected int screenHeight;
   protected int screenWidth;
   protected String title = "Main Window";

   private class Terminator extends WindowAdapter {
      public void windowClosing(WindowEvent e) {
         System.exit(0);
      }
   }

   public MainJFrame() {
      addWindowListener(new Terminator());
      setTitle(title);
      Toolkit tk = Toolkit.getDefaultToolkit();
      Dimension d = tk.getScreenSize();
      screenHeight = d.height;
      screenWidth = d.width;
      setSize(screenWidth/2, screenHeight/2);
      setLocation(screenWidth/4, screenHeight/4);
   }

   // test harness
   public static void main(String[] args) {
      MainJFrame frame = new MainJFrame();
      frame.show();
   }
}

Layout

Flow Layout

The default layout manager for JDialog is flow layout. This manager adds controls from left to right, starting a new row each time it runs out of room. The main feature of flow layout is that it attempts to center align the components.

Here is a simple keypad created using the flow layout manager:

KeyPadPanel Class

class KeyPadPanel extends JPanel {
   JTextField display = new JTextField("555-1234", 10);
   JButton b1 = new JButton("1");
   JButton b2 = new JButton("2");
   JButton b3 = new JButton("3");
   JButton b4 = new JButton("4");
   JButton b5 = new JButton("5");
   JButton b6 = new JButton("6");
   JButton b7 = new JButton("7");
   JButton b8 = new JButton("8");
   JButton b9 = new JButton("9");
   JButton b0 = new JButton("0");
   JButton bSharp = new JButton("#");
   JButton bStar = new JButton("*");

   public KeyPadPanel() {
      add(display);
      add(b1);
      add(b2);
      add(b3);
      add(b4);
      add(b5);
      add(b6);
      add(b7);
      add(b8);
      add(b9);
      add(bSharp);
      add(b0);
      add(bStar);
   }
}

KeyPad Class public class KeyPad extends util.MainJFrame {
   public KeyPad() {
      setTitle("Key Pad");
      Container contentPane = getContentPane();
      contentPane.add(new KeyPadPanel());
   }
   public static void main(String[] args) {
      JFrame demo = new KeyPad();
      demo.show();
   }
}
Grid and Border Layout

KeyPadPanel Constructor

   public KeyPadPanel() {
      setLayout(new BorderLayout());
      add(display, "North");
      JPanel keys = new JPanel();
      keys.setLayout(new GridLayout(4, 3));
      keys.add(b1);
      // etc.
      add(keys, "South");
   }
If we add keys without specifying the region, then it will be added to the central region and the east, west, and south regions disappear.    public KeyPadPanel() {
      setLayout(new BorderLayout());
      add(display, "North");
      JPanel keys = new JPanel();
      keys.setLayout(new GridLayout(4, 3));
      keys.add(b1);
      // etc.
      add(keys);
   }
 
 

Paneled Controls
 
 

KeyPadPanel Constructor

   public KeyPadPanel() {
      setLayout(new BorderLayout());
      JPanel p = new JPanel();
      p.add(display);
      add(p, "North");
      JPanel keys = new JPanel();
      keys.setLayout(new GridLayout(4, 3));
      p = new JPanel();
      p.add(b1);
      keys.add(p);
      p = new JPanel();
      p.add(b2);
      keys.add(p);
      // etc.
      add(keys);
   }
Controls

Text Fields and Labels

Example: Temperature Converter

If the user mistakenly enters a non-numeric temperature (e.g. "zero"), then an error dialog box appears:

TempConverter Class

public class TempConverter extends util.MainJFrame {
   public TempConverter() {
      setTitle("Temperature Converter");
      Container contentPane = getContentPane();
      JPanel panel = new TempPanel();
      contentPane.add(panel);
   }
   public static void main(String[] args) {
      JFrame demo = new TempConverter();
      demo.show();
   }
}
TempPanel Class

A temperature panel is an action listener for the two text fields it contains:

class TempPanel extends JPanel implements ActionListener {
   JTextField cenField = new JTextField("0", 20);
   JTextField farField = new JTextField("32", 20);
Notice that the JTextField constructor allows programmers to specify the initial string displayed plus the field width. In our example, users will only be able to see 20 characters of the text through the text field window.

The layout adds a paneled label and a paneled text field to rows one and two of a grid layout panel. This happens twice, once for each text field. (Setting the background color to white makes it easier to see the paneled controls.)

   public TempPanel() {
      setBackground(Color.white);
      JPanel p = new JPanel();
      p.setLayout(new GridLayout(2, 1));
      JPanel p1 = new JPanel();
      p1.add(new JLabel("Fahrenheit"));
      p.add(p1);
      p1 = new JPanel();
      p1.add(farField);
      p.add(p1);
      add(p);

      p = new JPanel();
      p.setLayout(new GridLayout(2, 1));
      p1 = new JPanel();
      p1.add(new JLabel("centigrade"));
      p.add(p1);
      p1 = new JPanel();
      p1.add(cenField);
      p.add(p1);
      add(p);

      cenField.setHorizontalAlignment(JTextField.CENTER);
      farField.setHorizontalAlignment(JTextField.CENTER);

      cenField.addActionListener(this);
      farField.addActionListener(this);
   }

The action event handler will be called when the user types a newline into either text field. The handler uses the ActionEvent.getSource() method to determine which text field was changed. The string is extracted from the text field, converted into a float, the corresponding temperature is computed, converted into a string, then displayed in the corresponding text field:    public void actionPerformed(ActionEvent e) {
      try {
         String digits;
         float temp1, temp2;
         if (e.getSource() == cenField) {
            digits = cenField.getText();
            temp1 = Float.valueOf(digits).floatValue();
            temp2 = (float)(1.8 * temp1 + 32);
            farField.setText("" + temp2);
         } else if (e.getSource() == farField) {
            digits = farField.getText();
            temp1 = Float.valueOf(digits).floatValue();
            temp2 = (float)(5.0/9.0 * (temp1 - 32));
            cenField.setText("" + temp2);
         }
      }
The static Float.valueOf() method throws a number format exception if the given string cannot be construed as a number. Swing provides a useful JOptionPane class with static methods for creating and displaying a variety of simple dialog boxes. Our exception handler creates and displays an error message dialog. When the user clicks the dialog's "OK" button, the dialog box disappears and control returns to the handler, which resets the text fields to sensible values:        catch (NumberFormatException x) {
         JOptionPane.showMessageDialog(null,
            "temperatures must be numbers",
            "OOPS!",
            JOptionPane.ERROR_MESSAGE);
         farField.setText("32");
         cenField.setText("0");
      }
   }
} // TempPanel
Lists and Sliders

Dialog Boxes

Let's add a modal dialog box to the draw program:

Here's what the dialog looks like:

Mouse Panel

class MousePanel extends JPanel {
   private java.util.List drawing = new java.util.Vector();
   public int brushWidth = 3;
   public int red = 255, green = 0, blue = 0;
   ...
}
Draw public class Draw extends util.MainJFrame {

   public MousePanel panel = new MousePanel();

   public Draw() {
      setTitle("Draw");
      Container contentPane = getContentPane();
      contentPane.add(panel);
      addKeyListener(new KeyboardHandler());
   }

   class KeyboardHandler extends KeyAdapter { ... }

   public static void main(String[] args) {
      JFrame demo = new Draw();
      demo.show();
   }
}

The Keyboard Handler class KeyboardHandler extends KeyAdapter {
      public void keyPressed(KeyEvent ke) {
         if (getKeyCode() == KeyEvent.VK_S) {
            DrawDialog d =
               new DrawDialog(
                  Draw.this,
                  panel.red,
                  panel.green,
                  panel.blue,
                  panel.brushWidth);
            d.show();
            panel.red = d.red;
            panel.green = d.green;
            panel.blue = d.blue;
            panel.brushWidth = d.pen;
            repaint();
         }
      }
   }
Draw Dialog public class DrawDialog extends JDialog implements ActionListener {

   private TextField redField = new TextField("255", 20);
   private TextField greenField = new TextField("0", 20);
   private TextField blueField = new TextField("0", 20);
   private TextField penField = new TextField("0", 20);
   private JButton okButton = new JButton("OK");
   private JButton cancelButton = new JButton("Cancel");
   public int red = 255, green = 0, blue = 0, pen = 5;
   public DrawDialog(JFrame parent, int r, int g, int b, int pn) {
      ...
   }
   public void actionPerformed(ActionEvent ae) { ... }
}

Constructor    public DrawDialog(JFrame parent, int r, int g, int b, int pn) {
      super(parent, "DrawDialog", true);
      setSize(300, 400);
      red = r;
      blue = b;
      green = g;
      pen = pn;
      redField.setText("" + red);
      greenField.setText("" + green);
      blueField.setText("" + blue);
      penField.setText("" + pen);

      JPanel dialogPanel = new JPanel();
      dialogPanel.setLayout(new GridLayout(4, 1));
      JPanel p, p1;

      p = new JPanel();
      p.setLayout(new GridLayout(1, 2));
      p1 = new JPanel();
      p1.add(new JLabel("Red"));
      p.add(p1);
      p1 = new JPanel();
      p1.add(redField);
      p.add(p1);
      //redField.setHorizontalAlignment(JTextField.CENTER);
      redField.addActionListener(this);
      dialogPanel.add(p);

      // same for green, blue, and pen

      p = new JPanel();
      p.add(okButton);
      okButton.addActionListener(this);
      p.add(cancelButton);
      cancelButton.addActionListener(this);

      Container contentPane = getContentPane();
      contentPane.add("Center", dialogPanel);
      contentPane.add("South", p);
   }

Data Exchange    public void actionPerformed(ActionEvent ae) {
      try { // DDX
         String digits;
         if (ae.getSource() == okButton) {
            digits = redField.getText();
            red = Integer.valueOf(digits).intValue();
            digits = greenField.getText();
            green = Integer.valueOf(digits).intValue();
            digits = blueField.getText();
            blue = Integer.valueOf(digits).intValue();
            digits = penField.getText();
            pen = Integer.valueOf(digits).intValue();
         }
      } catch (NumberFormatException x) {
         JOptionPane.showMessageDialog(null,
         "entries must be integers",
         "OOPS!",
         JOptionPane.ERROR_MESSAGE);
      }
      setVisible(false);
   } // actionPerformed
}
Modeless Dialog

Now let's make the Draw Dialog modeless. Here's the only change to the constructor:

public DrawDialog(JFrame parent, int r, int g, int b, int pn) {
   super(parent, "DrawDialog", false);
   // etc.
}
The button handler must update it's parent's panel: public void actionPerformed(ActionEvent ae) {
      try { // DDX
         String digits;
         if (ae.getSource() == okButton) {
            Draw parent = (Draw)getParent();
            MousePanel panel = parent.panel;
            digits = redField.getText();
            panel.red = Integer.valueOf(digits).intValue();
            digits = greenField.getText();
            panel.green = Integer.valueOf(digits).intValue();
            digits = blueField.getText();
            panel.blue = Integer.valueOf(digits).intValue();
            digits = penField.getText();
            panel.brushWidth = Integer.valueOf(digits).intValue();
            panel.repaint();
         } else {
            setVisible(false);
         }
      } catch (NumberFormatException x) {
         JOptionPane.showMessageDialog(null,
         "entries must be integers",
         "OOPS!",
         JOptionPane.ERROR_MESSAGE);
      }
   } // actionPerformed
}