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 }