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

Last change on this file since 10588 was 10588, checked in by Don-vip, 8 years ago

see #11390, fix #13120 - Use a new memory manager for imagery layers (patch by michael2402) - gsoc-core - requires java 8

  • Property svn:eol-style set to native
File size: 18.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.event.ActionEvent;
9import java.beans.PropertyChangeListener;
10import java.beans.PropertyChangeSupport;
11import java.io.File;
12import java.util.List;
13
14import javax.swing.AbstractAction;
15import javax.swing.Action;
16import javax.swing.Icon;
17import javax.swing.JOptionPane;
18import javax.swing.JSeparator;
19import javax.swing.SwingUtilities;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.actions.GpxExportAction;
23import org.openstreetmap.josm.actions.SaveAction;
24import org.openstreetmap.josm.actions.SaveActionBase;
25import org.openstreetmap.josm.actions.SaveAsAction;
26import org.openstreetmap.josm.data.ProjectionBounds;
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.tools.Destroyable;
31import org.openstreetmap.josm.tools.ImageProvider;
32import org.openstreetmap.josm.tools.Utils;
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 */
49public abstract class Layer extends AbstractMapViewPaintable implements Destroyable, ProjectionChangeListener {
50
51 /**
52 * Action related to a single layer.
53 */
54 public interface LayerAction {
55
56 /**
57 * Determines if this action supports a given list of layers.
58 * @param layers list of layers
59 * @return {@code true} if this action supports the given list of layers, {@code false} otherwise
60 */
61 boolean supportLayers(List<Layer> layers);
62
63 /**
64 * Creates and return the menu component.
65 * @return the menu component
66 */
67 Component createMenuComponent();
68 }
69
70 /**
71 * Action related to several layers.
72 */
73 public interface MultiLayerAction {
74
75 /**
76 * Returns the action for a given list of layers.
77 * @param layers list of layers
78 * @return the action for the given list of layers
79 */
80 Action getMultiLayerAction(List<Layer> layers);
81 }
82
83 /**
84 * Special class that can be returned by getMenuEntries when JSeparator needs to be created
85 */
86 public static class SeparatorLayerAction extends AbstractAction implements LayerAction {
87 /** Unique instance */
88 public static final SeparatorLayerAction INSTANCE = new SeparatorLayerAction();
89
90 @Override
91 public void actionPerformed(ActionEvent e) {
92 throw new UnsupportedOperationException();
93 }
94
95 @Override
96 public Component createMenuComponent() {
97 return new JSeparator();
98 }
99
100 @Override
101 public boolean supportLayers(List<Layer> layers) {
102 return false;
103 }
104 }
105
106 public static final String VISIBLE_PROP = Layer.class.getName() + ".visible";
107 public static final String OPACITY_PROP = Layer.class.getName() + ".opacity";
108 public static final String NAME_PROP = Layer.class.getName() + ".name";
109 public static final String FILTER_STATE_PROP = Layer.class.getName() + ".filterstate";
110
111 /**
112 * keeps track of property change listeners
113 */
114 protected PropertyChangeSupport propertyChangeSupport;
115
116 /**
117 * The visibility state of the layer.
118 */
119 private boolean visible = true;
120
121 /**
122 * The opacity of the layer.
123 */
124 private double opacity = 1;
125
126 /**
127 * The layer should be handled as a background layer in automatic handling
128 */
129 private boolean background;
130
131 /**
132 * The name of this layer.
133 */
134 private String name;
135
136 /**
137 * This is set if user renamed this layer.
138 */
139 private boolean renamed;
140
141 /**
142 * If a file is associated with this layer, this variable should be set to it.
143 */
144 private File associatedFile;
145
146 /**
147 * Create the layer and fill in the necessary components.
148 * @param name Layer name
149 */
150 public Layer(String name) {
151 this.propertyChangeSupport = new PropertyChangeSupport(this);
152 setName(name);
153 }
154
155 /**
156 * Initialization code, that depends on Main.map.mapView.
157 *
158 * It is always called in the event dispatching thread.
159 * Note that Main.map is null as long as no layer has been added, so do
160 * not execute code in the constructor, that assumes Main.map.mapView is
161 * not null.
162 *
163 * If you need to execute code when this layer is added to the map view, use
164 * {@link #attachToMapView(org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent)}
165 */
166 public void hookUpMapView() {
167 }
168
169 /**
170 * Return a representative small image for this layer. The image must not
171 * be larger than 64 pixel in any dimension.
172 * @return layer icon
173 */
174 public abstract Icon getIcon();
175
176 /**
177 * Return a Color for this layer. Return null when no color specified.
178 * @param ignoreCustom Custom color should return null, as no default color
179 * is used. When this is true, then even for custom coloring the base
180 * color is returned - mainly for layer internal use.
181 * @return layer color
182 */
183 public Color getColor(boolean ignoreCustom) {
184 return null;
185 }
186
187 /**
188 * @return A small tooltip hint about some statistics for this layer.
189 */
190 public abstract String getToolTipText();
191
192 /**
193 * Merges the given layer into this layer. Throws if the layer types are
194 * incompatible.
195 * @param from The layer that get merged into this one. After the merge,
196 * the other layer is not usable anymore and passing to one others
197 * mergeFrom should be one of the last things to do with a layer.
198 */
199 public abstract void mergeFrom(Layer from);
200
201 /**
202 * @param other The other layer that is tested to be mergable with this.
203 * @return Whether the other layer can be merged into this layer.
204 */
205 public abstract boolean isMergable(Layer other);
206
207 public abstract void visitBoundingBox(BoundingXYVisitor v);
208
209 public abstract Object getInfoComponent();
210
211 /**
212 * Determines if info dialog can be resized (false by default).
213 * @return {@code true} if the info dialog can be resized, {@code false} otherwise
214 * @since 6708
215 */
216 public boolean isInfoResizable() {
217 return false;
218 }
219
220 /**
221 * Returns list of actions. Action can implement LayerAction interface when it needs to be represented by other
222 * menu component than JMenuItem or when it supports multiple layers. Actions that support multiple layers should also
223 * have correct equals implementation.
224 *
225 * Use {@link SeparatorLayerAction#INSTANCE} instead of new JSeparator
226 * @return menu actions for this layer
227 */
228 public abstract Action[] getMenuEntries();
229
230 /**
231 * Called, when the layer is removed from the mapview and is going to be destroyed.
232 *
233 * This is because the Layer constructor can not add itself safely as listener
234 * to the layerlist dialog, because there may be no such dialog yet (loaded
235 * via command line parameter).
236 */
237 @Override
238 public void destroy() {
239 // Override in subclasses if needed
240 }
241
242 public File getAssociatedFile() {
243 return associatedFile;
244 }
245
246 public void setAssociatedFile(File file) {
247 associatedFile = file;
248 }
249
250 /**
251 * Replies the name of the layer
252 *
253 * @return the name of the layer
254 */
255 public String getName() {
256 return name;
257 }
258
259 /**
260 * Sets the name of the layer
261 *
262 * @param name the name. If null, the name is set to the empty string.
263 */
264 public final void setName(String name) {
265 if (name == null) {
266 name = "";
267 }
268 String oldValue = this.name;
269 this.name = name;
270 if (!this.name.equals(oldValue)) {
271 propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name);
272 }
273 }
274
275 /**
276 * Rename layer and set renamed flag to mark it as renamed (has user given name).
277 *
278 * @param name the name. If null, the name is set to the empty string.
279 */
280 public final void rename(String name) {
281 renamed = true;
282 setName(name);
283 }
284
285 /**
286 * Replies true if this layer was renamed by user
287 *
288 * @return true if this layer was renamed by user
289 */
290 public boolean isRenamed() {
291 return renamed;
292 }
293
294 /**
295 * Replies true if this layer is a background layer
296 *
297 * @return true if this layer is a background layer
298 */
299 public boolean isBackgroundLayer() {
300 return background;
301 }
302
303 /**
304 * Sets whether this layer is a background layer
305 *
306 * @param background true, if this layer is a background layer
307 */
308 public void setBackgroundLayer(boolean background) {
309 this.background = background;
310 }
311
312 /**
313 * Sets the visibility of this layer. Emits property change event for
314 * property {@link #VISIBLE_PROP}.
315 *
316 * @param visible true, if the layer is visible; false, otherwise.
317 */
318 public void setVisible(boolean visible) {
319 boolean oldValue = isVisible();
320 this.visible = visible;
321 if (visible && opacity == 0) {
322 setOpacity(1);
323 } else if (oldValue != isVisible()) {
324 fireVisibleChanged(oldValue, isVisible());
325 }
326 }
327
328 /**
329 * Replies true if this layer is visible. False, otherwise.
330 * @return true if this layer is visible. False, otherwise.
331 */
332 public boolean isVisible() {
333 return visible && opacity != 0;
334 }
335
336 /**
337 * Gets the opacity of the layer, in range 0...1
338 * @return The opacity
339 */
340 public double getOpacity() {
341 return opacity;
342 }
343
344 /**
345 * Sets the opacity of the layer, in range 0...1
346 * @param opacity The opacity
347 * @throws IllegalArgumentException if the opacity is out of range
348 */
349 public void setOpacity(double opacity) {
350 if (!(opacity >= 0 && opacity <= 1))
351 throw new IllegalArgumentException("Opacity value must be between 0 and 1");
352 double oldOpacity = getOpacity();
353 boolean oldVisible = isVisible();
354 this.opacity = opacity;
355 if (!Utils.equalsEpsilon(oldOpacity, getOpacity())) {
356 fireOpacityChanged(oldOpacity, getOpacity());
357 }
358 if (oldVisible != isVisible()) {
359 fireVisibleChanged(oldVisible, isVisible());
360 }
361 }
362
363 /**
364 * Sets new state to the layer after applying {@link ImageProcessor}.
365 */
366 public void setFilterStateChanged() {
367 fireFilterStateChanged();
368 }
369
370 /**
371 * Toggles the visibility state of this layer.
372 */
373 public void toggleVisible() {
374 setVisible(!isVisible());
375 }
376
377 /**
378 * Adds a {@link PropertyChangeListener}
379 *
380 * @param listener the listener
381 */
382 public void addPropertyChangeListener(PropertyChangeListener listener) {
383 propertyChangeSupport.addPropertyChangeListener(listener);
384 }
385
386 /**
387 * Removes a {@link PropertyChangeListener}
388 *
389 * @param listener the listener
390 */
391 public void removePropertyChangeListener(PropertyChangeListener listener) {
392 propertyChangeSupport.removePropertyChangeListener(listener);
393 }
394
395 /**
396 * fires a property change for the property {@link #VISIBLE_PROP}
397 *
398 * @param oldValue the old value
399 * @param newValue the new value
400 */
401 protected void fireVisibleChanged(boolean oldValue, boolean newValue) {
402 propertyChangeSupport.firePropertyChange(VISIBLE_PROP, oldValue, newValue);
403 }
404
405 /**
406 * fires a property change for the property {@link #OPACITY_PROP}
407 *
408 * @param oldValue the old value
409 * @param newValue the new value
410 */
411 protected void fireOpacityChanged(double oldValue, double newValue) {
412 propertyChangeSupport.firePropertyChange(OPACITY_PROP, oldValue, newValue);
413 }
414
415 /**
416 * fires a property change for the property {@link #FILTER_STATE_PROP}.
417 */
418 protected void fireFilterStateChanged() {
419 propertyChangeSupport.firePropertyChange(FILTER_STATE_PROP, null, null);
420 }
421
422 /**
423 * Check changed status of layer
424 *
425 * @return True if layer was changed since last paint
426 */
427 public boolean isChanged() {
428 return true;
429 }
430
431 /**
432 * allows to check whether a projection is supported or not
433 * @param proj projection
434 *
435 * @return True if projection is supported for this layer
436 */
437 public boolean isProjectionSupported(Projection proj) {
438 return proj != null;
439 }
440
441 /**
442 * Specify user information about projections
443 *
444 * @return User readable text telling about supported projections
445 */
446 public String nameSupportedProjections() {
447 return tr("All projections are supported");
448 }
449
450 /**
451 * The action to save a layer
452 */
453 public static class LayerSaveAction extends AbstractAction {
454 private final transient Layer layer;
455
456 public LayerSaveAction(Layer layer) {
457 putValue(SMALL_ICON, ImageProvider.get("save"));
458 putValue(SHORT_DESCRIPTION, tr("Save the current data."));
459 putValue(NAME, tr("Save"));
460 setEnabled(true);
461 this.layer = layer;
462 }
463
464 @Override
465 public void actionPerformed(ActionEvent e) {
466 SaveAction.getInstance().doSave(layer);
467 }
468 }
469
470 public static class LayerSaveAsAction extends AbstractAction {
471 private final transient Layer layer;
472
473 public LayerSaveAsAction(Layer layer) {
474 putValue(SMALL_ICON, ImageProvider.get("save_as"));
475 putValue(SHORT_DESCRIPTION, tr("Save the current data to a new file."));
476 putValue(NAME, tr("Save As..."));
477 setEnabled(true);
478 this.layer = layer;
479 }
480
481 @Override
482 public void actionPerformed(ActionEvent e) {
483 SaveAsAction.getInstance().doSave(layer);
484 }
485 }
486
487 public static class LayerGpxExportAction extends AbstractAction {
488 private final transient Layer layer;
489
490 public LayerGpxExportAction(Layer layer) {
491 putValue(SMALL_ICON, ImageProvider.get("exportgpx"));
492 putValue(SHORT_DESCRIPTION, tr("Export the data to GPX file."));
493 putValue(NAME, tr("Export to GPX..."));
494 setEnabled(true);
495 this.layer = layer;
496 }
497
498 @Override
499 public void actionPerformed(ActionEvent e) {
500 new GpxExportAction().export(layer);
501 }
502 }
503
504 /* --------------------------------------------------------------------------------- */
505 /* interface ProjectionChangeListener */
506 /* --------------------------------------------------------------------------------- */
507 @Override
508 public void projectionChanged(Projection oldValue, Projection newValue) {
509 if (!isProjectionSupported(newValue)) {
510 final String message = "<html><body><p>" +
511 tr("The layer {0} does not support the new projection {1}.", getName(), newValue.toCode()) + "</p>" +
512 "<p style='width: 450px;'>" + tr("Supported projections are: {0}", nameSupportedProjections()) + "</p>" +
513 tr("Change the projection again or remove the layer.");
514
515 // run later to not block loading the UI.
516 SwingUtilities.invokeLater(new Runnable() {
517 @Override
518 public void run() {
519 JOptionPane.showMessageDialog(Main.parent,
520 message,
521 tr("Warning"),
522 JOptionPane.WARNING_MESSAGE);
523 }
524 });
525 }
526 }
527
528 /**
529 * Initializes the layer after a successful load of data from a file
530 * @since 5459
531 */
532 public void onPostLoadFromFile() {
533 // To be overriden if needed
534 }
535
536 /**
537 * Replies the savable state of this layer (i.e if it can be saved through a "File-&gt;Save" dialog).
538 * @return true if this layer can be saved to a file
539 * @since 5459
540 */
541 public boolean isSavable() {
542 return false;
543 }
544
545 /**
546 * Checks whether it is ok to launch a save (whether we have data, there is no conflict etc.)
547 * @return <code>true</code>, if it is safe to save.
548 * @since 5459
549 */
550 public boolean checkSaveConditions() {
551 return true;
552 }
553
554 /**
555 * Creates a new "Save" dialog for this layer and makes it visible.<br>
556 * When the user has chosen a file, checks the file extension, and confirms overwrite if needed.
557 * @return The output {@code File}
558 * @see SaveActionBase#createAndOpenSaveFileChooser
559 * @since 5459
560 */
561 public File createAndOpenSaveFileChooser() {
562 return SaveActionBase.createAndOpenSaveFileChooser(tr("Save Layer"), "lay");
563 }
564
565 /**
566 * @return bytes that the tile will use. Needed for resource management
567 * @deprecated Not used any more.
568 */
569 @Deprecated
570 protected long estimateMemoryUsage() {
571 return 0;
572 }
573
574 /**
575 * Gets the strategy that specifies where this layer should be inserted in a layer list.
576 * @return That strategy.
577 * @since 10008
578 */
579 public LayerPositionStrategy getDefaultLayerPosition() {
580 if (isBackgroundLayer()) {
581 return LayerPositionStrategy.BEFORE_FIRST_BACKGROUND_LAYER;
582 } else {
583 return LayerPositionStrategy.AFTER_LAST_VALIDATION_LAYER;
584 }
585 }
586
587 /**
588 * Gets the {@link ProjectionBounds} for this layer to be visible to the user. This can be the exact bounds, the UI handles padding. Return
589 * <code>null</code> if you cannot provide this information. The default implementation uses the bounds from
590 * {@link #visitBoundingBox(BoundingXYVisitor)}.
591 * @return The bounds for this layer.
592 * @since 10371
593 */
594 public ProjectionBounds getViewProjectionBounds() {
595 BoundingXYVisitor v = new BoundingXYVisitor();
596 visitBoundingBox(v);
597 return v.getBounds();
598 }
599}
Note: See TracBrowser for help on using the repository browser.