// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/gui/StatusLightPanel.java,v $
// $RCSfile: StatusLightPanel.java,v $
// $Revision: 1.3.2.3 $
// $Date: 2006/02/27 23:23:07 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.gui;
import java.awt.GridLayout;
import java.awt.Insets;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JToolBar;
import com.bbn.openmap.Layer;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.PropertyConsumer;
import com.bbn.openmap.event.LayerStatusEvent;
import com.bbn.openmap.event.LayerStatusListener;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.PropUtils;
/**
*/
public class StatusLightPanel extends OMComponentPanel implements
LayerStatusListener, PropertyChangeListener, PropertyConsumer {
protected int numlayers = 0;
protected MapBean map;
protected Layer[] layers;
protected Hashtable statusLights = new Hashtable();
protected JToolBar container = null;
protected final static transient URL greyURL = StatusLightPanel.class.getResource("grey.gif");
public final static transient ImageIcon greyIcon = new ImageIcon(greyURL, "unknown");
protected final static transient URL redURL = StatusLightPanel.class.getResource("red.gif");
public final static transient ImageIcon redIcon = new ImageIcon(redURL, "working");
protected final static transient URL greenURL = StatusLightPanel.class.getResource("green.gif");
public final static transient ImageIcon greenIcon = new ImageIcon(greenURL, "stable");
protected boolean waitingForLayers = false;
protected boolean showWaitCursor = false;
/**
* Flag to make the status light buttons act as triggers to bring
* up the layer palettes.
*/
protected boolean lightTriggers = true;
public final static String LightTriggersProperty = "triggers";
public final static String defaultKey = "StatusLightPanel";
protected String key = defaultKey;
public StatusLightPanel() {
super();
setLayout(new GridLayout(1, 0));
reset();
}
public void setMap(MapBean map) {
if (this.map != null) {
this.map.removePropertyChangeListener(this);
}
this.map = map;
if (map != null) {
map.addPropertyChangeListener(this);
}
}
public MapBean getMap() {
return map;
}
/**
* Listen for the layer changes within the MapBean, to display the
* status lights for each layer.
*/
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName() == MapBean.LayersProperty) {
listenToLayers((Layer[]) evt.getNewValue());
}
}
/**
* Method to add the StatusLightPanel as an Information Display
* Event listener to a list of layers. Should not be called
* directly, because it is called as a result of the property
* change (layers) of the MapBean.
*/
protected void listenToLayers(Layer[] newLayers) {
int i;
if (layers != null) {
for (i = 0; i < layers.length; i++) {
if (layers[i] != null) {
layers[i].removeLayerStatusListener(this);
boolean stillOnMap = false;
for (int j = 0; j < newLayers.length; j++) {
if (layers[i] == newLayers[j]) {
stillOnMap = true;
}
}
if (!stillOnMap) {
JButton light = (JButton) statusLights.get(layers[i]);
light.removeActionListener(layers[i]);
statusLights.remove(layers[i]);
}
layers[i] = null;
}
}
}
if (newLayers != null) {
for (i = 0; i < newLayers.length; i++) {
// Could call addLayer(newLayers[i] instead, but I
// don't want to call setStatusBar unnecessarily.
if (newLayers[i] != null) {
newLayers[i].addLayerStatusListener(this);
getStatusLightForLayer(newLayers[i]);
} else {
Debug.message("statuslights",
"StatusLightPanel: null layer in new layer array");
}
}
layers = newLayers;
reset();
}
}
/**
* This method is really a get and set. The JLabel status gif is
* returned out of the HashTable for the layer. If the layer isn't
* in the HashTable, the new light is created, and a tooltip for
* it is set.
*
* @param layer the layer for the needed light.
* @return JLabel representing the status of the layer.
*/
protected JButton getStatusLightForLayer(Layer layer) {
if (layer == null) {
return null;
}
JButton newLight = (JButton) statusLights.get(layer);
if (newLight == null) {
// newLight = new JButton(greyIcon);
newLight = new JButton(greenIcon);
newLight.setToolTipText(layer.getName());
newLight.setMargin(new Insets(2, 1, 2, 1));
statusLights.put(layer, newLight);
if (lightTriggers) {
newLight.setActionCommand(Layer.DisplayPaletteCmd);
newLight.addActionListener(layer);
newLight.setBorderPainted(true);
} else {
newLight.setBorderPainted(false);
}
}
return newLight;
}
/**
* The method that updates the StatusLight display with the
* correct layer status representation.
*/
public void reset() {
removeAll();
if (container != null) {
container.removeAll();
}
if (lightTriggers) {
if (container == null) {
container = new JToolBar();
container.setFloatable(false);
}
add(container);
} else {
container = null;
}
if (layers != null) {
for (int i = 0; i < layers.length; i++) {
JButton statusgif = getStatusLightForLayer(layers[i]);
if (statusgif != null) {
if (lightTriggers) {
container.add(statusgif);
} else {
add(statusgif);
}
}
}
}
revalidate();
}
/**
* Set the light in the window to be a certain color, depending on
* the working status. If the layer light isn't stored, the whole
* thing is blown off. If the icon is red, then the watch cursor
* is requested, if allowed by showWaitCursor.
*
* @param layer the layer to update.
* @param icon the icon light representing the status.
*/
protected void setLayerStatus(Layer layer, Icon icon) {
JButton statusgif = (JButton) statusLights.get(layer);
if (statusgif != null) {
statusgif.setIcon(icon);
if (this.map != null) {
if (icon == redIcon && showWaitCursor) {
// this.map.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
waitingForLayers = true;
} else if (icon == greenIcon) {
Enumeration lights = statusLights.elements();
waitingForLayers = false;
while (lights.hasMoreElements()) {
JButton light = (JButton) lights.nextElement();
if (light.getIcon() == redIcon) {
waitingForLayers = true;
}
}
// if (!waitingForLayers) {
// if (currentMapBeanCursor != null) {
// this.map.setCursor(currentMapBeanCursor);
// } else {
// resetCursor();
// }
// }
}
}
}
}
///////////////////////////////////////////
// LayerStatusListener interface
/**
* Update the Layer status.
*
* @param evt LayerStatusEvent
*/
public void updateLayerStatus(LayerStatusEvent evt) {
switch (evt.getStatus()) {
// these need to be coordinated correctly by the Layer,
// otherwise
// we'll get phantom status ticks or maybe an ArrayOutOfBounds
// negative...
case LayerStatusEvent.START_WORKING:
setLayerStatus((Layer) evt.getSource(), redIcon);
break;
case LayerStatusEvent.STATUS_UPDATE:
break;
case LayerStatusEvent.FINISH_WORKING:
setLayerStatus((Layer) evt.getSource(), greenIcon);
break;
default:
System.err.println("InformationDelegator.updateLayerStatus(): "
+ "unknown status: " + evt.getStatus());
break;
}
}
/**
* Called when an object has been added to the BeanContext. The
* InformationDelegator will look for certain objects it needs.
*/
public void findAndInit(Object someObj) {
if (someObj instanceof MapBean) {
setMap((MapBean) someObj);
}
}
/**
* Called when an object is being removed from the BeanContext.
* Will cause the object to be disconnected from the
* InformationDelegator if it is being used.
*/
public void findAndUndo(Object someObj) {
if (someObj instanceof MapBean) {
setMap(null);
}
}
public void setProperties(String prefix, Properties props) {
setPropertyPrefix(prefix);
prefix = PropUtils.getScopedPropertyPrefix(prefix);
setLightTriggers(PropUtils.booleanFromProperties(props, prefix
+ LightTriggersProperty, lightTriggers));
}
public void setLightTriggers(boolean set) {
lightTriggers = set;
statusLights.clear();
reset();
}
public boolean getLightTriggers() {
return lightTriggers;
}
public Properties getProperties(Properties props) {
if (props == null) {
props = new Properties();
}
String prefix = PropUtils.getScopedPropertyPrefix(this);
props.put(prefix + LightTriggersProperty,
new Boolean(lightTriggers).toString());
return props;
}
public Properties getPropertyInfo(Properties props) {
if (props == null) {
props = new Properties();
}
PropUtils.setI18NPropertyInfo(i18n,
props,
StatusLightPanel.class,
LightTriggersProperty,
"Enable Triggers",
"Layer status indicators should launch layer palettes.",
"com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
return props;
}
}