// License: GPL. See LICENSE file for details. package org.openstreetmap.josm.gui.layer; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Component; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import javax.swing.AbstractAction; import javax.swing.Icon; import org.openstreetmap.josm.actions.GpxExportAction; import org.openstreetmap.josm.actions.SaveAction; import org.openstreetmap.josm.actions.SaveAsAction; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; import org.openstreetmap.josm.gui.MapView; import org.openstreetmap.josm.tools.Destroyable; import org.openstreetmap.josm.tools.ImageProvider; /** * A layer encapsulates the gui component of one dataset and its representation. * * Some layers may display data directly imported from OSM server. Other only * display background images. Some can be edited, some not. Some are static and * other changes dynamically (auto-updated). * * Layers can be visible or not. Most actions the user can do applies only on * selected layers. The available actions depend on the selected layers too. * * All layers are managed by the MapView. They are displayed in a list to the * right of the screen. * * @author imi */ abstract public class Layer implements Destroyable, MapViewPaintable { static public final String VISIBLE_PROP = Layer.class.getName() + ".visible"; static public final String NAME_PROP = Layer.class.getName() + ".name"; /** keeps track of property change listeners */ protected PropertyChangeSupport propertyChangeSupport; /** * The visibility state of the layer. * */ private boolean visible = true; /** * The layer should be handled as a background layer in automatic handling * */ private boolean background = false; /** * The name of this layer. * */ private String name; /** * If a file is associated with this layer, this variable should be set to it. */ private File associatedFile; /** * Create the layer and fill in the necessary components. */ public Layer(String name) { this.propertyChangeSupport = new PropertyChangeSupport(this); setName(name); } /** * Paint the dataset using the engine set. * @param mv The object that can translate GeoPoints to screen coordinates. */ abstract public void paint(Graphics2D g, MapView mv, Bounds box); /** * Return a representative small image for this layer. The image must not * be larger than 64 pixel in any dimension. */ abstract public Icon getIcon(); /** * @return A small tooltip hint about some statistics for this layer. */ abstract public String getToolTipText(); /** * Merges the given layer into this layer. Throws if the layer types are * incompatible. * @param from The layer that get merged into this one. After the merge, * the other layer is not usable anymore and passing to one others * mergeFrom should be one of the last things to do with a layer. */ abstract public void mergeFrom(Layer from); /** * @param other The other layer that is tested to be mergable with this. * @return Whether the other layer can be merged into this layer. */ abstract public boolean isMergable(Layer other); abstract public void visitBoundingBox(BoundingXYVisitor v); abstract public Object getInfoComponent(); abstract public Component[] getMenuEntries(); /** * Called, when the layer is removed from the mapview and is going to be * destroyed. * * This is because the Layer constructor can not add itself safely as listener * to the layerlist dialog, because there may be no such dialog yet (loaded * via command line parameter). */ public void destroy() {} public File getAssociatedFile() { return associatedFile; } public void setAssociatedFile(File file) { associatedFile = file; } /** * Replies the name of the layer * * @return the name of the layer */ public String getName() { return name; } /** * Sets the name of the layer * *@param name the name. If null, the name is set to the empty string. * */ public void setName(String name) { if (name == null) { name = ""; } String oldValue = this.name; this.name = name; if (!this.name.equals(oldValue)) { propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name); } } /** * Replies true if this layer is a background layer * * @return true if this layer is a background layer */ public boolean isBackgroundLayer() { return background; } /** * Sets whether this layer is a background layer * * @param background true, if this layer is a background layer */ public void setBackgroundLayer(boolean background) { this.background = background; } /** * Sets the visibility of this layer. Emits property change event for * property {@see #VISIBLE_PROP}. * * @param visible true, if the layer is visible; false, otherwise. */ public void setVisible(boolean visible) { boolean oldValue = this.visible; this.visible = visible; if (oldValue != this.visible) { fireVisibleChanged(oldValue, this.visible); } } /** * Replies true if this layer is visible. False, otherwise. * @return true if this layer is visible. False, otherwise. */ public boolean isVisible() { return visible; } /** * Toggles the visibility state of this layer. */ public void toggleVisible() { setVisible(!isVisible()); } /** * Adds a {@see PropertyChangeListener} * * @param listener the listener */ public void addPropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(listener); } /** * Removes a {@see PropertyChangeListener} * * @param listener the listener */ public void removePropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.removePropertyChangeListener(listener); } /** * fires a property change for the property {@see #VISIBLE_PROP} * * @param oldValue the old value * @param newValue the new value */ protected void fireVisibleChanged(boolean oldValue, boolean newValue) { propertyChangeSupport.firePropertyChange(VISIBLE_PROP, oldValue, newValue); } /** * * * @return True if layer was changed since last paint */ public boolean isChanged() { return true; } /** * The action to save a layer * */ public static class LayerSaveAction extends AbstractAction { private Layer layer; public LayerSaveAction(Layer layer) { putValue(SMALL_ICON, ImageProvider.get("save")); putValue(SHORT_DESCRIPTION, tr("Save the current data.")); putValue(NAME, tr("Save")); setEnabled(true); this.layer = layer; } public void actionPerformed(ActionEvent e) { new SaveAction().doSave(layer); } } public static class LayerSaveAsAction extends AbstractAction { private Layer layer; public LayerSaveAsAction(Layer layer) { putValue(SMALL_ICON, ImageProvider.get("save_as")); putValue(SHORT_DESCRIPTION, tr("Save the current data to a new file.")); putValue(NAME, tr("Save As...")); setEnabled(true); this.layer = layer; } public void actionPerformed(ActionEvent e) { new SaveAsAction().doSave(layer); } } public static class LayerGpxExportAction extends AbstractAction { private Layer layer; public LayerGpxExportAction(Layer layer) { putValue(SMALL_ICON, ImageProvider.get("exportgpx")); putValue(SHORT_DESCRIPTION, tr("Export the data to GPX file.")); putValue(NAME, tr("Export to GPX...")); setEnabled(true); this.layer = layer; } public void actionPerformed(ActionEvent e) { new GpxExportAction().export(layer); } } }