001    /*
002     * $Id: JXErrorPane.java 3165 2009-01-02 13:26:07Z rah003 $
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    package org.jdesktop.swingx;
022    
023    import java.awt.Component;
024    import java.lang.reflect.InvocationTargetException;
025    import javax.swing.Icon;
026    import javax.swing.JComponent;
027    import javax.swing.JDialog;
028    import javax.swing.JFrame;
029    import javax.swing.JInternalFrame;
030    import javax.swing.SwingUtilities;
031    import org.jdesktop.swingx.error.ErrorReporter;
032    import org.jdesktop.swingx.error.ErrorInfo;
033    import org.jdesktop.swingx.plaf.ErrorPaneUI;
034    import org.jdesktop.swingx.plaf.ErrorPaneAddon;
035    import org.jdesktop.swingx.plaf.LookAndFeelAddons;
036    
037    /**
038     * <p>JXErrorPane is a common error component suitable for displaying errors,
039     * warnings, and exceptional application behavior to users.</p>
040     * 
041     * <p>User interaction with the <code>JXErrorPane</code> includes the ability to
042     * view details associated with the error. This is the primary feature that differentiates
043     * <code>JXErrorPane</code> from <code>JOptionPane</code>. In addition,
044     * <code>JXErrorPane</code> specializes in handling unrecoverable errors. If you
045     * need an error dialog that allows the user to take some action to recover
046     * from an error (such as "Repair Disk", "Replace All", etc) then you should
047     * use <code>JOptionPane</code>.</p>
048     * 
049     * <p>Data and application state associated with an error are encapsulated
050     * in the {@link org.jdesktop.swingx.error.ErrorInfo} class. The
051     * {@code JXErrorPane} displays the data contained in the {@code ErrorInfo}.
052     * In addition, {@code ErrorInfo} is passed to the
053     * {@link org.jdesktop.swingx.error.ErrorReporter} if the user decides to report
054     * the incident.</p>
055     * 
056     * <h2>Basic Usage</h2>
057     * <p>Typically, the <code>JXErrorPane</code>
058     * is not created and displayed directly. Instead, one of the static showXXX methods
059     * are called that create and display the <code>JXErrorPane</code> in a 
060     * <code>JDialog</code>, <code>JFrame</code>, or <code>JInternalFrame</code>.</p>
061     * 
062     * <p>These static showXXX methods all follow the same pattern, namely (
063     * where XXX could be one of Dialog, Frame, or InternalFrame):
064     * <ul>
065     *  <li><b>showXXX(Throwable e)</b>: This usage allows you to show a default error
066     *      window, detailing the error</li>
067     *  <li><b>showXXX(Component owner, ErrorInfo info)</b>: This usage shows an
068     *      error dialog based on the given <code>ErrorInfo</code>. The component
069     *      argument is the component over which the dialog should be centered.</li>
070     *  <li><b>showXXX(Component owner, JXErrorPane pane)</b>: This usage shows
071     *      an error dialog using the given error pane. This allows you to completely
072     *      modify the pane (perhaps installing a custom UI delegate, etc) to present
073     *      to the user</li>
074     *  <li><b>createXXX(Component owner, JXErrorPane pane)</b>: Creates and returns
075     *      a dialog for presenting the given <code>JXErrorPane</code>, but does not
076     *      show it. This allows the developer to modify properties of the dialog
077     *      prior to display</li>
078     * </ul></p>
079     * 
080     * <p>Following are some examples and further discussion regarding some of these
081     * static methods. Example of the most basic usage:
082     * <pre><code>
083     *      try {
084     *          //do stuff.... something throws an exception in here
085     *      } catch (Exception e) {
086     *          JXErrorPane.showDialog(e);
087     *      }
088     * </code></pre>. Alternatively there are <code>showFrame</code> and
089     * <code>showInternalFrame</code> variants of each of the <code>showDialog</code>
090     * methods described in this API.</p>
091     *
092     * <p>While this is the simplest usage, it is not the recommended approach for
093     * most errors since it yields the most difficult messages for users to understand.
094     * Instead it is recommended to provide a more useful message for users. For example:
095     * <pre><code>
096     *      URL url = null;
097     *      try {
098     *          url = new URL(userSuppliedUrl);
099     *      } catch (MalformedURLException e) {
100     *          String msg = "The web resource you entered is not formatted"
101     *                      + " correctly.";
102     *          String details = "&lt;html&gt;Web resources should begin with \"http://\""
103     *                      + " and cannot contain any spaces. Below are a few"
104     *                      + " more guidelines.&lt;ul&gt;"
105     *                      + getURLGuidelines()
106     *                      + "&lt;/ul&gt;&lt;/html&gt;";
107     *          JXErrorPane.showDialog(myWindow, "Unknown Resource", msg, details, e);
108     *          return false;
109     *      }
110     * </code></pre></p>
111     * 
112     * <p>Before showing the <code>JXErrorPane</code> in a frame or dialog, you may modify
113     * the appearance and behavior of the <code>JXErrorPane</code> by setting one or more of its bean
114     * properties. For example, to modify the icon shown with a particular
115     * instance of a <code>JXErrorPane</code>, you might do the following:
116     * <pre><code>
117     *      JXErrorPane pane = new JXErrorPane();
118     *      pane.setErrorIcon(myErrorIcon);
119     *      pane.setErrorInfo(new ErrorInfo("Fatal Error", exception));
120     *      JXErrorPane.showDialog(null, pane);
121     * </code></pre></p>
122     *
123     * <p><code>JXErrorPane</code> may also be configured with a "Report" button which allows
124     * the user to send a bug report, typically through email. This is done through
125     * the pluggable {@link org.jdesktop.swingx.error.ErrorReporter} class. Simply instantiate
126     * some custom subclass of <code>ErrorReporter</code> and pass the instance into the
127     * {@link #setErrorReporter} method.</p>
128     *
129     * <p><code>JXErrorPane</code> can also be used for displaying fatal error messages to
130     * users. Fatal messages indicate a serious error in the application that cannot
131     * be corrected and that must result in the termination of the application. 
132     * After the close of a fatal error dialog, the application should
133     * automatically exit. Fatal messages are identified by the <code>Level</code>
134     * of the <code>ErrorInfo</code> being 
135     * {@link org.jdesktop.swingx.error.ErrorLevel}<code>.FATAL</code>.</p>
136     * 
137     * <p>By default, when Fatal error dialogs are closed the application exits with
138     * a code of "1". In other words, <code>System.exit(1)</code>. If you wish to implement
139     * custom handling, you can replace the default fatal action in the <code>ActionMap</code>
140     * of the <code>JXErrorPane</code> instance. If you specify a custom fatal 
141     * action, then the default action of calling
142     * System.exit will not occur. You are therefore responsible for shutting down
143     * the application.</p>
144     * 
145     * <h2>UI Default Keys</h2>
146     * <p>TODO</p>
147     * JXErrorPane.errorIcon
148     *      or, if not specified, JOptionPane.errorIcon
149     * JXErrorPane.warningIcon
150     *      or, if not specified, JOptionPane.warningIcon
151     * JXErrorPane.details_contract_text (ignored on Mac OS X)
152     * JXErrorPane.details_expand_text (ignored on Mac OS X)
153     * JXErrorPane.mac.details_contract_text
154     * JXErrorPane.mac.details_expand_text
155     * Tree.expandedIcon (on Mac OS X)
156     * Tree.collapsedIcon (on Mac OS X)
157     * 
158     * <h2>Customizing the Look and Feel</h2>
159     * <p>TODO</p>
160     * 
161     *
162     * @status REVIEWED
163     *
164     * @author Richard Bair
165     * @author Alexander Zuev
166     * @author Shai Almog
167     * @author rah003
168     */
169    public class JXErrorPane extends JComponent {
170        //---------------------------------------------------- static properties
171        /**
172         * Name of the Action used for reporting errors
173         */
174        public static final String REPORT_ACTION_KEY = "report-action";
175        /**
176         * Name of the Action used for fatal errors
177         */
178        public static final String FATAL_ACTION_KEY = "fatal-action";
179        /**
180         * UI Class ID
181         */
182        public final static String uiClassID = "ErrorPaneUI";
183        
184        /**
185         */
186        static {
187            LookAndFeelAddons.contribute(new ErrorPaneAddon());
188        }
189        
190        //-------------------------------------------------- instance properties
191        
192        /**
193         * ErrorInfo that contains all the information prepared for
194         * reporting.
195         */
196        private ErrorInfo errorInfo = new ErrorInfo("Error", "Normally this place contains problem description.\n You see this text because one of the following reasons:\n * Either it is a test\n * Developer have not provided error details\n * This error message was invoked unexpectedly and there are no more details available", null, null, null, null, null);
197        /**
198         * The Icon to use, regardless of the error message. The UI delegate is
199         * responsible for setting this icon, if the developer has not specified
200         * the icon.
201         */
202        private Icon icon;
203        /**
204         * The delegate to use for reporting errors.
205         */
206        private ErrorReporter reporter;
207        
208        //--------------------------------------------------------- constructors
209    
210        /**
211         * Create a new <code>JXErrorPane</code>.
212         */
213        public JXErrorPane() {
214            super();
215            updateUI();
216        }
217        
218        //------------------------------------------------------------- UI Logic
219        
220        /**
221         * @inheritDoc
222         */
223        public ErrorPaneUI getUI() {
224            return (ErrorPaneUI)ui;
225        }
226    
227        /**
228         * Sets the look and feel (L&F) object that renders this component.
229         * 
230         * @param ui
231         *            the ErrorPaneUI L&F object
232         * @see javax.swing.UIDefaults#getUI
233         * @beaninfo bound: true hidden: true attribute: visualUpdate true
234         *           description: The UI object that implements the Component's
235         *           LookAndFeel.
236         */
237        public void setUI(ErrorPaneUI ui) {
238            super.setUI(ui);
239        }
240    
241        /**
242         * Returns the name of the L&F class that renders this component.
243         *
244         * @return the string {@link #uiClassID}
245         * @see javax.swing.JComponent#getUIClassID
246         * @see javax.swing.UIDefaults#getUI
247         */
248        public String getUIClassID() {
249            return uiClassID;
250        }
251    
252        /**
253         * Notification from the <code>UIManager</code> that the L&F has changed.
254         * Replaces the current UI object with the latest version from the
255         * <code>UIManager</code>.
256         * 
257         * @see javax.swing.JComponent#updateUI
258         */
259        @Override
260        public void updateUI() {
261            setUI((ErrorPaneUI) LookAndFeelAddons
262                    .getUI(this, ErrorPaneUI.class));
263        }
264        
265        //-------------------------------------------- public methods/properties
266        
267        /**
268         * Sets the ErrorInfo for this dialog. ErrorInfo can't be null.
269         *
270         * @param info ErrorInfo that incorporates all the details about the error. Null value is not supported.
271         */
272        public void setErrorInfo(ErrorInfo info) {
273            if (info == null) {
274                throw new NullPointerException("ErrorInfo can't be null. Provide valid ErrorInfo object.");
275            }
276            ErrorInfo old = this.errorInfo;
277            this.errorInfo = info;
278            firePropertyChange("errorInfo", old, this.errorInfo);
279        }
280        
281        /**
282         * Gets the <code>JXErrorPane</code>'s <code>ErrorInfo</code>
283         *
284         * @return <code>ErrorInfo</code> assigned to this dialog
285         */
286        public ErrorInfo getErrorInfo() {
287            return errorInfo;
288        }
289        
290        /**
291         * Specifies the icon to use
292         *
293         * @param icon the Icon to use. May be null.
294         */
295        public void setIcon(Icon icon) {
296            Icon old = this.icon;
297            this.icon = icon;
298            firePropertyChange("icon", old, this.icon);
299        }
300        
301        /**
302         * Returns the Icon used
303         *
304         * @return the Icon
305         */
306        public Icon getIcon() {
307            return icon;
308        }
309        
310        /**
311         * Sets the {@link ErrorReporter} delegate to use. This delegate is called
312         * automatically when the report action is fired.
313         * 
314         * @param reporter the ErrorReporter to use. If null, the report button will
315         *        not be shown in the error dialog.
316         */
317        public void setErrorReporter(ErrorReporter reporter) {
318            ErrorReporter old = getErrorReporter();
319            this.reporter = reporter;
320            firePropertyChange("errorReporter", old, getErrorReporter());
321        }
322        
323        /**
324         * Gets the {@link ErrorReporter} delegate in use.
325         * 
326         * @return the ErrorReporter. May be null.
327         */
328        public ErrorReporter getErrorReporter() {
329            return reporter;
330        }
331        
332        //------------------------------------------------------- static methods
333        
334        /**
335         * <p>Constructs and shows the error dialog for the given exception.  The 
336         * exceptions message will be the errorMessage, and the stacktrace will form 
337         * the details for the error dialog.</p>
338         * 
339         * <p>This method may be called from any thread. It will actually show the error
340         * dialog on the AWT event dispatch thread. This method blocks. If called
341         * on the EDT, the dialog shown will be modal. Otherwise, this thread will
342         * block until the error dialog has been shown and hidden on the EDT.</p>
343         * 
344         * @param e Exception that contains information about the error cause and stack trace
345         */
346        public static void showDialog(Throwable e) {
347            ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null);
348            showDialog(null, ii);
349        }
350        
351        /**
352         * <p>Constructs and shows the error dialog, using the given
353         * <code>ErrorInfo</code> to initialize the view.</p>
354         * 
355         * <p>This method may be called from any thread. It will actually show the error
356         * dialog on the AWT event dispatch thread. This method blocks. If called
357         * on the EDT, the dialog shown will be modal. Otherwise, this thread will
358         * block until the error dialog has been shown and hidden on the EDT.</p>
359         * 
360         * @param owner Owner of this error dialog. Determines the Window in which the dialog
361         *        is displayed; if the <code>owner</code> has
362         *        no <code>Window</code>, a default <code>Frame</code> is used
363         * @param info <code>ErrorInfo</code> that incorporates all the information about the error
364         */
365        public static void showDialog(Component owner, ErrorInfo info) {
366            JXErrorPane pane = new JXErrorPane();
367            pane.setErrorInfo(info);
368            showDialog(owner, pane);
369        }
370        
371        /**
372         * <p>Constructs and shows the error dialog, using the given
373         * <code>JXErrorPane</code> for the view portion of the dialog.</p>
374         * 
375         * <p>This method may be called from any thread. It will actually show the error
376         * dialog on the AWT event dispatch thread. This method blocks. If called
377         * on the EDT, the dialog shown will be modal. Otherwise, this thread will
378         * block until the error dialog has been shown and hidden on the EDT.</p>
379         * 
380         * @param owner Owner of this error dialog. Determines the Window in which the dialog
381         *        is displayed; if the <code>owner</code> has
382         *        no <code>Window</code>, a default <code>Frame</code> is used
383         * @param pane <code>JXErrorPane</code> which will form the content area
384         *        of the dialog.
385         */
386        public static void showDialog(final Component owner, final JXErrorPane pane) {
387            Runnable r = new Runnable() {
388                public void run() {
389                    JDialog dlg = createDialog(owner, pane);
390                    dlg.setVisible(true);
391                }
392            };
393            
394            if (!SwingUtilities.isEventDispatchThread()) {
395                try {
396                    SwingUtilities.invokeAndWait(r);
397                } catch (InvocationTargetException ex) {
398                    ex.printStackTrace();
399                } catch (InterruptedException ex) {
400                    ex.printStackTrace();
401                }
402            } else {
403                r.run();
404            }
405        }
406        
407        /**
408         * <p>Constructs and returns an error dialog, using the given
409         * <code>JXErrorPane</code> for the view portion of the dialog.</p>
410         * 
411         * <p>This method may be called from any thread. It does not block. The
412         * caller is responsible for ensuring that the dialog is shown and manipulated
413         * on the AWT event dispatch thread. A common way to do this is to use
414         * <code>SwingUtilities.invokeAndWait</code> or 
415         * <code>SwingUtilities.invokeLater()</code>.</p>
416         * 
417         * @param owner Owner of this error dialog. Determines the Window in which the dialog
418         *        is displayed; if the <code>owner</code> has
419         *        no <code>Window</code>, a default <code>Frame</code> is used
420         * @param pane <code>JXErrorPane</code> which will form the content area
421         *        of the dialog.
422         * @return a <code>JDialog</code> configured to display the error.
423         */
424        public static JDialog createDialog(Component owner, JXErrorPane pane) {
425            JDialog window = pane.getUI().getErrorDialog(owner);
426            // If the owner is null applies orientation of the shared
427            // hidden window used as owner.
428            if(owner != null) {
429                pane.applyComponentOrientation(owner.getComponentOrientation());
430            } else {
431                pane.applyComponentOrientation(window.getComponentOrientation());
432            }
433            window.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
434            window.pack();
435            window.setLocationRelativeTo(owner);
436            return window;
437        }
438        
439        /**
440         * <p>Constructs and shows the error frame for the given exception.  The 
441         * exceptions message will be the errorMessage, and the stacktrace will form 
442         * the details for the error dialog.</p>
443         * 
444         * <p>This method may be called from any thread. It will actually show the error
445         * dialog on the AWT event dispatch thread. This method blocks. If called
446         * on the EDT, the frame shown will be modal. Otherwise, this thread will
447         * block until the error frame has been shown and hidden on the EDT.</p>
448         * 
449         * @param e Exception that contains information about the error cause and stack trace
450         */
451        public static void showFrame(Throwable e) {
452            ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null);
453            showFrame(null, ii);
454        }
455        
456        /**
457         * <p>Constructs and shows the error frame, using the given
458         * <code>ErrorInfo</code> to initialize the view.</p>
459         * 
460         * <p>This method may be called from any thread. It will actually show the error
461         * dialog on the AWT event dispatch thread. This method blocks. If called
462         * on the EDT, the frame shown will be modal. Otherwise, this thread will
463         * block until the error frame has been shown and hidden on the EDT.</p>
464         * 
465         * @param owner Owner of this error frame. Determines the Window in which the frame
466         *        is displayed; if the <code>owner</code> has
467         *        no <code>Window</code>, a default <code>Frame</code> is used
468         * @param info <code>ErrorInfo</code> that incorporates all the information about the error
469         */
470        public static void showFrame(Component owner, ErrorInfo info) {
471            JXErrorPane pane = new JXErrorPane();
472            pane.setErrorInfo(info);
473            showFrame(owner, pane);
474        }
475        
476        /**
477         * <p>Constructs and shows the error frame, using the given
478         * <code>JXErrorPane</code> for the view portion of the frame.</p>
479         * 
480         * <p>This method may be called from any thread. It will actually show the error
481         * dialog on the AWT event dispatch thread. This method blocks. If called
482         * on the EDT, the frame shown will be modal. Otherwise, this thread will
483         * block until the error frame has been shown and hidden on the EDT.</p>
484         * 
485         * @param owner Owner of this error frame. Determines the Window in which the dialog
486         *        is displayed; if the <code>owner</code> has
487         *        no <code>Window</code>, a default <code>Frame</code> is used
488         * @param pane <code>JXErrorPane</code> which will form the content area
489         *        of the frame.
490         */
491        public static void showFrame(final Component owner, final JXErrorPane pane) {
492            Runnable r = new Runnable() {
493                public void run() {
494                    JFrame window = createFrame(owner, pane);
495                    window.setVisible(true);
496                }
497            };
498            
499            if (!SwingUtilities.isEventDispatchThread()) {
500                try {
501                    SwingUtilities.invokeAndWait(r);
502                } catch (InvocationTargetException ex) {
503                    ex.printStackTrace();
504                } catch (InterruptedException ex) {
505                    ex.printStackTrace();
506                }
507            } else {
508                r.run();
509            }
510        }
511        
512        /**
513         * <p>Constructs and returns an error frame, using the given
514         * <code>JXErrorPane</code> for the view portion of the frame.</p>
515         * 
516         * <p>This method may be called from any thread. It does not block. The
517         * caller is responsible for ensuring that the frame is shown and manipulated
518         * on the AWT event dispatch thread. A common way to do this is to use
519         * <code>SwingUtilities.invokeAndWait</code> or 
520         * <code>SwingUtilities.invokeLater()</code>.</p>
521         * 
522         * @param owner Owner of this error frame. Determines the Window in which the frame
523         *        is displayed; if the <code>owner</code> has
524         *        no <code>Window</code>, a default <code>Frame</code> is used
525         * @param pane <code>JXErrorPane</code> which will form the content area
526         *        of the frame.
527         * @return a <code>JFrame</code> configured to display the error.
528         */
529        public static JFrame createFrame(Component owner, JXErrorPane pane) {
530            JFrame window = pane.getUI().getErrorFrame(owner);
531            // If the owner is null applies orientation of the shared
532            // hidden window used as owner.
533            if(owner != null) {
534                pane.applyComponentOrientation(owner.getComponentOrientation());
535            } else {
536                pane.applyComponentOrientation(window.getComponentOrientation());
537            }
538            window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
539            window.pack();
540    //        window.setLocationRelativeTo(owner);
541            return window;
542        }
543        
544        /**
545         * <p>Constructs and shows the error frame for the given exception.  The 
546         * exceptions message will be the errorMessage, and the stacktrace will form 
547         * the details for the error dialog.</p>
548         * 
549         * <p>This method may be called from any thread. It will actually show the error
550         * dialog on the AWT event dispatch thread. This method blocks. If called
551         * on the EDT, the frame shown will be modal. Otherwise, this thread will
552         * block until the error frame has been shown and hidden on the EDT.</p>
553         * 
554         * @param e Exception that contains information about the error cause and stack trace
555         */
556        public static void showInternalFrame(Throwable e) {
557            ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null);
558            showInternalFrame(null, ii);
559        }
560        
561        /**
562         * <p>Constructs and shows the error frame, using the given
563         * <code>ErrorInfo</code> to initialize the view.</p>
564         * 
565         * <p>This method may be called from any thread. It will actually show the error
566         * dialog on the AWT event dispatch thread. This method blocks. If called
567         * on the EDT, the frame shown will be modal. Otherwise, this thread will
568         * block until the error frame has been shown and hidden on the EDT.</p>
569         * 
570         * @param owner Owner of this error frame. Determines the Window in which the frame
571         *        is displayed; if the <code>owner</code> has
572         *        no <code>Window</code>, a default <code>Frame</code> is used
573         * @param info <code>ErrorInfo</code> that incorporates all the information about the error
574         */
575        public static void showInternalFrame(Component owner, ErrorInfo info) {
576            JXErrorPane pane = new JXErrorPane();
577            pane.setErrorInfo(info);
578            showInternalFrame(owner, pane);
579        }
580        
581        /**
582         * <p>Constructs and shows the error frame, using the given
583         * <code>JXErrorPane</code> for the view portion of the frame.</p>
584         * 
585         * <p>This method may be called from any thread. It will actually show the error
586         * dialog on the AWT event dispatch thread. This method blocks. If called
587         * on the EDT, the frame shown will be modal. Otherwise, this thread will
588         * block until the error frame has been shown and hidden on the EDT.</p>
589         * 
590         * @param owner Owner of this error frame. Determines the Window in which the dialog
591         *        is displayed; if the <code>owner</code> has
592         *        no <code>Window</code>, a default <code>Frame</code> is used
593         * @param pane <code>JXErrorPane</code> which will form the content area
594         *        of the frame.
595         */
596        public static void showInternalFrame(final Component owner, final JXErrorPane pane) {
597            Runnable r = new Runnable() {
598                public void run() {
599                    JInternalFrame window = createInternalFrame(owner, pane);
600                    window.setVisible(true);                
601                }
602            };
603            
604            if (!SwingUtilities.isEventDispatchThread()) {
605                try {
606                    SwingUtilities.invokeAndWait(r);
607                } catch (InvocationTargetException ex) {
608                    ex.printStackTrace();
609                } catch (InterruptedException ex) {
610                    ex.printStackTrace();
611                }
612            } else {
613                r.run();
614            }
615        }
616        
617        /**
618         * <p>Constructs and returns an error frame, using the given
619         * <code>JXErrorPane</code> for the view portion of the frame.</p>
620         * 
621         * <p>This method may be called from any thread. It does not block. The
622         * caller is responsible for ensuring that the frame is shown and manipulated
623         * on the AWT event dispatch thread. A common way to do this is to use
624         * <code>SwingUtilities.invokeAndWait</code> or 
625         * <code>SwingUtilities.invokeLater()</code>.</p>
626         * 
627         * @param owner Owner of this error frame. Determines the Window in which the frame
628         *    is displayed; if the <code>owner</code> has
629         *    no <code>Window</code>, a default <code>Frame</code> is used
630         * @param pane <code>JXErrorPane</code> which will form the content area
631         *    of the frame.
632         * @return a <code>JInternalFrame</code> configured to display the error.
633         */
634        public static JInternalFrame createInternalFrame(Component owner, JXErrorPane pane) {
635            JInternalFrame window = pane.getUI().getErrorInternalFrame(owner);
636            // If the owner is null applies orientation of the shared
637            // hidden window used as owner.
638            if(owner != null) {
639                pane.applyComponentOrientation(owner.getComponentOrientation());
640            } else {
641                pane.applyComponentOrientation(window.getComponentOrientation());
642            }
643            window.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
644            window.pack();
645            //TODO!
646    //                window.setLocationRelativeTo(owner);
647            return window;
648        }
649        
650    }