source: josm/trunk/src/org/openstreetmap/josm/gui/layer/Layer.java @ 5241

Revision 5048, 12.9 KB checked in by akks, 3 months ago (diff)

fix #7429 - Shortcut redefinition in Save As feature

  • Property svn:eol-style set to native
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui.layer;
4
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Color;
8import java.awt.Component;
9import java.awt.Graphics2D;
10import java.awt.event.ActionEvent;
11import java.beans.PropertyChangeListener;
12import java.beans.PropertyChangeSupport;
13import java.io.File;
14import java.util.List;
15
16import javax.swing.AbstractAction;
17import javax.swing.Action;
18import javax.swing.Icon;
19import javax.swing.JOptionPane;
20import javax.swing.JSeparator;
21
22import org.openstreetmap.josm.Main;
23import org.openstreetmap.josm.actions.GpxExportAction;
24import org.openstreetmap.josm.actions.SaveAction;
25import org.openstreetmap.josm.actions.SaveAsAction;
26import org.openstreetmap.josm.data.Bounds;
27import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
28import org.openstreetmap.josm.data.projection.Projection;
29import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
30import org.openstreetmap.josm.gui.MapView;
31import org.openstreetmap.josm.tools.Destroyable;
32import org.openstreetmap.josm.tools.ImageProvider;
33
34/**
35 * A layer encapsulates the gui component of one dataset and its representation.
36 *
37 * Some layers may display data directly imported from OSM server. Other only
38 * display background images. Some can be edited, some not. Some are static and
39 * other changes dynamically (auto-updated).
40 *
41 * Layers can be visible or not. Most actions the user can do applies only on
42 * selected layers. The available actions depend on the selected layers too.
43 *
44 * All layers are managed by the MapView. They are displayed in a list to the
45 * right of the screen.
46 *
47 * @author imi
48 */
49abstract public class Layer implements Destroyable, MapViewPaintable, ProjectionChangeListener {
50
51    public interface LayerAction {
52        boolean supportLayers(List<Layer> layers);
53        Component createMenuComponent();
54    }
55
56    public interface MultiLayerAction {
57        Action getMultiLayerAction(List<Layer> layers);
58    }
59
60
61    /**
62     * Special class that can be returned by getMenuEntries when JSeparator needs to be created
63     *
64     */
65    public static class SeparatorLayerAction extends AbstractAction implements LayerAction {
66        public static final SeparatorLayerAction INSTANCE = new SeparatorLayerAction();
67        @Override
68        public void actionPerformed(ActionEvent e) {
69            throw new UnsupportedOperationException();
70        }
71        @Override
72        public Component createMenuComponent() {
73            return new JSeparator();
74        }
75        @Override
76        public boolean supportLayers(List<Layer> layers) {
77            return false;
78        }
79    }
80
81    static public final String VISIBLE_PROP = Layer.class.getName() + ".visible";
82    static public final String OPACITY_PROP = Layer.class.getName() + ".opacity";
83    static public final String NAME_PROP = Layer.class.getName() + ".name";
84
85    /** keeps track of property change listeners */
86    protected PropertyChangeSupport propertyChangeSupport;
87
88    /**
89     * The visibility state of the layer.
90     *
91     */
92    private boolean visible = true;
93
94    /**
95     * The opacity of the layer.
96     *
97     */
98    private double opacity = 1;
99
100    /**
101     * The layer should be handled as a background layer in automatic handling
102     *
103     */
104    private boolean background = false;
105
106    /**
107     * The name of this layer.
108     *
109     */
110    private  String name;
111
112    /**
113     * If a file is associated with this layer, this variable should be set to it.
114     */
115    private File associatedFile;
116
117    /**
118     * Create the layer and fill in the necessary components.
119     */
120    public Layer(String name) {
121        this.propertyChangeSupport = new PropertyChangeSupport(this);
122        setName(name);
123    }
124
125    /**
126     * Paint the dataset using the engine set.
127     * @param mv The object that can translate GeoPoints to screen coordinates.
128     */
129    @Override
130    abstract public void paint(Graphics2D g, MapView mv, Bounds box);
131    /**
132     * Return a representative small image for this layer. The image must not
133     * be larger than 64 pixel in any dimension.
134     */
135    abstract public Icon getIcon();
136
137    /**
138     * Return a Color for this layer. Return null when no color specified.
139     * @param ignoreCustom Custom color should return null, as no default color
140     *      is used. When this is true, then even for custom coloring the base
141     *      color is returned - mainly for layer internal use.
142     */
143    public Color getColor(boolean ignoreCustom) {
144        return null;
145    }
146
147    /**
148     * @return A small tooltip hint about some statistics for this layer.
149     */
150    abstract public String getToolTipText();
151
152    /**
153     * Merges the given layer into this layer. Throws if the layer types are
154     * incompatible.
155     * @param from The layer that get merged into this one. After the merge,
156     *      the other layer is not usable anymore and passing to one others
157     *      mergeFrom should be one of the last things to do with a layer.
158     */
159    abstract public void mergeFrom(Layer from);
160
161    /**
162     * @param other The other layer that is tested to be mergable with this.
163     * @return Whether the other layer can be merged into this layer.
164     */
165    abstract public boolean isMergable(Layer other);
166
167    abstract public void visitBoundingBox(BoundingXYVisitor v);
168
169    abstract public Object getInfoComponent();
170
171    /**
172     * Returns list of actions. Action can implement LayerAction interface when it needs to be represented by other
173     * menu component than JMenuItem or when it supports multiple layers. Actions that support multiple layers should also
174     * have correct equals implementation.
175     *
176     * Use SeparatorLayerAction.INSTANCE instead of new JSeparator
177     *
178     */
179    abstract public Action[] getMenuEntries();
180
181    /**
182     * Called, when the layer is removed from the mapview and is going to be
183     * destroyed.
184     *
185     * This is because the Layer constructor can not add itself safely as listener
186     * to the layerlist dialog, because there may be no such dialog yet (loaded
187     * via command line parameter).
188     */
189    @Override
190    public void destroy() {}
191
192    public File getAssociatedFile() { return associatedFile; }
193    public void setAssociatedFile(File file) { associatedFile = file; }
194
195    /**
196     * Replies the name of the layer
197     *
198     * @return the name of the layer
199     */
200    public String getName() {
201        return name;
202    }
203
204    /**
205     * Sets the name of the layer
206     *
207     *@param name the name. If null, the name is set to the empty string.
208     *
209     */
210    public void setName(String name) {
211        if (name == null) {
212            name = "";
213        }
214        String oldValue = this.name;
215        this.name = name;
216        if (!this.name.equals(oldValue)) {
217            propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name);
218        }
219    }
220
221    /**
222     * Replies true if this layer is a background layer
223     *
224     * @return true if this layer is a background layer
225     */
226    public boolean isBackgroundLayer() {
227        return background;
228    }
229
230    /**
231     * Sets whether this layer is a background layer
232     *
233     * @param background true, if this layer is a background layer
234     */
235    public void setBackgroundLayer(boolean background) {
236        this.background = background;
237    }
238
239    /**
240     * Sets the visibility of this layer. Emits property change event for
241     * property {@see #VISIBLE_PROP}.
242     *
243     * @param visible true, if the layer is visible; false, otherwise.
244     */
245    public void setVisible(boolean visible) {
246        boolean oldValue = isVisible();
247        this.visible  = visible;
248        if (visible && opacity == 0) {
249            setOpacity(1);
250        } else if (oldValue != isVisible()) {
251            fireVisibleChanged(oldValue, isVisible());
252        }
253    }
254
255    /**
256     * Replies true if this layer is visible. False, otherwise.
257     * @return  true if this layer is visible. False, otherwise.
258     */
259    public boolean isVisible() {
260        return visible && opacity != 0;
261    }
262
263    public double getOpacity() {
264        return opacity;
265    }
266
267    public void setOpacity(double opacity) {
268        if (!(opacity >= 0 && opacity <= 1))
269            throw new IllegalArgumentException("Opacity value must be between 0 and 1");
270        double oldOpacity = getOpacity();
271        boolean oldVisible = isVisible();
272        this.opacity = opacity;
273        if (oldOpacity != getOpacity()) {
274            fireOpacityChanged(oldOpacity, getOpacity());
275        }
276        if (oldVisible != isVisible()) {
277            fireVisibleChanged(oldVisible, isVisible());
278        }
279    }
280
281    /**
282     * Toggles the visibility state of this layer.
283     */
284    public void toggleVisible() {
285        setVisible(!isVisible());
286    }
287
288    /**
289     * Adds a {@see PropertyChangeListener}
290     *
291     * @param listener the listener
292     */
293    public void addPropertyChangeListener(PropertyChangeListener listener) {
294        propertyChangeSupport.addPropertyChangeListener(listener);
295    }
296
297    /**
298     * Removes a {@see PropertyChangeListener}
299     *
300     * @param listener the listener
301     */
302    public void removePropertyChangeListener(PropertyChangeListener listener) {
303        propertyChangeSupport.removePropertyChangeListener(listener);
304    }
305
306    /**
307     * fires a property change for the property {@see #VISIBLE_PROP}
308     *
309     * @param oldValue the old value
310     * @param newValue the new value
311     */
312    protected void fireVisibleChanged(boolean oldValue, boolean newValue) {
313        propertyChangeSupport.firePropertyChange(VISIBLE_PROP, oldValue, newValue);
314    }
315
316    /**
317     * fires a property change for the property {@see #OPACITY_PROP}
318     *
319     * @param oldValue the old value
320     * @param newValue the new value
321     */
322    protected void fireOpacityChanged(double oldValue, double newValue) {
323        propertyChangeSupport.firePropertyChange(OPACITY_PROP, oldValue, newValue);
324    }
325
326    /**
327     * Check changed status of layer
328     *
329     * @return True if layer was changed since last paint
330     */
331    public boolean isChanged() {
332        return true;
333    }
334
335    /**
336     * allows to check whether a projection is supported or not
337     *
338     * @return True if projection is supported for this layer
339     */
340    public boolean isProjectionSupported(Projection proj) {
341        return true;
342    }
343
344    /**
345     * Specify user information about projections
346     *
347     * @return User readable text telling about supported projections
348     */
349    public String nameSupportedProjections() {
350        return tr("All projections are supported");
351    }
352
353    /**
354     * The action to save a layer
355     *
356     */
357    public static class LayerSaveAction extends AbstractAction {
358        private Layer layer;
359        public LayerSaveAction(Layer layer) {
360            putValue(SMALL_ICON, ImageProvider.get("save"));
361            putValue(SHORT_DESCRIPTION, tr("Save the current data."));
362            putValue(NAME, tr("Save"));
363            setEnabled(true);
364            this.layer = layer;
365        }
366
367        public void actionPerformed(ActionEvent e) {
368            SaveAction.getInstance().doSave(layer);
369        }
370    }
371
372    public static class LayerSaveAsAction extends AbstractAction {
373        private Layer layer;
374        public LayerSaveAsAction(Layer layer) {
375            putValue(SMALL_ICON, ImageProvider.get("save_as"));
376            putValue(SHORT_DESCRIPTION, tr("Save the current data to a new file."));
377            putValue(NAME, tr("Save As..."));
378            setEnabled(true);
379            this.layer = layer;
380        }
381
382        public void actionPerformed(ActionEvent e) {
383            SaveAsAction.getInstance().doSave(layer);
384        }
385    }
386
387    public static class LayerGpxExportAction extends AbstractAction {
388        private Layer layer;
389        public LayerGpxExportAction(Layer layer) {
390            putValue(SMALL_ICON, ImageProvider.get("exportgpx"));
391            putValue(SHORT_DESCRIPTION, tr("Export the data to GPX file."));
392            putValue(NAME, tr("Export to GPX..."));
393            setEnabled(true);
394            this.layer = layer;
395        }
396
397        public void actionPerformed(ActionEvent e) {
398            new GpxExportAction().export(layer);
399        }
400    }
401
402    /* --------------------------------------------------------------------------------- */
403    /* interface ProjectionChangeListener                                                */
404    /* --------------------------------------------------------------------------------- */
405    @Override
406    public void projectionChanged(Projection oldValue, Projection newValue) {
407        if(!isProjectionSupported(newValue)) {
408              JOptionPane.showMessageDialog(Main.parent,
409                  tr("The layer {0} does not support the new projection {1}.\n{2}\n"
410                  + "Change the projection again or remove the layer.",
411                      getName(), newValue.toCode(), nameSupportedProjections()),
412                      tr("Warning"),
413                      JOptionPane.WARNING_MESSAGE);
414        }
415    }
416}
Note: See TracBrowser for help on using the repository browser.