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