001: /*
002: #IFNDEF ALT_LICENSE
003: ThinWire(R) RIA Ajax Framework
004: Copyright (C) 2003-2007 Custom Credit Systems
005:
006: This library is free software; you can redistribute it and/or modify it under
007: the terms of the GNU Lesser General Public License as published by the Free
008: Software Foundation; either version 2.1 of the License, or (at your option) any
009: later version.
010:
011: This library is distributed in the hope that it will be useful, but WITHOUT ANY
012: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013: PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014:
015: You should have received a copy of the GNU Lesser General Public License along
016: with this library; if not, write to the Free Software Foundation, Inc., 59
017: Temple Place, Suite 330, Boston, MA 02111-1307 USA
018:
019: Users who would rather have a commercial license, warranty or support should
020: contact the following company who invented, built and supports the technology:
021:
022: Custom Credit Systems, Richardson, TX 75081, USA.
023: email: info@thinwire.com ph: +1 (888) 644-6405
024: http://www.thinwire.com
025: #ENDIF
026: [ v1.2_RC2 ]
027: */
028: package thinwire.ui;
029:
030: import java.util.ArrayList;
031: import java.util.List;
032:
033: import thinwire.ui.event.ActionEvent;
034: import thinwire.ui.event.ActionListener;
035: import thinwire.ui.style.Font;
036:
037: /**
038: * A <code>MessageBox</code> displays a message (or a component) and allows a
039: * user to respond.
040: * <p>
041: * <b>Example:</b> <br>
042: * <img src="doc-files/MessageBox-1.png"> <br>
043: *
044: * <pre>
045: * MessageBox.confirm("resources/ngLF/info.png", "ThinWire",
046: * "Get ready for ThinWire");
047: * </pre>
048: *
049: * </p>
050: * <p>
051: * <b>Keyboard Navigation:</b><br>
052: * <table border="1">
053: * <tr>
054: * <td>KEY</td>
055: * <td>RESPONSE</td>
056: * <td>NOTE</td>
057: * </table>
058: * </p>
059: *
060: * @author Joshua J. Gertzen
061: */
062: public final class MessageBox {
063: private static final int TEXT_LINE_HEIGHT = 16;
064: private static final int TEXT_CHAR_WIDTH = 6;
065: private static final int ICON_SIZE = 32;
066: private static final int SPACING = 5;
067: private static final int MIN_BUTTON_HEIGHT = 25;
068: private static final int MIN_BUTTON_WIDTH = 40;
069: private static final int DEFAULT_COMPONENT_HEIGHT = 40;
070: private static final int DEFAULT_COMPONENT_WIDTH = 150;
071: private static final Application.Local<List<MessageBox>> messageBoxStack = new Application.Local<List<MessageBox>>() {
072: protected List<MessageBox> initialValue() {
073: return new ArrayList<MessageBox>();
074: }
075: };
076:
077: private Dialog dialog;
078: private String title = "";
079: private String icon = "";
080: private String text = "";
081: private Component component;
082: private String buttons;
083: private int buttonId;
084: private boolean hasButtons;
085:
086: /**
087: * Display a dialog window without buttons.<p>
088: *
089: * Adds the MessageBox to the MessageBox stack on the application
090: * instance.<br>
091: *
092: * This method will not block.<br>
093: *
094: * For multiple line messages, separate the lines with "\n".
095: *
096: * @param message The dialog message, a \n separated sequence of lines.
097: */
098: public static void show(String message) {
099: MessageBox box = new MessageBox();
100: box.setText(message);
101: box.show();
102: }
103:
104: /**
105: * Display a dialog window without buttons.<p>
106: *
107: * Adds the MessageBox to the MessageBox stack on the application
108: * instance.<br>
109: *
110: * This method will not block.<br>
111: *
112: * For multiple line messages, separate the lines with "\n".
113: *
114: * @param title the dialog title
115: * @param message The dialog message, a \n separated sequence of lines.
116: */
117: public static void show(String title, String message) {
118: MessageBox box = new MessageBox();
119: box.setTitle(title);
120: box.setText(message);
121: box.show();
122: }
123:
124: /**
125: * Display a dialog window without buttons.<p>
126: *
127: * Adds the MessageBox to the MessageBox stack on the application
128: * instance.<br>
129: *
130: * This method will not block.<br>
131: *
132: * For multiple line messages, separate the lines with "\n".
133: *
134: * @param icon name of the image file without path info or file extension.
135: * @param title the dialog title
136: * @param message The dialog message, a \n separated sequence of lines.
137: */
138: public static void show(String icon, String title, String message) {
139: MessageBox box = new MessageBox();
140: box.setTitle(title);
141: box.setIcon(icon);
142: box.setText(message);
143: box.show();
144: }
145:
146: /**
147: * Display a dialog window with an OK button and message.<p>
148: *
149: * Sample usage:
150: * <pre>
151: * MessageBox.confirm("Invalid Return by Time\nInvalid Zip Code");
152: * </pre>
153: *
154: * For multiple line messages, separate the lines with "\n".
155: *
156: * @param message The dialog message, a \n separated sequence of lines.
157: */
158: public static void confirm(String message) {
159: confirm(null, null, message, null);
160: }
161:
162: /**
163: * Display a dialog window with an OK button, title and message.<p>
164: *
165: * Sample usage:
166: * <pre>
167: * MessageBox.confirm("Operator Message",
168: * "Invalid Return by Time\nInvalid Zip Code");
169: * </pre>
170: *
171: * For multiple line messages, separate the lines with "\n".
172: *
173: * @param title the dialog title
174: * @param message The dialog message, a \n separated sequence of lines.
175: */
176: public static void confirm(String title, String message) {
177: confirm(null, title, message, null);
178: }
179:
180: /**
181: * Display a dialog window with an OK button, icon, title and message.<p>
182: *
183: * Sample usage:
184: * <pre>
185: * MessageBox.confirm("error", "Operator Message",
186: * "Invalid Return by Time\nInvalid Zip Code");
187: * </pre>
188: *
189: * For multiple line messages, separate the lines with "\n".
190: *
191: * @param icon name of the image file without path info or file extension.
192: * @param title the dialog title
193: * @param message The dialog message, a \n separated sequence of lines.
194: */
195: public static void confirm(String icon, String title, String message) {
196: confirm(icon, title, message, null);
197: }
198:
199: /**
200: * Display a dialog window with specified buttons, message and icons.<p>
201: *
202: * Sample usage:
203: * <pre>
204: * MessageBox.confirm("question", "Operator Message",
205: * "Would you like to shut down the server?",
206: * "text1;image1|text2,image2");
207: * </pre>
208: *
209: * For multiple line messages, separate the lines with "\n".<p>
210: *
211: * The buttons string should specify text and optionally an image for each button.
212: * Use a semi-colon to separate the text and image for a button, and a
213: * pipe to separate one button's info from that of the next.<br>
214: * e.g. "text1;image1|text2;image2|......|textk;imagek"<p>
215: *
216: * The buttons string can also specify int return values for the buttons.<br>
217: * e.g. "text1;image1;5|text2;image2;17|......|textK;imageK;27"<p>
218: *
219: * Warning: Give all of the buttons return values or none of them. If you don't specify
220: * return values, a button's return value will depend on the order in which it is added
221: * to the dialog. Also, return values must be integers greater than or equal to 0.
222: *
223: * @param icon The dialog's icon.
224: * @param title The dialog's title.
225: * @param message The dialog's message.
226: * @param buttons specifies what text and icon each button will have
227: * @return the clicked button's return value
228: *
229: */
230: public static int confirm(String icon, String title,
231: String message, String buttons) {
232: return confirm(icon, title, message, null, buttons);
233: }
234:
235: /**
236: * Display a dialog window with the specified icon, title,
237: * component, and buttons.
238: * @param icon the name of the icon, optional
239: * @param title the MessageBox title
240: * @param component the Component for the content panel.
241: * @param buttons a string specifying the buttons
242: * @return the clicked button's return value
243: */
244: public static int confirm(String icon, String title,
245: Component component, String buttons) {
246: return confirm(icon, title, null, component, buttons);
247: }
248:
249: /**
250: * Display a dialog window with the specified icon, title,
251: * message, component, and buttons.<p>
252: * Note: Don't specify both a message and a component.
253: * @param icon the name of the icon, optional
254: * @param title the MessageBox title
255: * @param message the message for this MessageBox
256: * @param component the Component for the content panel.
257: * @param buttons a string specifying the buttons
258: * @return the clicked button's return value
259: */
260: private static int confirm(String icon, String title,
261: String message, Component component, String buttons) {
262: MessageBox box = new MessageBox();
263: box.setTitle(title);
264: box.setIcon(icon);
265: box.setText(message);
266: box.setComponent(component);
267: box.setButtons(buttons);
268: return box.confirm();
269: }
270:
271: /**
272: * Closes the current message box.
273: */
274: public static void closeCurrent() {
275: List<MessageBox> l = getMessageBoxes();
276: if (l.size() > 0)
277: l.get(l.size() - 1).close();
278: }
279:
280: /**
281: * Returns a list of messageboxes.
282: * @return the current Application's list of MessageBoxes.
283: */
284: private static List<MessageBox> getMessageBoxes() {
285: return messageBoxStack.get();
286: }
287:
288: /**
289: * Returns the title of the MessageBox.
290: * @return the title of this MessageBox
291: */
292: public String getTitle() {
293: return title;
294: }
295:
296: /**
297: * Set the title for the MessageBox.
298: * @param title the MessageBox title
299: */
300: public void setTitle(String title) {
301: title = title == null ? "" : title;
302: this .title = title;
303: }
304:
305: /**
306: * Returns the message in the message box.
307: * @return the message in this MessageBox
308: */
309: public String getText() {
310: return text;
311: }
312:
313: /**
314: * Set the message for the MessageBox.<p>
315: * For multiple line messages, separate the lines with "\n".<br>
316: * Don't set both a message and a Component.
317: *
318: * @param text the message
319: */
320: public void setText(String text) {
321: text = text == null ? "" : text;
322: this .text = text;
323: }
324:
325: /**
326: * Returns the component for the MessageBox.
327: * @return a Component
328: */
329: public Component getComponent() {
330: return component;
331: }
332:
333: /**
334: * Set the component for the MessageBox.<p>
335: * The component will appear in the content panel.<br>
336: * Don't set both a message and a Component.
337: * @param component the content for the MessageBox.
338: */
339: public void setComponent(Component component) {
340: this .component = component;
341: }
342:
343: /**
344: * Returns the icon for the message box.
345: * @return the name for the icon for the MessageBox
346: */
347: public String getIcon() {
348: return icon;
349: }
350:
351: /**
352: * Set the icon for the MessageBox. It appears in the
353: * top left corner.
354: *
355: * @param icon name of the icon
356: */
357: public void setIcon(String icon) {
358: icon = icon == null ? "" : icon;
359: this .icon = icon;
360: }
361:
362: /**
363: * Returns the string that sets all the buttons for the message box.
364: * @return the string describing this MessageBox's buttons
365: */
366: public String getButtons() {
367: return buttons;
368: }
369:
370: /**
371: * Sets the buttons based on a string.
372: * </p>
373: * Example:
374: * <pre>
375: * currentMB.setButtons("Delete;ERASE|Activate;STAR|Cancel;CANCEL");
376: * </pre>
377: * @param buttons a String describing the buttons
378: */
379: public void setButtons(String buttons) {
380: buttons = buttons == null ? "" : buttons;
381: this .buttons = buttons;
382: }
383:
384: /**
385: * Makes the MessageBox visible. <p>
386: *
387: * Adds the MessageBox to the MessageBox stack on the application
388: * instance.<p>
389: *
390: * This method is non-blocking.
391: */
392: public void show() {
393: if (dialog != null)
394: throw new IllegalStateException(
395: "messageBox is already open");
396: dialog = getDialog();
397: getMessageBoxes().add(this );
398: dialog.setWaitForWindow(false);
399: dialog.setVisible(true);
400: }
401:
402: /**
403: * Make the MessageBox visible, and then block.
404: * @return the clicked button's return value
405: */
406: public int confirm() {
407: if (dialog != null)
408: throw new IllegalStateException(
409: "messageBox is already open");
410: if (buttons == null || buttons.length() == 0)
411: buttons = "OK";
412: hasButtons = true;
413: dialog = getDialog();
414: dialog.setWaitForWindow(true);
415: dialog.setVisible(true);
416: return buttonId;
417: }
418:
419: /**
420: * Close the message box.
421: *
422: */
423: public void close() {
424: if (this .dialog == null)
425: throw new IllegalStateException("messageBox is not open");
426: getMessageBoxes().remove(this );
427: if (component != null)
428: dialog.getChildren().remove(component);
429: dialog.setVisible(false);
430: dialog = null;
431: }
432:
433: private Dialog getDialog() {
434: Dialog dialog = new Dialog();
435: dialog.setTitle(title);
436: List<Component> items = dialog.getChildren();
437: int x = SPACING;
438: int y = SPACING;
439:
440: if (icon.length() > 0) {
441: Image image = new Image();
442: image.setImage(icon);
443: image.setX(SPACING);
444: image.setY(SPACING);
445: image.setWidth(ICON_SIZE);
446: image.setHeight(ICON_SIZE);
447: items.add(image);
448: x += ICON_SIZE + SPACING;
449: }
450:
451: Component content = getContent();
452: Component buttons = hasButtons ? getButtonPanel() : null;
453: content.setY(y);
454: y += content.getHeight() + SPACING;
455:
456: if (hasButtons) {
457: buttons.setY(y);
458: y += buttons.getHeight() + SPACING;
459: }
460:
461: int maxWidth = dialog.getTitle().length() * TEXT_CHAR_WIDTH
462: + 40; //20 is X button
463: if (content.getWidth() > maxWidth)
464: maxWidth = content.getWidth();
465: if (hasButtons && buttons.getWidth() > maxWidth)
466: maxWidth = buttons.getWidth();
467:
468: if (content.getWidth() == maxWidth)
469: content.setX(x);
470: else {
471: int diff = maxWidth - content.getWidth();
472: content.setX(x + (diff > 0 ? diff / 2 : 0));
473: }
474:
475: if (hasButtons) {
476: if (buttons.getWidth() == maxWidth)
477: buttons.setX(x);
478: else {
479: int diff = maxWidth - buttons.getWidth();
480: buttons.setX(x + (diff > 0 ? diff / 2 : 0));
481: }
482: }
483:
484: x += maxWidth + SPACING;
485: items.add(content);
486: if (hasButtons)
487: items.add(buttons);
488: dialog.setWidth(x);
489: if (icon.length() > 0 && y < ICON_SIZE + SPACING * 2)
490: y = ICON_SIZE + SPACING * 2;
491: dialog.setHeight(y + 22);
492: Frame f = Application.current().getFrame();
493:
494: int width = f.getInnerWidth();
495: int height = f.getInnerHeight();
496: if (width < 0)
497: width = 560;
498: if (height < 0)
499: height = 400;
500: x = (width / 2) - (dialog.getWidth() / 2);
501: y = (height / 2) - (dialog.getHeight() / 2);
502: if (x < 0)
503: x = 0;
504: if (y < 0)
505: y = 0;
506: dialog.setX(x);
507: dialog.setY(y);
508: return dialog;
509: }
510:
511: private Component getButtonPanel() {
512: Panel panel = new Panel();
513:
514: if (buttons != null) {
515: class ButtonDef {
516: String text = "";
517: String imageFileName;
518: int id;
519: int width;
520: }
521:
522: int currentX = 0;
523:
524: String[] buttonLines = buttons.split("\\|");
525: ButtonDef[] buttonDefs = new ButtonDef[buttonLines.length];
526: int maxHeight = 0;
527:
528: for (int i = 0; i < buttonLines.length; i++) {
529: String[] def = buttonLines[i].split(";");
530: ButtonDef bd = new ButtonDef();
531: buttonDefs[i] = bd;
532:
533: if (def.length > 0 && def[0].length() > 0)
534: bd.text = def[0];
535: if (def.length > 1 && def[1].length() > 0)
536: bd.imageFileName = def[1];
537: bd.id = def.length > 2 && def[2].length() > 0 ? Integer
538: .valueOf(def[2]).intValue() : i;
539:
540: int width = bd.text.length() * TEXT_CHAR_WIDTH + 30;
541: if (width < MIN_BUTTON_WIDTH)
542: width = MIN_BUTTON_WIDTH;
543: int height = 0;
544:
545: if (bd.imageFileName != null) {
546: height = 16 + 11;
547: width += 16;
548: }
549:
550: if (height < MIN_BUTTON_HEIGHT)
551: height = MIN_BUTTON_HEIGHT;
552: if (maxHeight < height)
553: maxHeight = height;
554: bd.width = width;
555: }
556:
557: for (int i = 0; i < buttonDefs.length; i++) {
558: Button button = new Button();
559: button.setText(buttonDefs[i].text);
560: if (buttonDefs[i].imageFileName != null
561: && buttonDefs[i].imageFileName.length() > 0)
562: button.setImage(buttonDefs[i].imageFileName);
563:
564: if (i == 0) {
565: button.setStandard(true);
566: if (component == null)
567: button.setFocus(true);
568: }
569:
570: button.setWidth(buttonDefs[i].width);
571: button.setHeight(maxHeight);
572: button.setX(currentX);
573: button.setY(0);
574: final int id = buttonDefs[i].id;
575:
576: button.addActionListener("click", new ActionListener() {
577: public void actionPerformed(ActionEvent ev) {
578: if (ev.getAction().equals(Button.ACTION_CLICK)) {
579: MessageBox.this .buttonId = id;
580: if (component != null)
581: dialog.getChildren().remove(component);
582: dialog.setVisible(false);
583: }
584: }
585: });
586:
587: panel.getChildren().add(button);
588: currentX += button.getWidth() + SPACING;
589: }
590:
591: panel.setWidth(currentX - SPACING);
592: panel.setHeight(maxHeight);
593: }
594:
595: return panel;
596: }
597:
598: private Component getContent() {
599: Component comp;
600:
601: if (text != null && !"".equals(text)) {
602: String[] lines = text.split("\\r?\\n");
603:
604: int width = 0;
605: Font f = new Label().getStyle().getFont();
606:
607: for (int i = 0; i < lines.length; i++) {
608: int length = f.getStringWidth(lines[i]);
609:
610: if (length > width)
611: width = f.getStringWidth(lines[i]);
612: }
613:
614: int y = 0;
615: Panel content = new Panel();
616: List<Component> items = content.getChildren();
617:
618: for (int i = 0; i < lines.length; i++) {
619: Label label = new Label();
620: label.setText(lines[i]);
621: label.setAlignX(Label.AlignX.CENTER);
622: label.setHeight(TEXT_LINE_HEIGHT);
623: label.setWidth(width);
624: label.setX(0);
625: label.setY(y);
626: items.add(label);
627: y += label.getHeight();
628: }
629:
630: content.setWidth(width);
631: content.setHeight(y + SPACING);
632: comp = content;
633: } else if (component != null) {
634: comp = component;
635: if (comp.getWidth() == 0)
636: comp.setWidth(DEFAULT_COMPONENT_WIDTH);
637: if (comp.getHeight() == 0)
638: comp.setHeight(DEFAULT_COMPONENT_HEIGHT);
639: comp.setFocus(true);
640: } else
641: comp = new Panel();
642:
643: return comp;
644: }
645: }
|