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

Last change on this file since 4230 was 4230, checked in by stoecker, 13 years ago

allow to color the entries in layer list dialog according to assigned layer drawing color

  • 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 new SaveAction().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 new SaveAsAction().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.