/** * eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the * <e-UCM> research group. * * Copyright 2005-2010 <e-UCM> research group. * * You can access a list of all the contributors to eAdventure at: * http://e-adventure.e-ucm.es/contributors * * <e-UCM> is a research group of the Department of Software Engineering * and Artificial Intelligence at the Complutense University of Madrid * (School of Computer Science). * * C Profesor Jose Garcia Santesmases sn, * 28040 Madrid (Madrid), Spain. * * For more info please visit: <http://e-adventure.e-ucm.es> or * <http://www.e-ucm.es> * * **************************************************************************** * * This file is part of eAdventure, version 2.0 * * eAdventure is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * eAdventure is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with eAdventure. If not, see <http://www.gnu.org/licenses/>. */ package es.eucm.ead.editor.util; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.Insets; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import es.eucm.ead.editor.util.i18n.I18N; abstract public class SwingUtilities { static private Logger logger = LoggerFactory .getLogger(SwingUtilities.class); /** * <p> Executes {@code doRun.run()} method synchronously with the AWT Event * Dispatcher Thread (EDT). </p> * * <p> Unlike {@link javax.swing.SwingUtilities#invokeAndWait(Runnable)} * exceptions are treated. </p> <p> <ul> <li>{@code java.lang.InterruptedException} * marks current thread as interrupted, {@code Thread.currentThread().interrupt()}.</li> <li>{@code java.lang.reflect.InvocationTargetException}: * shows an error dialog including the stack trace.</li> </ul> </p> * * @param doRun Piece of code to be run in EDT * * @see javax.swing.SwingUtilities#invokeAndWait(Runnable) */ static public void doInEDTNow(Runnable doRun) { if (javax.swing.SwingUtilities.isEventDispatchThread()) { doRun.run(); } else { try { javax.swing.SwingUtilities.invokeAndWait(doRun); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (InvocationTargetException e) { showExceptionDialog(e); e.printStackTrace(System.out); } } } static public void doInEDT(Runnable doRun) { javax.swing.SwingUtilities.invokeLater(doRun); } static public <T> T doInEDT(final ReturnableRunnable<T> doRun) { final Pointer<T> pointer = new Pointer<T>(); doInEDT(new Runnable() { public void run() { pointer.reference = doRun.run(); } }); return pointer.reference; } static public <T> T doInEDTNow(final ReturnableRunnable<T> doRun) { final Pointer<T> pointer = new Pointer<T>(); doInEDTNow(new Runnable() { public void run() { pointer.reference = doRun.run(); } }); return pointer.reference; } /** * Find the current {@code Frame} */ static public Frame findActiveFrame() { Frame result = null; Frame[] frames = JFrame.getFrames(); boolean found = false; int i = 0; while (!found && i < frames.length) { found = frames[i].isVisible(); i++; } if (found) { result = frames[i - 1]; } return result; } /** * Shared dialog for all exceptions */ static private JDialog exceptionDialog = null; static private JEditorPane exceptionEditorPane = null; static private ArrayList<String> exceptionsSoFar = new ArrayList<String>(); /** * Display an exception in the exception dialog. Will create a new * exception dialog if none was currently being displayed. * @param exp the throwable to display */ static public void showExceptionDialog(final Throwable exp) { logger.error("Exception captured by swing", exp); doInEDTNow(new Runnable() { @Override public void run() { showExceptionDialogEDT(exp); } }); } /** * Display an exception in the exception dialog. */ static private void showExceptionDialogEDT(final Throwable exp) { if (exceptionDialog == null) { exceptionDialog = new JDialog(findActiveFrame(), Messages.exception_dialog_title, false); exceptionDialog.setLayout(new BorderLayout()); exceptionEditorPane = new JEditorPane("text/html", ""); exceptionEditorPane.setEditable(false); exceptionDialog.add(new JScrollPane(exceptionEditorPane), BorderLayout.CENTER); // Add close button JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); btnPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JButton closeBtn = new JButton( Messages.exception_dialog_button_close); closeBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { exceptionDialog.dispose(); exceptionsSoFar.clear(); exceptionDialog = null; } }); btnPanel.add(closeBtn); exceptionDialog.getContentPane().add(btnPanel, BorderLayout.SOUTH); // Setup Dialog Size, make visible int w = exceptionDialog.getWidth(); int h = exceptionDialog.getHeight(); exceptionDialog.setSize(500, 300); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); exceptionDialog.setLocation((screenSize.width / 2) - (w / 2), (screenSize.height / 2) - (h / 2)); exceptionDialog.setResizable(true); exceptionDialog.setVisible(true); } // Contents StringBuilder sb = new StringBuilder(); String expType = I18N.bind( (exp instanceof Error) ? Messages.exception_dialog_type_error : Messages.exception_dialog_type_exception, exp .getClass().getName()); sb.append("<h3>" + expType + "<a name='a" + (exceptionsSoFar.size() + 1) + "'></a>" + "</h3>"); sb.append("<div style='font-size:90%; padding-left: 10px;'><pre>"); fillStackTrace(sb, exp); sb.append("</pre></div>"); exceptionsSoFar.add(sb.toString()); StringBuilder html = new StringBuilder("<html><head></head><body>"); for (String s : exceptionsSoFar) { html.append(s); } html.append("</body></html>"); exceptionEditorPane.setText(html.toString()); exceptionEditorPane.scrollToReference("a" + exceptionsSoFar.size()); } static private void fillStackTrace(StringBuilder buf, Throwable exp) { buf.append("<b>").append( I18N.bind(Messages.exception_dialog_message, exp .getLocalizedMessage())).append("</b>\n"); StackTraceElement traces[] = exp.getStackTrace(); for (int i = 0; i < traces.length; i++) { buf.append(traces[i].toString()).append("\n"); } Throwable cause = exp.getCause(); if (cause != null) { String className = cause.getClass().getName(); buf.append("<b>").append( I18N.bind(Messages.exception_dialog_caused_by, className)) .append("</b>\n"); fillStackTrace(buf, cause); } } public static Rectangle calculatePaintingArea(JComponent c) { Dimension d = c.getSize(); Rectangle result = new Rectangle(); result.x = 0; result.y = 0; result.width = d.width; result.height = d.height; Insets insets = c.getInsets(); if (insets != null) { result.width = d.width - (insets.left + insets.right); result.height = d.height - (insets.top + insets.bottom); result.x = insets.left; result.y = insets.top; } return result; } public interface ReturnableRunnable<E> { public E run(); } }