// **********************************************************************
//
// <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/BasicMapPanel.java,v $
// $RCSfile: BasicMapPanel.java,v $
// $Revision: 1.17 $
// $Date: 2004/10/14 18:05:47 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.Toolkit;
import java.net.URL;
import java.util.Collection;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import com.bbn.openmap.BufferedLayerMapBean;
import com.bbn.openmap.Environment;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.MapHandler;
import com.bbn.openmap.PropertyHandler;
import com.bbn.openmap.gui.menu.MenuList;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.proj.ProjectionFactory;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.PropUtils;
/**
* The BasicMapPanel is a MapPanel and OMComponentPanel that is the heart of the
* OpenMap application framework. It can be used in a application or applet. The
* Panel has a BorderLayout, and creates a MapBean for its center area. It
* creates a MapHandler to use to hold all of its OpenMap components, and uses
* the PropertyHandler given to it in its constructor to create and configure
* all of the application components. The best way to add components to the
* MapPanel is to get the MapHandler from it and add components to that. The
* BasicMapPanel also adds itself to its MapHandler, so when the PropertyHandler
* adds MapPanelChildren components to the MapHandler, the BasicMapPanel is able
* to find them via the findAndInit method. By default, the BasicMapPanel looks
* for MapPanelChildren and asks them for where they would prefer to be located
* (BorderLayout.NORTH, BorderLayout.SOUTH, BorderLayout.EAST,
* BorderLayout.WEST). If you extend this component, though, other components
* could be found via that same findAndInit method.
* <p>
* If a property prefix is set on this MapPanel, that property prefix can be
* used to designate MapPanelChild objects for this MapPanel. The setName
* variable should be set to true, and the children's parent name should match
* whatever property prefix is given to the panel.
*
*/
public class BasicMapPanel extends OMComponentPanel implements MapPanel {
public static Logger logger = Logger.getLogger("com.bbn.openmap.gui.MapPanel");
public final static String SET_NAME_PROPERTY = "setName";
protected MapHandler mapHandler;
protected MapBean mapBean;
protected PropertyHandler propertyHandler;
protected MenuList menuList;
protected boolean setName = false;
/**
* Creates an empty MapPanel that creates its own empty PropertyHandler. The
* MapPanel will contain a MapBean, a MapHandler, and a PropertyHandler with
* no properties. The constructor to use to create a blank map framework to
* add components to.
*/
public BasicMapPanel() {
this(new PropertyHandler(new Properties()), false);
}
/**
* Create a MapPanel with the option of delaying the search for properties
* until the <code>create()</code> call is made.
*
* @param delayCreation true to let the MapPanel know that the artful
* programmer will call <code>create()</code>
*/
public BasicMapPanel(boolean delayCreation) {
this(null, delayCreation);
}
/**
* Create a MapPanel that configures itself with the properties contained in
* the PropertyHandler provided. If the PropertyHandler is null, a new one
* will be created.
*/
public BasicMapPanel(PropertyHandler propertyHandler) {
this(propertyHandler, false);
}
/**
* Create a MapPanel that configures itself with properties contained in the
* PropertyHandler provided, and with the option of delaying the search for
* properties until the <code>create()</code> call is made.
*
* @param delayCreation true to let the MapPanel know that the artful
* programmer will call <code>create()</code>
*/
public BasicMapPanel(PropertyHandler propertyHandler, boolean delayCreation) {
MapHandler mh = getMapHandler();
mh.add(this);
setPropertyHandler(propertyHandler);
if (!delayCreation) {
create();
}
}
/**
* Sets the properties in the PropertyHandler managed by this BasicMapPanel.
* This method is intended to be called when the PropertyHandler is set on
* the panel, in order for configuration parameters to be set on this panel
* before configuration.
*
* @param prefix property prefix for scoping properties for this panel
* @param props the properties to search for properties in.
*/
public void setProperties(String prefix, Properties props) {
String scopedPrefix = PropUtils.getScopedPropertyPrefix(prefix);
setSetName(PropUtils.booleanFromProperties(props, scopedPrefix
+ SET_NAME_PROPERTY, isSetName()));
if (isSetName()) {
super.setProperties(prefix, props);
}
}
/**
* The method that triggers setLayout() and createComponents() to be called.
* If you've told the BasicMapPanel to delay creation, you should call this
* method to trigger the PropertyHandler to create components based on the
* contents of its properties.
*/
public void create() {
setLayout(createLayoutManager());
createComponents();
}
/**
* The constructor calls this method that sets the LayoutManager for this
* MapPanel. It returns a BorderLayout by default, but this method can be
* overridden to change how the MapPanel places components. If you change
* what this method returns, you should also change how components are added
* in the findAndInit() method.
*/
protected LayoutManager createLayoutManager() {
return new BorderLayout();
}
/**
* Position the map bean in this panel according to the layout manger.
* Defaults to BorderLayout.CENTER.
*/
protected void addMapBeanToPanel(MapBean map) {
add(map, BorderLayout.CENTER);
}
/**
* The constructor calls this method that creates the MapHandler and
* MapBean, and then tells the PropertyHandler to create the components
* described in its properties. This method calls getMapHandler() and
* getMapBean(). If the PropertyHandler is not null, it will be called to
* created components based on its properties, and those components will be
* added to the MapHandler in this MapPanel.
*/
protected void createComponents() {
// Make this call first to load the properties into
// Environment, before the MapBean gets created.
PropertyHandler ph = getPropertyHandler();
// Make sure the MapBean is created and added to the
// MapHandler.
MapBean mb = getMapBean();
MapHandler mh = getMapHandler();
ph.createComponents(getMapHandler());
// At this point, check the MapHandler to see if a
// ProjectionFactory has been added. If it hasn't, create one
// with the default ProjectionLoaders. We might want to
// remove this at some point, but not having it here will
// catch some people by surprise when 4.6.1 comes out.
Object obj = mh.get(com.bbn.openmap.proj.ProjectionFactory.class);
if (obj == null) {
Debug.message("basic", "BasicMapPanel adding ProjectionFactory and projections to MapHandler since there are none to be found.");
mh.add(ProjectionFactory.loadDefaultProjections());
}
// Environment will only get loaded after the property file is
// read.
mb.setBckgrnd(Environment.getCustomBackgroundColor());
Projection proj = mb.getProjectionFactory().getDefaultProjectionFromEnvironment(Environment.getInstance());
if (Debug.debugging("mappanel")) {
Debug.output("MapPanel: creating MapBean with initial projection " + proj);
}
mb.setProjection(proj);
}
/**
* MapPanel method. Get the MapBean used for the MapPanel. If the MapBean is
* null, calls createMapBean() which will create a BufferedLayerMapBean and
* add it to the MapHandler via a setMapBean call. If you want something
* different, override this method.
*/
public MapBean getMapBean() {
if (mapBean == null) {
setMapBean(BasicMapPanel.createMapBean());
}
return mapBean;
}
/**
* Set the map bean used in this map panel, replace the map bean in the
* MapHandler if there isn't already one, or if the policy allows
* replacement. The MapHandler will be created if it doesn't exist via a
* getMapHandler() method call.
*
* @throws MultipleSoloMapComponentException if there is already a map bean
* in the map handler and the policy is to reject duplicates (since
* the MapBean is a SoloMapComponent).
*/
public void setMapBean(MapBean bean) {
if (bean == null && mapBean != null) {
// remove the current MapBean from the application...
getMapHandler().remove(mapBean);
}
mapBean = bean;
if (mapBean != null) {
getMapHandler().add(mapBean);
addMapBeanToPanel(mapBean);
}
}
/**
* Get the PropertyHandler containing properties used to configure the
* panel, creating it if it doesn't exist.
*/
public PropertyHandler getPropertyHandler() {
if (propertyHandler == null) {
setPropertyHandler(new PropertyHandler());
}
return propertyHandler;
}
/**
* Set the PropertyHandler containing the properties used to configure this
* panel. Adds the PropertyHandler to the MapHandler. If the MapHandler
* isn't set at this point, it will be created via a getMapHandler() call.
*/
public void setPropertyHandler(PropertyHandler handler) {
propertyHandler = handler;
if (handler != null) {
getMapHandler().add(handler);
setProperties(handler.getPropertyPrefix(), handler.getProperties());
}
}
/**
* MapPanel method. Get the MapHandler used for the MapPanel. Creates a
* standard MapHandler if it hasn't been created yet.
*/
public MapHandler getMapHandler() {
if (mapHandler == null) {
mapHandler = new MapHandler();
}
return mapHandler;
}
/**
* MapPanel method. Get a JMenuBar containing menus created from properties.
*/
public JMenuBar getMapMenuBar() {
if (menuList != null) {
return menuList.getMenuBar();
} else {
return null;
}
}
/**
* MapPanel method. Get a JMenu containing sub-menus created from
* properties.
*/
public JMenu getMapMenu() {
if (menuList != null) {
return menuList.getMenu();
} else {
return null;
}
}
// Map Component Methods:
// //////////////////////
/**
* Adds a component to the map bean context. This makes the
* <code>mapComponent</code> available to the map layers and other
* components.
*
* @param mapComponent a component to be added to the map bean context
* @throws com.bbn.openmap.MultipleSoloMapComponentException if mapComponent is a
* SoloMapComponent and another instance already exists and the
* policy is a reject policy.
*/
public void addMapComponent(Object mapComponent) {
if (mapComponent != null) {
getMapHandler().add(mapComponent);
}
}
/**
* Remove a component from the map bean context.
*
* @param mapComponent a component to be removed to the map bean context
* @return true if the mapComponent was removed.
*/
public boolean removeMapComponent(Object mapComponent) {
if (mapComponent != null) {
return getMapHandler().remove(mapComponent);
}
return true;
}
/**
* Given a Class, find the object in the MapHandler. If the class is not a
* SoloMapComponent and there are more than one of them in the MapHandler,
* you will get the first one found.
*/
public Object getMapComponentByType(Class<?> c) {
return getMapHandler().get(c);
}
/**
* Get all of the mapComponents that are of the given class type.
*/
public Collection<?> getMapComponentsByType(Class<?> c) {
return getMapHandler().getAll(c);
}
/**
* Find the object with the given prefix by looking it up in the prefix
* librarian in the MapHandler.
*/
public Object getMapComponent(String prefix) {
return getPropertyHandler().get(prefix);
}
/**
* The BasicMapPanel looks for MapPanelChild components, finds out from them
* where they prefer to be placed, and adds them.
*/
public void findAndInit(Object someObj) {
if (someObj instanceof MapPanelChild && someObj instanceof Component) {
String parentName = ((MapPanelChild) someObj).getParentName();
boolean hasNamedParent = parentName != null && parentName.trim().length() != 0;
String myName = getPropertyPrefix();
boolean hasName = myName != null && myName.trim().length() != 0;
@SuppressWarnings("null")
boolean makeMyChild = (hasName && hasNamedParent && myName.equalsIgnoreCase(parentName))
|| (!hasName && !hasNamedParent);
if (makeMyChild) {
if (Debug.debugging("basic")) {
Debug.output("MapPanel: adding " + someObj.getClass().getName());
}
MapPanelChild mpc = (MapPanelChild) someObj;
addMapPanelChild(mpc);
invalidate();
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("MapPanel with name: " + myName + " not adding child ("
+ someObj.getClass().getName() + ") looking for: " + parentName);
}
}
}
if (someObj instanceof MenuList) {
menuList = (MenuList) someObj;
}
}
/**
* Add a child to the MapPanel.
*/
protected void addMapPanelChild(MapPanelChild mpc) {
add((Component) mpc, mpc.getPreferredLocation());
}
/**
* The MapPanel looks for MapPanelChild components and removes them from
* itself.
*/
public void findAndUndo(Object someObj) {
if (someObj instanceof MapPanelChild && someObj instanceof Component) {
if (Debug.debugging("basic")) {
Debug.output("MapPanel: removing " + someObj.getClass().getName());
}
remove((Component) someObj);
invalidate();
}
if (someObj instanceof MenuList && menuList == someObj) {
menuList = null;
}
if (this.equals(someObj)) {
dispose();
}
}
/**
* Sets the MapBean variable to null and removes all children.
*/
public void dispose() {
setMapBean(null);
setLayout(null);
removeAll();
}
// MapBean Methods:
// ////////////////
/**
* A static method that creates a MapBean with it's projection set to the
* values set in the Environment. Also creates a BevelBorder.LOWERED border
* for the MapBean.
*/
public static MapBean createMapBean() {
int envWidth = Environment.getInteger(Environment.Width, MapBean.DEFAULT_WIDTH);
int envHeight = Environment.getInteger(Environment.Height, MapBean.DEFAULT_HEIGHT);
if (envWidth <= 0 || envHeight <= 0) {
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
if (envWidth <= 0) {
envWidth = (int) d.getWidth();
}
if (envHeight <= 0) {
envHeight = (int) d.getHeight();
}
}
return createMapBean(null, new BevelBorder(BevelBorder.LOWERED));
}
/**
* A static method that creates a MapBean and sets its projection and border
* to the values given.
*/
public static MapBean createMapBean(Projection proj, Border border) {
MapBean mapBeano = new BufferedLayerMapBean();
mapBeano.setBorder(border);
if (proj != null) {
mapBeano.setProjection(proj);
mapBeano.setPreferredSize(new Dimension(proj.getWidth(), proj.getHeight()));
}
return mapBeano;
}
// Property Functions:
// ///////////////////
/**
* Get the current properties.
*/
public Properties getProperties() {
return getPropertyHandler().getProperties();
}
/**
* Remove an existing property if it exists.
*
* @return true if a property was actually removed.
*/
public boolean removeProperty(String property) {
return getPropertyHandler().removeProperty(property);
}
/**
* Add (or overwrite) a property to the current properties
*/
public void addProperty(String property, String value) {
getPropertyHandler().addProperty(property, value);
}
/**
* Add in the properties from the given URL. Any existing properties will be
* overwritten except for openmap.components, openmap.layers and
* openmap.startUpLayers which will be appended.
*/
public void addProperties(URL urlToProperties) {
getPropertyHandler().addProperties(urlToProperties);
}
/**
* Add in the properties from the given source, which can be a resource,
* file or URL. Any existing properties will be overwritten except for
* openmap.components, openmap.layers and openmap.startUpLayers which will
* be appended.
*
* @throws MalformedURLException if propFile doesn't resolve properly.
*/
public void addProperties(String propFile) throws java.net.MalformedURLException {
getPropertyHandler().addProperties(propFile);
}
/**
* remove a marker from a space delimited set of properties.
*/
public void removeMarker(String property, String marker) {
getPropertyHandler().removeMarker(property, marker);
}
/**
* Add in the properties from the given Properties object. Any existing
* properties will be overwritten except for openmap.components,
* openmap.layers and openmap.startUpLayers which will be appended.
*/
public void addProperties(Properties p) {
getPropertyHandler().addProperties(p);
}
/**
* Append the given property into the current properties
*/
public void appendProperty(String property, Properties src) {
getPropertyHandler().appendProperty(property, src);
}
/**
* Append the given property into the current properties
*/
public void appendProperty(String property, String value) {
getPropertyHandler().appendProperty(property, value);
}
/**
* Prepend the given property into the current properties
*/
public void prependProperty(String property, Properties src) {
getPropertyHandler().prependProperty(property, src);
}
/**
* Prepend the given property into the current properties
*/
public void prependProperty(String property, String value) {
getPropertyHandler().prependProperty(property, value);
}
/**
* @return the setName setting, whether the property prefix will be set on
* the MapPanel when setProperties is called.
*/
public boolean isSetName() {
return setName;
}
/**
* @param setName whether the property prefix provided in setProperties will
* be set on the MapPanel. If it is, then when MapPanelChild objects
* are found in the MapHandler, they will only be added if the names
* match.
*/
public void setSetName(boolean setName) {
this.setName = setName;
}
}