Yes / No Panel
/* * 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); } }