001 /*
002 * $Id: JXErrorDialog.java,v 1.22 2006/03/18 22:52:10 rbair Exp $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021
022 package org.jdesktop.swingx;
023
024 import java.awt.Component;
025 import java.awt.Dialog;
026 import java.awt.Dimension;
027 import java.awt.Frame;
028 import java.awt.GridBagConstraints;
029 import java.awt.GridBagLayout;
030 import java.awt.Insets;
031 import java.awt.Window;
032 import java.awt.datatransfer.StringSelection;
033 import java.awt.datatransfer.Transferable;
034 import java.awt.event.ActionEvent;
035 import java.awt.event.ActionListener;
036 import java.util.Enumeration;
037 import java.util.ResourceBundle;
038 import java.util.logging.Level;
039
040 import javax.swing.AbstractAction;
041 import javax.swing.Action;
042 import javax.swing.Icon;
043 import javax.swing.JButton;
044 import javax.swing.JComponent;
045 import javax.swing.JDialog;
046 import javax.swing.JEditorPane;
047 import javax.swing.JLabel;
048 import javax.swing.JScrollPane;
049 import javax.swing.TransferHandler;
050 import javax.swing.UIManager;
051 import javax.swing.plaf.basic.BasicHTML;
052 import org.jdesktop.swingx.util.WindowUtils;
053
054 /**
055 * <p>Common Error Dialog, suitable for representing information about
056 * errors and exceptions happened in application. The common usage of the
057 * <code>JXErrorDialog</code> is to show collected data about the incident and
058 * probably ask customer for a feedback. The data about the incident consists
059 * from the title which will be displayed in the dialog header, short
060 * description of the problem that will be immediately seen after dialog is
061 * became visible, full description of the problem which will be visible after
062 * user clicks "Details" button and Throwable that contains stack trace and
063 * another usable information that may be displayed in the dialog.</p>
064 *
065 * <p>There are two basic ways to use the JXErrorDialog. The first is to call one
066 * of the standard static methods for displaying the error dialog. Here is a
067 * basic example:<br/>
068 * <pre><code>
069 * try {
070 * //do some work
071 * //some exception is thrown
072 * } catch (Exception e) {
073 * JXErrorDialog.showDialog(this, "Critical Error",
074 * "<html><body>Some <b>critical error</b> has occured." +
075 * " You may need to restart this application. Message #SC21." +
076 * "</body></html>", e);
077 * }
078 * </code></pre></p>
079 *
080 * <p>For the vast majority of use cases, the static methods should be sufficient and
081 * are recommended. However, for those few use cases requireing customizations, you
082 * can create and display a JXErrorDialog manually.</p>
083 *
084 * <p>To ask user for feedback extend abstract class <code>ErrorReporter</code> and
085 * set your reporter using <code>setReporter</code> method. Report button will
086 * be added to the dialog automatically.<br>
087 * See {@link MailErrorReporter MailErrorReporter} documentation for the
088 * example of error reporting usage.</p>
089 *
090 * <p>For example, to show simple <code>JXErrorDialog</code> call <br>
091 * <code>JXErrorDialog.showDialog(null, "Application Error",
092 * "The application encountered the unexpected error,
093 * please contact developers")</code></p>
094 *
095 * <p>Internationalization is handled via a resource bundle or via the UIManager
096 * bidi orientation (usefull for right to left languages) is determined in the
097 * same way as the JOptionPane where the orientation of the parent component is
098 * picked. So when showDialog(Component cmp, ...) is invoked the component
099 * orientation of the error dialog will match the component orientation of cmp.</p>
100 *
101 * @author Richard Bair
102 * @author Alexander Zuev
103 * @author Shai Almog
104 */
105 public class JXErrorDialog extends JDialog {
106 //---------------------------------------------------- static properties
107 /**
108 * Used as a prefix when pulling data out of UIManager for i18n
109 */
110 private static String CLASS_NAME;
111 /**
112 * Icon for the error dialog (stop sign, etc)
113 */
114 private static Icon DEFAULT_ERROR_ICON = UIManager.getIcon("OptionPane.errorIcon");
115 /**
116 * Icon for the error dialog (stop sign, etc)
117 */
118 private static Icon DEFAULT_WARNING_ICON = UIManager.getIcon("OptionPane.warningIcon");
119 /**
120 * Error reporting engine assigned for error reporting for all error dialogs
121 */
122 private static ErrorReporter DEFAULT_REPORTER;
123
124 //-------------------------------------------------- instance properties
125
126 /**
127 * Error message text area
128 */
129 private JEditorPane errorMessage;
130 /**
131 * details text area
132 */
133 private JXEditorPane details;
134 /**
135 * detail button
136 */
137 private EqualSizeJButton detailButton;
138 /**
139 * details panel
140 */
141 private JXPanel detailsPanel;
142 /**
143 * label used to display the warning/error icon
144 */
145 private JLabel iconLabel;
146 /**
147 * report an error button
148 */
149 private EqualSizeJButton reportButton;
150 /**
151 * IncidentInfo that contains all the information prepared for
152 * reporting.
153 */
154 private IncidentInfo incidentInfo;
155 /**
156 * The Action that will be executed to report an error/warning. If null,
157 * then a default ReportAction will be used.
158 */
159 private Action reportAction;
160 /**
161 * The ErrorReporter to use. It defaults to the global default reporter
162 * specified via the static methods, but may be overridden
163 */
164 private ErrorReporter reporter = DEFAULT_REPORTER;
165 /**
166 * The Icon to use if the error message is indeed an Error, as specified
167 * by IncidentInfo.getErrorLevel == Level.SEVERE
168 */
169 private Icon errorIcon = DEFAULT_ERROR_ICON;
170 /**
171 * The Icon to use if the error message is not an Error
172 * (IncidentInfo.getErrorLevel != Level.SEVERE)
173 */
174 private Icon warningIcon = DEFAULT_WARNING_ICON;
175
176 //------------------------------------------------------ private helpers
177 /**
178 * The height of the window when collapsed. This value is stashed when the
179 * dialog is expanded
180 */
181 private int collapsedHeight = 0;
182 /**
183 * The height of the window when last expanded. This value is stashed when
184 * the dialog is collapsed
185 */
186 private int expandedHeight = 0;
187
188 //------------------------------------------------- static configuration
189
190 /**
191 * Creates initialize the UIManager with localized strings
192 */
193 static {
194 // Popuplate UIDefaults with the localizable Strings we will use
195 // in the Login panel.
196 CLASS_NAME = JXErrorDialog.class.getCanonicalName();
197 String lookup;
198 ResourceBundle res = ResourceBundle.getBundle("org.jdesktop.swingx.plaf.resources.ErrorDialog");
199 Enumeration<String> keys = res.getKeys();
200 while (keys.hasMoreElements()) {
201 String key = keys.nextElement();
202 lookup = CLASS_NAME + "." + key;
203 if (UIManager.getString(lookup) == null) {
204 UIManager.put(lookup, res.getString(key));
205 }
206 }
207 }
208
209 //--------------------------------------------------------- constructors
210
211 /**
212 * Create a new ErrorDialog with the given Frame as the owner
213 * @param owner Owner of this error dialog.
214 */
215 public JXErrorDialog(Frame owner) {
216 super(owner, true);
217 initGui();
218 }
219
220 /**
221 * Create a new ErrorDialog with the given Dialog as the owner
222 * @param owner Owner of this error dialog.
223 */
224 public JXErrorDialog(Dialog owner) {
225 super(owner, true);
226 initGui();
227 }
228
229 //-------------------------------------------- public methods/properties
230
231 /**
232 * Sets the IncidentInfo for this dialog
233 *
234 * @param info IncidentInfo that incorporates all the details about the error
235 */
236 public void setIncidentInfo(IncidentInfo info) {
237 IncidentInfo old = this.incidentInfo;
238 this.incidentInfo = info;
239 firePropertyChange("incidentInfo", old, this.incidentInfo);
240 reinit();
241 }
242
243 /**
244 * Get curent dialog's IncidentInfo
245 *
246 * @return <code>IncidentInfo</code> assigned to this dialog
247 */
248 public IncidentInfo getIncidentInfo() {
249 return incidentInfo;
250 }
251
252 /**
253 * Sets the ErrorReporter to use with this instance of JXErrorDialog.
254 * If not specified, the default error reporter is used (as specified
255 * by the setDefaultErrorReporter() static method).
256 *
257 * @param rep if null, the default error reporter is used
258 */
259 public void setErrorReporter(ErrorReporter rep) {
260 ErrorReporter old = this.reporter;
261 this.reporter = rep == null ? DEFAULT_REPORTER : rep;
262 firePropertyChange("errorReporter", old, this.reporter);
263 reinit();
264 }
265
266 /**
267 * Returns the error reporter in use with this instance of JXErrorDialog.
268 * If not specified, the default error reporter is returned
269 *
270 * @return the ErrorReporter in use. May be null
271 */
272 public ErrorReporter getErrorReporter() {
273 return reporter;
274 }
275
276 /**
277 * Specifies the icon to use if the IncidentInfo is Level.SEVERE
278 *
279 * @param icon the Icon to use. If null, the default error icon will be used
280 */
281 public void setErrorIcon(Icon icon) {
282 Icon old = this.errorIcon;
283 this.errorIcon = icon == null ? DEFAULT_ERROR_ICON : icon;
284 firePropertyChange("errorIcon", old, this.errorIcon);
285 reinit();
286 }
287
288 /**
289 * Returns the Icon in use if the IncidentInfo is Level.SEVERE
290 *
291 * @return the Icon
292 */
293 public Icon getErrorIcon() {
294 return errorIcon;
295 }
296
297 /**
298 * Specifies the icon to use if the IncidentInfo is not Level.SEVERE
299 *
300 * @param icon the Icon to use. If null, the default warning icon will be used
301 */
302 public void setWarningIcon(Icon icon) {
303 Icon old = this.warningIcon;
304 this.warningIcon = icon == null ? DEFAULT_WARNING_ICON : icon;
305 firePropertyChange("warningIcon", old, this.warningIcon);
306 reinit();
307 }
308
309 /**
310 * Returns the Icon in use if the IncidentInfo is not Level.SEVERE
311 *
312 * @return the Icon
313 */
314 public Icon getWarningIcon() {
315 return warningIcon;
316 }
317
318 /**
319 * Specify the Action that will be executed to report an error/warning. If null,
320 * then a default ReportAction will be used.
321 *
322 * @param action The Action to execute if the user attempts to report a problem
323 */
324 public void setReportAction(Action action) {
325 Action old = this.reportAction;
326 this.reportAction = action == null ? new ReportAction() : action;
327 firePropertyChange("reportAction", old, this.reportAction);
328 reportButton.setAction(this.reportAction);
329 }
330
331 /**
332 * @return the Action that is executed if the user attempts to report a problem
333 */
334 public Action getReportAction() {
335 return reportAction;
336 }
337
338 //----------------------------------------------- private helper methods
339 /**
340 * initialize the gui.
341 */
342 private void initGui() {
343 //initialize the gui
344 GridBagLayout layout = new GridBagLayout();
345 this.getContentPane().setLayout(layout);
346
347 GridBagConstraints gbc = new GridBagConstraints();
348 gbc.anchor = GridBagConstraints.NORTH;
349 gbc.fill = GridBagConstraints.NONE;
350 gbc.gridheight = 1;
351 gbc.insets = new Insets(22, 12, 11, 17);
352 iconLabel = new JLabel(DEFAULT_ERROR_ICON);
353 this.getContentPane().add(iconLabel, gbc);
354
355 errorMessage = new JEditorPane();
356 errorMessage.setEditable( false );
357 errorMessage.setContentType("text/html");
358 errorMessage.setOpaque( false );
359 errorMessage.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
360 gbc = new GridBagConstraints();
361 gbc.anchor = GridBagConstraints.LINE_START;
362 gbc.fill = GridBagConstraints.BOTH;
363 gbc.gridheight = 1;
364 gbc.gridwidth = 3;
365 gbc.gridx = 1;
366 gbc.weightx = 0.0;
367 gbc.weighty = 0.00001; //ensures that when details is hidden, it get all
368 //the extra space, but none when details is shown
369 //(unless you have a REALLY BIG MONITOR
370 gbc.insets = new Insets(24, 0, 0, 11);
371 this.getContentPane().add(errorMessage, gbc);
372
373 gbc = new GridBagConstraints();
374 gbc.fill = GridBagConstraints.NONE;
375 gbc.gridx = 1;
376 gbc.gridy = 1;
377 gbc.gridwidth = 1;
378 gbc.weightx = 1.0;
379 gbc.weighty = 0.0;
380 gbc.anchor = GridBagConstraints.LINE_END;
381 gbc.insets = new Insets(12, 0, 11, 5);
382 EqualSizeJButton okButton = new EqualSizeJButton(UIManager.getString(CLASS_NAME + ".ok_button_text"));
383 this.getContentPane().add(okButton, gbc);
384
385 reportAction = new ReportAction();
386 reportButton = new EqualSizeJButton(reportAction);
387 gbc = new GridBagConstraints();
388 gbc.gridx = 2;
389 gbc.weightx = 0.0;
390 gbc.insets = new Insets(12, 0, 11, 5);
391 this.getContentPane().add(reportButton, gbc);
392 reportButton.setVisible(false); // not visible by default
393
394 detailButton = new EqualSizeJButton(UIManager.getString(CLASS_NAME + ".details_expand_text"));
395 gbc = new GridBagConstraints();
396 gbc.gridx = 3;
397 gbc.weightx = 0.0;
398 gbc.insets = new Insets(12, 0, 11, 11);
399 this.getContentPane().add(detailButton, gbc);
400
401 details = new JXEditorPane();
402 details.setContentType("text/html");
403 details.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
404 details.setTransferHandler(new DetailsTransferHandler());
405 JScrollPane detailsScrollPane = new JScrollPane(details);
406 detailsScrollPane.setPreferredSize(new Dimension(10, 250));
407 details.setEditable(false);
408 detailsPanel = new JXPanel(new GridBagLayout());
409 detailsPanel.add(detailsScrollPane, new GridBagConstraints(0,0,1,1,1.0,1.0,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(6,11,11,11),0,0));
410 gbc = new GridBagConstraints();
411 gbc.fill = GridBagConstraints.BOTH;
412 gbc.gridwidth = 4;
413 gbc.gridx = 0;
414 gbc.gridy = 2;
415 gbc.weighty = 1.0;
416 this.getContentPane().add(detailsPanel, gbc);
417
418 JButton button = new JButton(UIManager.getString(CLASS_NAME + ".copy_to_clipboard_button_text"));
419 button.addActionListener(new ActionListener() {
420 public void actionPerformed(ActionEvent ae) {
421 details.copy();
422 }
423 });
424 gbc = new GridBagConstraints();
425 gbc.anchor = GridBagConstraints.LINE_END;
426 gbc.fill = GridBagConstraints.NONE;
427 gbc.gridwidth = 1;
428 gbc.gridx = 0;
429 gbc.gridy = 1;
430 gbc.weighty = 0.0;
431 gbc.weightx = 1.0;
432 gbc.insets = new Insets(6, 11, 11, 11);
433 detailsPanel.add(button, gbc);
434
435
436 //make the buttons the same size
437 EqualSizeJButton[] buttons = new EqualSizeJButton[] {
438 detailButton, okButton, reportButton };
439 okButton.setGroup(buttons);
440 reportButton.setGroup(buttons);
441 detailButton.setGroup(buttons);
442
443 okButton.setMinimumSize(okButton.getPreferredSize());
444 reportButton.setMinimumSize(reportButton.getPreferredSize());
445 detailButton.setMinimumSize(detailButton.getPreferredSize());
446
447 //set the event handling
448 okButton.addActionListener(new OkClickEvent());
449 detailButton.addActionListener(new DetailsClickEvent());
450 }
451
452 /**
453 * Set the details section of the error dialog. If the details are either
454 * null or an empty string, then hide the details button and hide the detail
455 * scroll pane. Otherwise, just set the details section.
456 * @param details Details to be shown in the detail section of the dialog.
457 * This can be null if you do not want to display the details section of the
458 * dialog.
459 */
460 private void setDetails(String details) {
461 if (details == null || details.equals("")) {
462 setDetailsVisible(false);
463 detailButton.setVisible(false);
464 } else {
465 this.details.setText(details);
466 setDetailsVisible(false);
467 detailButton.setVisible(true);
468 }
469 }
470
471 /**
472 * Set the details section to be either visible or invisible. Set the
473 * text of the Details button accordingly.
474 * @param b if true details section will be visible
475 */
476 private void setDetailsVisible(boolean b) {
477 if (b) {
478 collapsedHeight = getHeight();
479 setSize(getWidth(), expandedHeight == 0 ? collapsedHeight + 300 : expandedHeight);
480 detailsPanel.setVisible(true);
481 detailButton.setText(UIManager.getString(CLASS_NAME + ".details_contract_text"));
482 detailsPanel.applyComponentOrientation(detailButton.getComponentOrientation());
483
484 // workaround for bidi bug, if the text is not set "again" and the component orientation has changed
485 // then the text won't be aligned correctly. To reproduce this (in JDK 1.5) show two dialogs in one
486 // use LTOR orientation and in the second use RTOL orientation and press "details" in both.
487 // Text in the text box should be aligned to right/left respectively, without this line this doesn't
488 // occure I assume because bidi properties are tested when the text is set and are not updated later
489 // on when setComponentOrientation is invoked.
490 details.setText(details.getText());
491 details.setCaretPosition(0);
492 } else {
493 expandedHeight = getHeight();
494 detailsPanel.setVisible(false);
495 detailButton.setText(UIManager.getString(CLASS_NAME + ".details_expand_text"));
496 // Trick to force errorMessage JTextArea to resize according
497 // to its columns property.
498 errorMessage.setSize( 0, 0 );
499 errorMessage.setSize( errorMessage.getPreferredSize() );
500 setSize(getWidth(), collapsedHeight);
501 }
502
503 repaint();
504 }
505
506 /**
507 * Set the error message for the dialog box
508 * @param errorMessage Message for the error dialog
509 */
510 private void setErrorMessage(String errorMessage) {
511 if(BasicHTML.isHTMLString(errorMessage)) {
512 this.errorMessage.setContentType("text/html");
513 } else {
514 this.errorMessage.setContentType("text/plain");
515 }
516 this.errorMessage.setText(errorMessage);
517 }
518
519 /**
520 * Reconfigures the dialog if settings have changed, such as the
521 * IncidentInfo, errorIcon, warningIcon, etc
522 */
523 private void reinit() {
524 reportButton.setVisible(getErrorReporter() != null);
525 if (incidentInfo == null) {
526 iconLabel.setIcon(DEFAULT_ERROR_ICON);
527 setTitle("");
528 setErrorMessage("");
529 setDetails("");
530 } else {
531 iconLabel.setIcon(incidentInfo.getErrorLevel() == Level.SEVERE ?
532 errorIcon : warningIcon);
533 setTitle(incidentInfo.getHeader());
534 setErrorMessage(incidentInfo.getBasicErrorMessage());
535 String details = incidentInfo.getDetailedErrorMessage();
536 if(details == null) {
537 if(incidentInfo.getErrorException() != null) {
538 //convert the stacktrace into a more pleasent bit of HTML
539 StringBuffer html = new StringBuffer("<html>");
540 html.append("<h2>" + incidentInfo.getHeader() + "</h2>");
541 html.append("<HR size='1' noshade>");
542 html.append("<div></div>");
543 html.append("<b>Message:</b>");
544 html.append("<pre>");
545 html.append(" " + incidentInfo.getErrorException().toString());
546 html.append("</pre>");
547 html.append("<b>Level:</b>");
548 html.append("<pre>");
549 html.append(" " + incidentInfo.getErrorLevel());
550 html.append("</pre>");
551 html.append("<b>Stack Trace:</b>");
552 html.append("<pre>");
553 for (StackTraceElement el : incidentInfo.getErrorException().getStackTrace()) {
554 html.append(" " + el.toString() + "\n");
555 }
556 html.append("</pre></html>");
557 details = html.toString();
558 } else {
559 details = "";
560 }
561 }
562 setDetails(details);
563 }
564
565 //set the preferred width of the message area
566 //the preferred width should not exceed 500 pixels, or be less than 300 pixels
567 Dimension prefSize = errorMessage.getPreferredSize();
568 prefSize.width = Math.min(500, prefSize.width);
569 prefSize.width = Math.max(300, prefSize.width);
570 errorMessage.setSize(prefSize);
571 prefSize.height = errorMessage.getPreferredSize().height;
572 errorMessage.setPreferredSize(prefSize);
573 }
574
575 //------------------------------------------------------- static methods
576
577 /**
578 * Constructs and shows the error dialog for the given exception. The exceptions message will be the
579 * errorMessage, and the stacktrace will be the details.
580 * @param owner Owner of this error dialog. Determines the Window in which the dialog
581 * is displayed; if the <code>owner</code> has
582 * no <code>Window</code>, a default <code>Frame</code> is used
583 * @param title Title of the error dialog
584 * @param e Exception that contains information about the error cause and stack trace
585 */
586 public static void showDialog(Component owner, String title, Throwable e) {
587 IncidentInfo ii = new IncidentInfo(title, null, null, e);
588 showDialog(owner, ii);
589 }
590
591 /**
592 * Constructs and shows the error dialog for the given exception. The exceptions message is specified,
593 * and the stacktrace will be the details.
594 * @param owner Owner of this error dialog. Determines the Window in which the dialog
595 * is displayed; if the <code>owner</code> has
596 * no <code>Window</code>, a default <code>Frame</code> is used
597 * @param title Title of the error dialog
598 * @param errorMessage Message for the error dialog
599 * @param e Exception that contains information about the error cause and stack trace
600 */
601 public static void showDialog(Component owner, String title, String errorMessage, Throwable e) {
602 IncidentInfo ii = new IncidentInfo(title, errorMessage, null, e);
603 showDialog(owner, ii);
604 }
605
606 /**
607 * Show the error dialog.
608 * @param owner Owner of this error dialog. Determines the Window in which the dialog
609 * is displayed; if the <code>owner</code> has
610 * no <code>Window</code>, a default <code>Frame</code> is used
611 * @param title Title of the error dialog
612 * @param errorMessage Message for the error dialog
613 * @param details Details to be shown in the detail section of the dialog. This can be null
614 * if you do not want to display the details section of the dialog.
615 */
616 public static void showDialog(Component owner, String title, String errorMessage, String details) {
617 IncidentInfo ii = new IncidentInfo(title, errorMessage, details);
618 showDialog(owner, ii);
619 }
620
621 /**
622 * Show the error dialog.
623 * @param owner Owner of this error dialog. Determines the Window in which the dialog
624 * is displayed; if the <code>owner</code> has
625 * no <code>Window</code>, a default <code>Frame</code> is used
626 * @param title Title of the error dialog
627 * @param errorMessage Message for the error dialog
628 */
629 public static void showDialog(Component owner, String title, String errorMessage) {
630 IncidentInfo ii = new IncidentInfo(title, errorMessage, (String)null);
631 showDialog(owner, ii);
632 }
633
634 /**
635 * Show the error dialog.
636 * @param owner Owner of this error dialog. Determines the Window in which the dialog
637 * is displayed; if the <code>owner</code> has
638 * no <code>Window</code>, a default <code>Frame</code> is used
639 * @param info <code>IncidentInfo</code> that incorporates all the information about the error
640 */
641 public static void showDialog(Component owner, IncidentInfo info) {
642 JXErrorDialog dlg;
643
644 Window window = WindowUtils.findWindow(owner);
645
646 if (window instanceof Dialog) {
647 dlg = new JXErrorDialog((Dialog)window);
648 } else {
649 dlg = new JXErrorDialog((Frame)window);
650 }
651 dlg.setIncidentInfo(info);
652 // If the owner is null applies orientation of the shared
653 // hidden window used as owner.
654 if(owner != null)
655 dlg.applyComponentOrientation(owner.getComponentOrientation());
656 else
657 dlg.applyComponentOrientation(window.getComponentOrientation());
658 dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
659 dlg.pack();
660 dlg.setLocationRelativeTo(owner);
661 dlg.setVisible(true);
662 }
663
664 /**
665 * Returns the current reporting engine that will be used to report a problem if
666 * user clicks on 'Report' button or <code>null</code> if no reporting engine set.
667 *
668 * @return reporting engine
669 * @deprecated Use <code>getDefaultErrorReporter</code> instead
670 */
671 public static ErrorReporter getReporter() {
672 return DEFAULT_REPORTER;
673 }
674
675 /**
676 * Set reporting engine which will handle error reporting if user clicks 'report' button.
677 *
678 * @param rep <code>ErrorReporter</code> to be used or <code>null</code> to turn reporting facility off.
679 * @deprecated Use <code>setDefaultErrorReporter</code> instead
680 */
681 public static void setReporter(ErrorReporter rep) {
682 DEFAULT_REPORTER = rep;
683 }
684
685 /**
686 * Returns the current reporting engine that will be used to report a problem if
687 * user clicks on 'Report' button or <code>null</code> if no reporting engine set.
688 *
689 * @return reporting engine
690 */
691 public static ErrorReporter getDefaultErrorReporter() {
692 return DEFAULT_REPORTER;
693 }
694
695 /**
696 * Set reporting engine which will handle error reporting if user clicks 'report' button.
697 *
698 * @param rep <code>ErrorReporter</code> to be used or <code>null</code> to turn reporting facility off.
699 */
700 public static void setDefaultErrorReporter(ErrorReporter rep) {
701 DEFAULT_REPORTER = rep;
702 }
703
704 /**
705 * Set the Icon to use as the default error icon for JXErrorDialog
706 * instances. This icon is used whenever the IncidentInfo for a JXErrorDialog
707 * has an errorLevel of Level.SEVERE
708 *
709 * @param icon the Icon to use as the default error icon
710 */
711 public static void setDefaultErrorIcon(Icon icon) {
712 DEFAULT_ERROR_ICON = icon;
713 }
714
715 /**
716 * @return the default error icon
717 */
718 public static Icon getDefaultErrorIcon() {
719 return DEFAULT_ERROR_ICON;
720 }
721
722 /**
723 * Set the Icon to use as the default warning icon for JXErrorDialog
724 * instances. This icon is used whenever the IncidentInfo for a JXErrorDialog
725 * has an errorLevel that is not Level.SEVERE
726 *
727 * @param icon the Icon to use as the default warning icon
728 */
729 public static void setDefaultWarningIcon(Icon icon) {
730 DEFAULT_WARNING_ICON = icon;
731 }
732
733 /**
734 * @return the default warning icon
735 */
736 public static Icon getDefaultWarningIcon() {
737 return DEFAULT_WARNING_ICON;
738 }
739
740 //------------------------------------------------ actions/inner classes
741
742 /**
743 * Listener for Ok button click events
744 * @author Richard Bair
745 */
746 private final class OkClickEvent implements ActionListener {
747
748 /* (non-Javadoc)
749 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
750 */
751 public void actionPerformed(ActionEvent e) {
752 //close the window
753 setVisible(false);
754 dispose();
755 }
756 }
757
758 /**
759 * Listener for Details click events. Alternates whether the details section
760 * is visible or not.
761 * @author Richard Bair
762 */
763 private final class DetailsClickEvent implements ActionListener {
764
765 /* (non-Javadoc)
766 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
767 */
768 public void actionPerformed(ActionEvent e) {
769 setDetailsVisible(!detailsPanel.isVisible());
770 }
771 }
772
773 /**
774 * Action for report button
775 */
776 public class ReportAction extends AbstractAction {
777
778 public boolean isEnabled() {
779 return (getErrorReporter() != null);
780 }
781
782 public void actionPerformed(ActionEvent e) {
783 getErrorReporter().reportIncident(getIncidentInfo());
784 }
785
786 public Object getValue(String key) {
787 if(key == Action.NAME) {
788 if(getErrorReporter() != null && getErrorReporter().getActionName() != null) {
789 return getErrorReporter().getActionName();
790 } else {
791 return UIManager.getString(CLASS_NAME + ".report_button_text");
792 }
793 }
794 return super.getValue(key);
795 }
796 }
797
798 /**
799 * This is a button that maintains the size of the largest button in the button
800 * group by returning the largest size from the getPreferredSize method.
801 * This is better than using setPreferredSize since this will work regardless
802 * of changes to the text of the button and its language.
803 */
804 private static class EqualSizeJButton extends JButton {
805 public EqualSizeJButton() {
806 }
807
808 public EqualSizeJButton(String text) {
809 super(text);
810 }
811
812 public EqualSizeJButton(Action a) {
813 super(a);
814 }
815
816 /**
817 * Buttons whose size should be taken into consideration
818 */
819 private EqualSizeJButton[] group;
820
821 public void setGroup(EqualSizeJButton[] group) {
822 this.group = group;
823 }
824
825 /**
826 * Returns the actual preferred size on a different instance of this button
827 */
828 private Dimension getRealPreferredSize() {
829 return super.getPreferredSize();
830 }
831
832 /**
833 * If the <code>preferredSize</code> has been set to a
834 * non-<code>null</code> value just returns it.
835 * If the UI delegate's <code>getPreferredSize</code>
836 * method returns a non <code>null</code> value then return that;
837 * otherwise defer to the component's layout manager.
838 *
839 * @return the value of the <code>preferredSize</code> property
840 * @see #setPreferredSize
841 * @see ComponentUI
842 */
843 public Dimension getPreferredSize() {
844 int width = 0;
845 int height = 0;
846 for(int iter = 0 ; iter < group.length ; iter++) {
847 Dimension size = group[iter].getRealPreferredSize();
848 width = Math.max(size.width, width);
849 height = Math.max(size.height, height);
850 }
851
852 return new Dimension(width, height);
853 }
854
855 }
856
857 /**
858 * Returns the text as non-HTML in a COPY operation, and disabled CUT/PASTE
859 * operations for the Details pane.
860 */
861 private final class DetailsTransferHandler extends TransferHandler {
862 protected Transferable createTransferable(JComponent c) {
863 String text = details.getSelectedText();
864 if (text == null || text.equals("")) {
865 details.selectAll();
866 text = details.getSelectedText();
867 details.select(-1, -1);
868 }
869 return new StringSelection(text);
870 }
871
872 public int getSourceActions(JComponent c) {
873 return TransferHandler.COPY;
874 }
875
876 }
877 }