/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.tools.gui.exceptionnotification; import org.jdesktop.swingx.JXErrorPane; import org.jdesktop.swingx.error.ErrorInfo; import org.openide.util.NbBundle; import java.awt.CardLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseMotionAdapter; import java.util.Properties; import java.util.logging.Level; import javax.swing.JFrame; import javax.swing.Timer; import de.cismet.tools.gui.StaticSwingTools; /** * A small panel shown in the status bar, with a click on a icon a error dialog is shown.At the start of the Navigator * the panel is empty, if an uncaught exception occurs than an error-icon flashes a few times and afterwards is steady * for several seconds. After these seconds the icon disappears again. With a click on that icon an error dialog is * shown, which contains the stack trace of that uncaught exception. After the dialog is closed, the icon disappears, * except another exception occurs.<br/> * To be notified about the uncaught exceptions ExceptionNotificationStatusPanel can be added as a listener to various * ExceptionHandler.<br/> * The different times can be configured in the exceptionNotificationStatusPanel.properties * * @author Gilles Baatz * @version $Revision$, $Date$ * @see DefaultExceptionHandlerListener */ public class ExceptionNotificationStatusPanel extends javax.swing.JPanel implements DefaultExceptionHandlerListener { //~ Static fields/initializers --------------------------------------------- private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger( ExceptionNotificationStatusPanel.class); private static int FLASH_TIME; private static int FLASH_PAUSE; private static int STEADY_TIME; static { final Properties prop = new Properties(); try { prop.load(ExceptionNotificationStatusPanel.class.getResourceAsStream( "exceptionNotificationStatusPanel.properties")); FLASH_TIME = Math.abs(Integer.parseInt(prop.getProperty("flashTime"))); FLASH_PAUSE = Math.abs(Integer.parseInt(prop.getProperty("flashPause"))); STEADY_TIME = Math.abs(Integer.parseInt(prop.getProperty("steadyTime"))); } catch (Exception ex) { LOG.error("Could not load the properties for the ExceptionNotificationStatusPanel", ex); FLASH_TIME = 5; FLASH_PAUSE = 500; STEADY_TIME = 30; } } //~ Instance fields -------------------------------------------------------- private Throwable uncaughtException; private final Timer flashTimer; private final Timer steadyTimer; // Variables declaration - do not modify//GEN-BEGIN:variables private org.jdesktop.swingx.JXHyperlink hlErrorIcon; private javax.swing.JPanel pnlDisabled; private javax.swing.JPanel pnlIcon; // End of variables declaration//GEN-END:variables //~ Constructors ----------------------------------------------------------- /** * Creates new form ExceptionNotificationStatusPanel. */ public ExceptionNotificationStatusPanel() { initComponents(); this.setVisible(false); final int repetitions = (int)Math.floor(FLASH_TIME * 1000d / FLASH_PAUSE); flashTimer = new Timer(FLASH_PAUSE, new FlashHandler(repetitions)); flashTimer.setRepeats(true); flashTimer.setInitialDelay(0); steadyTimer = new Timer(STEADY_TIME * 1000, new SteadyHandler()); steadyTimer.setRepeats(false); } //~ Methods ---------------------------------------------------------------- /** * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The * content of this method is always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { pnlDisabled = new javax.swing.JPanel(); pnlIcon = new javax.swing.JPanel(); hlErrorIcon = new org.jdesktop.swingx.JXHyperlink(); setLayout(new java.awt.CardLayout()); pnlDisabled.setLayout(new java.awt.GridLayout(1, 0)); add(pnlDisabled, "DISABLED"); pnlIcon.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR)); pnlIcon.setMaximumSize(new java.awt.Dimension(17, 16)); pnlIcon.setMinimumSize(new java.awt.Dimension(17, 16)); pnlIcon.setPreferredSize(new java.awt.Dimension(17, 16)); pnlIcon.addMouseListener(new java.awt.event.MouseAdapter() { @Override public void mouseClicked(final java.awt.event.MouseEvent evt) { pnlIconMouseClicked(evt); } }); pnlIcon.setLayout(new java.awt.GridLayout(1, 0)); hlErrorIcon.setIcon(new javax.swing.ImageIcon( getClass().getResource("/de/cismet/tools/gui/exceptionnotification/exclamation.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText( hlErrorIcon, org.openide.util.NbBundle.getMessage( ExceptionNotificationStatusPanel.class, "ExceptionNotificationStatusPanel.hlErrorIcon.text")); // NOI18N hlErrorIcon.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(final java.awt.event.ActionEvent evt) { hlErrorIconActionPerformed(evt); } }); pnlIcon.add(hlErrorIcon); add(pnlIcon, "ICON"); pnlIcon.addMouseListener(new MouseAdapter() { }); pnlIcon.addMouseMotionListener(new MouseMotionAdapter() { }); } // </editor-fold>//GEN-END:initComponents /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ private void hlErrorIconActionPerformed(final java.awt.event.ActionEvent evt) { //GEN-FIRST:event_hlErrorIconActionPerformed showErrorPanel(); } //GEN-LAST:event_hlErrorIconActionPerformed /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ private void pnlIconMouseClicked(final java.awt.event.MouseEvent evt) { //GEN-FIRST:event_pnlIconMouseClicked showErrorPanel(); } //GEN-LAST:event_pnlIconMouseClicked /** * Shows the error panel, if another exception occurs while the panel is shown, then the icon is not hidden * afterwards. */ private void showErrorPanel() { final Throwable shownException = uncaughtException; final String basicMessage = NbBundle.getMessage( ExceptionNotificationStatusPanel.class, "ExceptionNotificationStatusPanel.hlErrorIconActionPerformed().error.basicMessage"); final ErrorInfo ei = new ErrorInfo( NbBundle.getMessage( ExceptionNotificationStatusPanel.class, "ExceptionNotificationStatusPanel.hlErrorIconActionPerformed().error.title"), basicMessage, null, null, uncaughtException, Level.SEVERE, null); JXErrorPane.showDialog(StaticSwingTools.getParentFrameIfNotNull(this), ei); if (shownException == uncaughtException) { // hide the icon flashTimer.stop(); steadyTimer.stop(); letIconFlashOrShowAnEmptyPanel(null); this.setVisible(false); } } /** * This method allows the flashing of the icon and it is possible to click on the empty panel and show the error * dialog. Whereas the disabled panel does not have a click listener.<br/> * <br/> * The argument card can have three states: * * <ul> * <li>null - show the disabled panel</li> * <li>True - show the error icon</li> * <li>False - show the empty panel and hide the error icon</li> * </ul> * <br/> * * <p><b>Note:</b> To show the disabled panel is actually not needed at the moment as the whole panel disappears if * no error dialog is available. Although this might be useful for future implementations and was therefor not * removed.</p> * * @param card DOCUMENT ME! */ private void letIconFlashOrShowAnEmptyPanel(final Boolean card) { String cardStr = "DISABLED"; if (card != null) { cardStr = "ICON"; hlErrorIcon.setVisible(card); } ((CardLayout)this.getLayout()).show(this, cardStr); } @Override public void uncaughtException(final Thread thread, final Throwable error) { uncaughtException = error; this.setVisible(true); flashTimer.restart(); } /** * DOCUMENT ME! * * @param args DOCUMENT ME! */ public static void main(final String[] args) { final JFrame frame = new JFrame(); frame.setSize(50, 50); final ExceptionNotificationStatusPanel panel = new ExceptionNotificationStatusPanel(); frame.add(panel); frame.setVisible(true); panel.uncaughtException(null, new NullPointerException("Some error message.")); } //~ Inner Classes ---------------------------------------------------------- /** * The ActionListener of the flashTimer. Let the icon appear/disappear a few times and the start the steadyTimer. * * @version $Revision$, $Date$ */ public class FlashHandler implements ActionListener { //~ Instance fields ---------------------------------------------------- private int counter; private final int repetitions; //~ Constructors ------------------------------------------------------- /** * Creates a new FlashHandler object. * * @param repetitions DOCUMENT ME! */ public FlashHandler(final int repetitions) { this.repetitions = repetitions; } //~ Methods ------------------------------------------------------------ @Override public void actionPerformed(final ActionEvent ae) { if (flashTimer.isRunning()) { letIconFlashOrShowAnEmptyPanel((counter % 2) == 0); counter++; if (counter >= repetitions) { counter = 0; letIconFlashOrShowAnEmptyPanel(true); ((Timer)ae.getSource()).stop(); steadyTimer.restart(); } } } } /** * The ActionListener of the steadyTimer. * * @version $Revision$, $Date$ */ public class SteadyHandler implements ActionListener { //~ Methods ------------------------------------------------------------ @Override public void actionPerformed(final ActionEvent ae) { letIconFlashOrShowAnEmptyPanel(null); ExceptionNotificationStatusPanel.this.setVisible(false); } } }