/**
*
*/
package javax.swing.origamist;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Queue;
import java.util.ResourceBundle;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.Border;
import cz.cuni.mff.peckam.java.origamist.services.ServiceLocator;
import cz.cuni.mff.peckam.java.origamist.services.interfaces.ConfigurationManager;
/**
* A simple statusbar with customizable regions.
*
* @author Martin Pecka
*/
public class JStatusBar extends JPanel implements MessageBar
{
/** Needed for serialization */
private static final long serialVersionUID = -2772216384360313722L;
/**
* Name of the default area
*/
public static final String DEFAULT_AREA_NAME = "default";
/**
* The label used for displaying status messages in default area
*/
protected JLabel defaultLabel = new JLabel();
/** The text set by a non-timed-out showMessage on the default area. */
protected String fixedDefaultText = " ";
/** The messages with timeout to be displayed. */
protected Queue<TimedOutMessage> timedOutMessages = new LinkedList<TimedOutMessage>();
/**
* Areas of the statusbar (always contains an area called DEFAULT_AREA_NAME
* with a label)
*/
protected Hashtable<String, JComponent> areas = new Hashtable<String, JComponent>();
/**
* The border all areas will have
*/
protected Border areaBorder = BorderFactory.createEtchedBorder();
/**
* Sets the border all areas will have
*
* @param border The border all areas will have
*/
public void setAreaBorder(Border border)
{
areaBorder = border;
}
/**
* Timer used for displaying timed messages.
*/
protected final Timer timer = new Timer(2000, null);
{
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
JPanel defaultArea = new JPanel();
defaultArea.setAlignmentX(LEFT_ALIGNMENT);
defaultArea.setMaximumSize(new Dimension(Integer.MAX_VALUE, defaultArea.getMaximumSize().height));
defaultArea.setLayout(new BoxLayout(defaultArea, BoxLayout.LINE_AXIS));
defaultLabel.setAlignmentX(LEFT_ALIGNMENT);
defaultLabel.setFont(defaultLabel.getFont().deriveFont(11f).deriveFont(Font.PLAIN));
defaultArea.add(defaultLabel);
addArea(DEFAULT_AREA_NAME, defaultArea);
timer.setRepeats(false);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
if (timedOutMessages.isEmpty()) {
defaultLabel.setText(fixedDefaultText);
} else {
TimedOutMessage message = timedOutMessages.poll();
timer.setInitialDelay(message.milis);
defaultLabel.setText(message.message);
timer.start();
}
}
});
}
/**
* Adds a new area to the statusbar
*
* You can use area.setAlignmentX() to set its position in the statusbar
*
* @param name Name of the area
* @param area The area to add
*/
public synchronized void addArea(String name, JComponent area)
{
if (areas.contains(name))
return;
if (areas.size() > 0) {
int maxHeight = 0;
for (JComponent a : areas.values()) {
if (a.getPreferredSize().height > maxHeight)
maxHeight = a.getPreferredSize().height;
}
if (area.getPreferredSize().height < maxHeight) {
area.setPreferredSize(new Dimension(area.getPreferredSize().width, maxHeight));
} else if (area.getPreferredSize().height > maxHeight) {
for (JComponent a : areas.values()) {
a.setPreferredSize(new Dimension(a.getPreferredSize().width, area.getPreferredSize().height));
}
}
}
area.setBorder(areaBorder);
area.setAlignmentY(TOP_ALIGNMENT);
areas.put(name, area);
add(area);
}
/**
* Removes area of the given name
*
* @param name Name of the area to be removed
*/
public synchronized void removeArea(String name)
{
// one cannot delete the default area
if (name.equals(DEFAULT_AREA_NAME))
return;
remove(areas.get(name));
areas.remove(name);
}
/**
* @param name Name of the area you want
* @return The area of the given name or null if it does not exist
*/
public synchronized JComponent getArea(String name)
{
return areas.get(name);
}
/**
* @return The default area
*/
public synchronized JComponent getDefaultArea()
{
return getArea(DEFAULT_AREA_NAME);
}
@Override
public void showL7dMessage(String bundle, String messageKey)
{
showMessage(ResourceBundle.getBundle(bundle, ServiceLocator.get(ConfigurationManager.class).get().getLocale())
.getString(messageKey));
}
@Override
public synchronized void showMessage(final String message)
{
if (message == null)
return;
if (getDefaultArea().isAncestorOf(defaultLabel)) {
fixedDefaultText = message;
if (!timer.isRunning()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run()
{
defaultLabel.setText(message);
}
});
}
}
}
@Override
public void showL7dMessage(String bundle, String messageKey, Integer milis)
{
showMessage(ResourceBundle.getBundle(bundle, ServiceLocator.get(ConfigurationManager.class).get().getLocale())
.getString(messageKey), milis);
}
@Override
public synchronized void showMessage(final String message, Integer milis)
{
if (message == null)
return;
int timeout;
if (milis != null)
timeout = milis;
else
timeout = getTimeout(message);
if (!timer.isRunning()) {
timer.setInitialDelay(timeout);
timer.start();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run()
{
defaultLabel.setText(message);
}
});
} else {
timedOutMessages.add(new TimedOutMessage(message, timeout));
}
}
/**
* Compute timeout of the given message based on its length.
*
* @param message The message the timeout is computed for. If message contains HTML, ensure that all < are
* properly encoded with < .
* @return The timeout.
*/
protected int getTimeout(String message)
{
// the first regex is for stripping HTML out of the text; the second one counts spaces
return Math.max(2000, 200 * message.replaceAll("\\<[^>]*>", "").replaceAll("[^ ]", "").length());
}
/**
* A message string with timeout information.
*
* @author Martin Pecka
*/
protected class TimedOutMessage
{
/** The message to display. */
protected String message;
/** The number of miliseconds the message has to be displayed for. */
protected int milis;
/**
* @param message The message to display.
* @param milis The number of miliseconds the message has to be displayed for.
*/
public TimedOutMessage(String message, int milis)
{
this.message = message;
this.milis = milis;
}
}
}