/***************************************************
*
* 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);
}
}
}