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

Last change on this file since 5048 was 5048, checked in by akks, 12 years ago

fix #7429 - Shortcut redefinition in Save As feature

  • Property svn:eol-style set to native
File size: 12.9 KB
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.