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

Last change on this file since 7329 was 6890, checked in by Don-vip, 10 years ago

fix some Sonar issues (Constructor Calls Overridable Method)

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