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

Last change on this file since 6525 was 6084, checked in by bastiK, 11 years ago

see #8902 - add missing @Override annotations (patch by shinigami)

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