001: /*************************************************************************
002: * *
003: * 1) This source code file, in unmodified form, and compiled classes *
004: * derived from it can be used and distributed without restriction, *
005: * including for commercial use. (Attribution is not required *
006: * but is appreciated.) *
007: * *
008: * 2) Modified versions of this file can be made and distributed *
009: * provided: the modified versions are put into a Java package *
010: * different from the original package, edu.hws; modified *
011: * versions are distributed under the same terms as the original; *
012: * and the modifications are documented in comments. (Modification *
013: * here does not include simply making subclasses that belong to *
014: * a package other than edu.hws, which can be done without any *
015: * restriction.) *
016: * *
017: * David J. Eck *
018: * Department of Mathematics and Computer Science *
019: * Hobart and William Smith Colleges *
020: * Geneva, New York 14456, USA *
021: * Email: eck@hws.edu WWW: http://math.hws.edu/eck/ *
022: * *
023: *************************************************************************/package edu.hws.jcm.awt;
024:
025: import java.awt.*;
026: import java.awt.event.*;
027: import java.util.Vector;
028:
029: /**
030: * The class MessagePopup represents a Window that pops up to display an error
031: * message. A MessagePopup object is created by a "source" component. If
032: * that component is contained in a Frame, then the popup will be a modal dialog
033: * box with that Parent. If the component is not in a Frame (or is null), then an
034: * independent Frame is used. The message box is popped up when reportError() is
035: * called. It is closed either when the user clicks the OK button,
036: * or if clearErrorMessage() is called.
037: */
038: public class MessagePopup implements ActionListener, ErrorReporter {
039: private String errorMessage;
040: private Controller errorSource;
041: private Component source;
042: private Window popup;
043:
044: /**
045: * Create a MessagePopup with the give source component. If source is null, then
046: * an independent window will always be used to show the error message.
047: */
048: public MessagePopup(Component source) {
049: this .source = source;
050: }
051:
052: /**
053: * Show the given message in a dialog box or independent window,
054: * depending on whether the source component is contained in
055: * a Frame or not.
056: *
057: * @param c The Controller that calls this method, or null if it is not called by a Controller.
058: * (The Controller, if any, will be notified when the error message is cleared.)
059: * @param message The message to display.
060: */
061: public void setErrorMessage(Controller c, String message) {
062: if (popup != null)
063: clearErrorMessage();
064: if (message == null)
065: return;
066: errorSource = c;
067: errorMessage = message;
068: Component parent = source;
069: while (parent != null && !(parent instanceof Frame))
070: parent = parent.getParent();
071: if (parent != null)
072: popup = new Dialog((Frame) parent, "Error Message", true); // modal dialog
073: else
074: popup = new Frame("Error Message"); // independent window
075: popup.setBackground(Color.white);
076: popup.add(new MC(message), BorderLayout.CENTER);
077: Panel buttonBar = new Panel();
078: buttonBar.setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 10));
079: Button OK = new Button(" OK ");
080: OK.addActionListener(this );
081: buttonBar.add(OK);
082: popup.add(buttonBar, BorderLayout.SOUTH);
083: popup.pack();
084: if (parent == null)
085: popup.setLocation(100, 80);
086: else
087: popup.setLocation(parent.getLocation().x + 50, parent
088: .getLocation().y + 30);
089: popup.addWindowListener(new WindowAdapter() {
090: public void windowClosing(WindowEvent evt) {
091: popup.dispose();
092: }
093: });
094: popup.show(); // make the dialog visible.
095: }
096:
097: /**
098: * Get the currently displayed error message. The return value is null if no error message is being displayed.
099: */
100: public String getErrorMessage() {
101: return errorMessage;
102: }
103:
104: /**
105: * Clear the error message and close the window. This can be
106: * called from outside this class. It is called automatically
107: * when the user clicks the OK button or close box of the window
108: * that displays the error message.
109: */
110: synchronized public void clearErrorMessage() {
111: if (popup == null)
112: return;
113: popup.dispose();
114: errorMessage = null;
115: if (errorSource != null)
116: errorSource.errorCleared();
117: errorSource = null;
118: popup = null;
119: }
120:
121: /**
122: * Respond when user clicks OK. This is not meant to be called directly.
123: */
124: public void actionPerformed(ActionEvent evt) {
125: clearErrorMessage();
126: }
127:
128: /**
129: * The nested class MC (Message Canvas) displays the message passed
130: * to it in the constructor. Unless the message is very short,
131: * it will be broken into multiple lines.
132: */
133: private static class MC extends Canvas {
134:
135: private String message; // A copy of the message
136:
137: // The following data is computed in makeStringList()
138:
139: private Vector messageStrings; // The message broken up into lines.
140: private int messageWidth; // The width in pixels of the message display.
141: private int messageHeight; // The height in pixels of the message display.
142: private Font font; // The font that will be used to display the message.
143: private int lineHeight; // The height of one line in that font.
144: private int fontAscent; // The font ascent of the font (disance from the
145:
146: // baseline to the top of a tall character.)
147:
148: /**
149: * Constructor: store the message.
150: *
151: * @param message message to store.
152: */
153: MC(String message) {
154: if (message == null)
155: this .message = ""; // this.message can't be null.
156: else
157: this .message = message;
158: }
159:
160: /**
161: * Return the message size, as determined by makeStringList(), allowing
162: * space for a border around the message.
163: *
164: * @return the message size.
165: */
166: public Dimension getPreferredSize() {
167: if (messageStrings == null)
168: makeStringList();
169: return new Dimension(messageWidth + 20, messageHeight + 20);
170: }
171:
172: /**
173: * Display the message using data stored in instance variables.
174: *
175: * @param g the Graphics context.
176: */
177: public void paint(Graphics g) {
178: if (messageStrings == null)
179: makeStringList();
180: int y = (getSize().height - messageHeight) / 2 + fontAscent;
181: if (y < fontAscent)
182: y = fontAscent;
183: int x = (getSize().width - messageWidth) / 2;
184: if (x < 0)
185: x = 0;
186: g.setFont(font);
187: for (int i = 0; i < messageStrings.size(); i++) {
188: g
189: .drawString((String) messageStrings
190: .elementAt(i), x, y);
191: y += lineHeight;
192: }
193: }
194:
195: /**
196: * Compute all the instance variables necessary for displaying
197: * the message. If the total width of the message in pixels
198: * would be more than 280, break it up into several lines.
199: */
200: private void makeStringList() {
201: messageStrings = new Vector();
202: font = new Font("Dialog", Font.PLAIN, 12);
203: FontMetrics fm = getFontMetrics(font);
204: lineHeight = fm.getHeight() + 3;
205: fontAscent = fm.getAscent();
206: int totalWidth = fm.stringWidth(message);
207: if (totalWidth <= 280) {
208: messageStrings.addElement(message);
209: messageWidth = 280;
210: messageHeight = lineHeight;
211: } else {
212: if (totalWidth > 1800)
213: messageWidth = Math.min(500, totalWidth / 6);
214: else
215: messageWidth = 300;
216: int actualWidth = 0;
217: String line = " ";
218: String word = "";
219: message += " "; // this forces word == "" after the following for loop ends.
220: for (int i = 0; i < message.length(); i++) {
221: if (message.charAt(i) == ' ') {
222: if (fm.stringWidth(line + word) > messageWidth + 8) {
223: messageStrings.addElement(line);
224: actualWidth = Math.max(actualWidth, fm
225: .stringWidth(line));
226: line = "";
227: }
228: line += word;
229: if (line.length() > 0)
230: line += ' ';
231: word = "";
232: } else {
233: word += message.charAt(i);
234: }
235: }
236: if (line.length() > 0) {
237: messageStrings.addElement(line);
238: actualWidth = Math.max(actualWidth, fm
239: .stringWidth(line));
240:
241: }
242: messageHeight = lineHeight * messageStrings.size()
243: - fm.getLeading();
244: messageWidth = Math.max(280, actualWidth);
245: }
246: }
247:
248: } // end nested class MC
249:
250: } // end class MessageDialog
|