Swing Components Java

/*
 * Copyright (c) 2004 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 3nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose,
 * including teaching and use in open-source projects.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book, 
 * please visit http://www.davidflanagan.com/javaexamples3.
 */
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Alignment {
  /** This private constructor prevents anyone from instantiating us */
  private Alignment() {
  };
  // The following three constants are the only instances of this class
  public static final Alignment LEFT = new Alignment();
  public static final Alignment CENTER = new Alignment();
  public static final Alignment RIGHT = new Alignment();
}
/**
 * A custom component that displays multiple lines of text with specified
 * margins and alignment.
 */
class MultiLineLabel extends JComponent {
  // User-specified properties
  protected String label; // The label, not broken into lines
  protected int margin_width; // Left and right margins
  protected int margin_height; // Top and bottom margins
  protected Alignment alignment; // The alignment of the text.
  // Computed state values
  protected int num_lines; // The number of lines
  protected String[] lines; // The label, broken into lines
  protected int[] line_widths; // How wide each line is
  protected int max_width; // The width of the widest line
  protected int line_height; // Total height of the font
  protected int line_ascent; // Font height above baseline
  protected boolean measured = false; // Have the lines been measured?
  // Here are five versions of the constructor.
  public MultiLineLabel(String label, int margin_width, int margin_height, Alignment alignment) {
    this.label = label; // Remember all the properties.
    this.margin_width = margin_width;
    this.margin_height = margin_height;
    this.alignment = alignment;
    newLabel(); // Break the label up into lines.
  }
  public MultiLineLabel(String label, int margin_width, int margin_height) {
    this(label, margin_width, margin_height, Alignment.LEFT);
  }
  public MultiLineLabel(String label, Alignment alignment) {
    this(label, 10, 10, alignment);
  }
  public MultiLineLabel(String label) {
    this(label, 10, 10, Alignment.LEFT);
  }
  public MultiLineLabel() {
    this("");
  }
  // Methods to set and query the various attributes of the component.
  // Note that some query methods are inherited from the superclass.
  public void setLabel(String label) {
    this.label = label;
    newLabel(); // Break the label into lines.
    repaint(); // Request a redraw.
    measured = false; // Note that we need to measure lines.
    invalidate(); // Tell our containers about this
  }
  public void setAlignment(Alignment a) {
    alignment = a;
    repaint();
  }
  public void setMarginWidth(int mw) {
    margin_width = mw;
    repaint();
  }
  public void setMarginHeight(int mh) {
    margin_height = mh;
    repaint();
  }
  // Override this property setter method because we need to remeasure
  public void setFont(Font f) {
    super.setFont(f); // Tell our superclass about the new font.
    repaint(); // Request a redraw.
    measured = false; // Note that we need to remeasure lines.
    invalidate(); // Tell our containers about new size
  }
  // Property getter methods.
  public String getLabel() {
    return label;
  }
  public Alignment getAlignment() {
    return alignment;
  }
  public int getMarginWidth() {
    return margin_width;
  }
  public int getMarginHeight() {
    return margin_height;
  }
  /**
   * This method is called by a layout manager when it wants to know how big
   * we'd like to be.
   */
  public Dimension getPreferredSize() {
    if (!measured)
      measure();
    return new Dimension(max_width + 2 * margin_width, num_lines * line_height + 2 * margin_height);
  }
  /**
   * This method is called when the layout manager wants to know the bare
   * minimum amount of space we need to get by.
   */
  public Dimension getMinimumSize() {
    return getPreferredSize();
  }
  /**
   * This method draws the component. Note that it handles the margins and the
   * alignment, but that it doesn't have to worry about the color or font--the
   * superclass takes care of setting those in the Graphics object we're passed.
   */
  public void paintComponent(Graphics g) {
    int x, y;
    Dimension size = this.getSize();
    if (!measured)
      measure();
    y = line_ascent + (size.height - num_lines * line_height) / 2;
    for (int i = 0; i < num_lines; i++, y += line_height) {
      if (alignment == Alignment.LEFT)
        x = margin_width;
      else if (alignment == Alignment.CENTER)
        x = (size.width - line_widths[i]) / 2;
      else
        x = size.width - margin_width - line_widths[i];
      g.drawString(lines[i], x, y);
    }
  }
  /**
   * This internal method breaks a specified label up into an array of lines. It
   * uses the StringTokenizer utility class.
   */
  protected synchronized void newLabel() {
    StringTokenizer t = new StringTokenizer(label, "\n");
    num_lines = t.countTokens();
    lines = new String[num_lines];
    line_widths = new int[num_lines];
    for (int i = 0; i < num_lines; i++)
      lines[i] = t.nextToken();
  }
  /**
   * This internal method figures out how the font is, and how wide each line of
   * the label is, and how wide the widest line is.
   */
  protected synchronized void measure() {
    FontMetrics fm = this.getFontMetrics(this.getFont());
    line_height = fm.getHeight();
    line_ascent = fm.getAscent();
    max_width = 0;
    for (int i = 0; i < num_lines; i++) {
      line_widths[i] = fm.stringWidth(lines[i]);
      if (line_widths[i] > max_width)
        max_width = line_widths[i];
    }
    measured = true;
  }
}
class AnswerEvent extends java.util.EventObject {
  public static final int YES = 0, NO = 1, CANCEL = 2; // Button constants
  protected int id; // Which button was pressed?
  public AnswerEvent(Object source, int id) {
    super(source);
    this.id = id;
  }
  public int getID() {
    return id;
  } // Return the button
}
interface AnswerListener extends java.util.EventListener {
  public void yes(AnswerEvent e);
  public void no(AnswerEvent e);
  public void cancel(AnswerEvent e);
}
/**
 * This JavaBean displays a multi-line message and up to three buttons. It fires
 * an AnswerEvent when the user clicks on one of the buttons
 */
public class YesNoPanel extends JPanel {
  // Properties of the bean.
  protected String messageText; // The message to display
  protected Alignment alignment; // The alignment of the message
  protected String yesLabel; // Text for the yes, no, & cancel buttons
  protected String noLabel;
  protected String cancelLabel;
  // Internal components of the panel
  protected MultiLineLabel message;
  protected JPanel buttonbox;
  protected JButton yes, no, cancel;
  /** The no-argument bean constructor, with default property values */
  public YesNoPanel() {
    this("Your\nMessage\nHere");
  }
  public YesNoPanel(String messageText) {
    this(messageText, Alignment.LEFT, "Yes", "No", "Cancel");
  }
  /** A constructor for programmers using this class "by hand" */
  public YesNoPanel(String messageText, Alignment alignment, String yesLabel, String noLabel,
      String cancelLabel) {
    // Create the components for this panel
    setLayout(new BorderLayout(15, 15));
    // Put the message label in the middle of the window.
    message = new MultiLineLabel(messageText, 20, 20, alignment);
    message.setOpaque(false); // allow background color to show through
    add(message, BorderLayout.CENTER);
    // Create a panel for the Panel buttons and put it at the bottom
    // of the Panel. Specify a FlowLayout layout manager for it.
    buttonbox = new JPanel();
    buttonbox.setLayout(new FlowLayout(FlowLayout.CENTER, 25, 15));
    buttonbox.setOpaque(false); // allow background color to show through
    add(buttonbox, BorderLayout.SOUTH);
    // Create each specified button, specifying the action listener
    // and action command for each, and adding them to the buttonbox
    yes = new JButton(); // Create buttons
    no = new JButton();
    cancel = new JButton();
    // Add the buttons to the button box
    buttonbox.add(yes);
    buttonbox.add(no);
    buttonbox.add(cancel);
    // Register listeners for each button
    yes.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fireEvent(new AnswerEvent(YesNoPanel.this, AnswerEvent.YES));
      }
    });
    no.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fireEvent(new AnswerEvent(YesNoPanel.this, AnswerEvent.NO));
      }
    });
    cancel.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fireEvent(new AnswerEvent(YesNoPanel.this, AnswerEvent.CANCEL));
      }
    });
    // Now call property setter methods to set the message and button
    // components to contain the right text
    setMessageText(messageText);
    setAlignment(alignment);
    setYesLabel(yesLabel);
    setNoLabel(noLabel);
    setCancelLabel(cancelLabel);
  }
  // Methods to query all of the bean properties.
  public String getMessageText() {
    return messageText;
  }
  public Alignment getAlignment() {
    return alignment;
  }
  public String getYesLabel() {
    return yesLabel;
  }
  public String getNoLabel() {
    return noLabel;
  }
  public String getCancelLabel() {
    return cancelLabel;
  }
  public Font getMessageFont() {
    return message.getFont();
  }
  public Color getMessageColor() {
    return message.getForeground();
  }
  public Font getButtonFont() {
    return yes.getFont();
  }
  // Methods to set all of the bean properties.
  public void setMessageText(String messageText) {
    this.messageText = messageText;
    message.setLabel(messageText);
  }
  public void setAlignment(Alignment alignment) {
    this.alignment = alignment;
    message.setAlignment(alignment);
  }
  public void setYesLabel(String l) {
    yesLabel = l;
    yes.setText(l);
    yes.setVisible((l != null) && (l.length() > 0));
  }
  public void setNoLabel(String l) {
    noLabel = l;
    no.setText(l);
    no.setVisible((l != null) && (l.length() > 0));
  }
  public void setCancelLabel(String l) {
    cancelLabel = l;
    cancel.setText(l);
    cancel.setVisible((l != null) && (l.length() > 0));
  }
  public void setMessageFont(Font f) {
    message.setFont(f);
  }
  public void setMessageColor(Color c) {
    message.setForeground(c);
  }
  public void setButtonFont(Font f) {
    yes.setFont(f);
    no.setFont(f);
    cancel.setFont(f);
  }
  /** This field holds a list of registered ActionListeners. */
  protected List listeners = new ArrayList();
  /** Register an action listener to be notified when a button is pressed */
  public void addAnswerListener(AnswerListener l) {
    listeners.add(l);
  }
  /** Remove an Answer listener from our list of interested listeners */
  public void removeAnswerListener(AnswerListener l) {
    listeners.remove(l);
  }
  /** Send an event to all registered listeners */
  public void fireEvent(AnswerEvent e) {
    // Make a copy of the list and fire the events using that copy.
    // This means that listeners can be added or removed from the original
    // list in response to this event.
    Object[] copy = listeners.toArray();
    for (int i = 0; i < copy.length; i++) {
      AnswerListener listener = (AnswerListener) copy[i];
      switch (e.getID()) {
      case AnswerEvent.YES:
        listener.yes(e);
        break;
      case AnswerEvent.NO:
        listener.no(e);
        break;
      case AnswerEvent.CANCEL:
        listener.cancel(e);
        break;
      }
    }
  }
  /** A main method that demonstrates the class */
  public static void main(String[] args) {
    // Create an instance of YesNoPanel, with title and message specified:
    YesNoPanel p = new YesNoPanel("Do you really want to quit?");
    // Register an action listener for the Panel. This one just prints
    // the results out to the console.
    p.addAnswerListener(new AnswerListener() {
      public void yes(AnswerEvent e) {
        System.exit(0);
      }
      public void no(AnswerEvent e) {
        System.out.println("No");
      }
      public void cancel(AnswerEvent e) {
        System.out.println("Cancel");
      }
    });
    JFrame f = new JFrame();
    f.getContentPane().add(p);
    f.pack();
    f.setVisible(true);
  }
}