source: josm/trunk/src/org/openstreetmap/josm/gui/MapView.java@ 3116

Last change on this file since 3116 was 3116, checked in by jttt, 14 years ago

Reuse offscreenBuffer if layers didn't change

  • Property svn:eol-style set to native
File size: 26.5 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui;
4
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Color;
8import java.awt.Graphics;
9import java.awt.Graphics2D;
10import java.awt.Point;
11import java.awt.Rectangle;
12import java.awt.event.ComponentAdapter;
13import java.awt.event.ComponentEvent;
14import java.awt.event.MouseEvent;
15import java.awt.event.MouseMotionListener;
16import java.awt.geom.Area;
17import java.awt.geom.GeneralPath;
18import java.awt.image.BufferedImage;
19import java.beans.PropertyChangeEvent;
20import java.beans.PropertyChangeListener;
21import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.Comparator;
25import java.util.Enumeration;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.concurrent.CopyOnWriteArrayList;
29
30import javax.swing.AbstractButton;
31import javax.swing.JComponent;
32import javax.swing.JOptionPane;
33
34import org.openstreetmap.josm.Main;
35import org.openstreetmap.josm.actions.AutoScaleAction;
36import org.openstreetmap.josm.actions.JosmAction;
37import org.openstreetmap.josm.actions.MoveAction;
38import org.openstreetmap.josm.actions.mapmode.MapMode;
39import org.openstreetmap.josm.data.Bounds;
40import org.openstreetmap.josm.data.SelectionChangedListener;
41import org.openstreetmap.josm.data.coor.LatLon;
42import org.openstreetmap.josm.data.osm.DataSet;
43import org.openstreetmap.josm.data.osm.DataSource;
44import org.openstreetmap.josm.data.osm.OsmPrimitive;
45import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
46import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
47import org.openstreetmap.josm.gui.layer.GpxLayer;
48import org.openstreetmap.josm.gui.layer.Layer;
49import org.openstreetmap.josm.gui.layer.MapViewPaintable;
50import org.openstreetmap.josm.gui.layer.OsmDataLayer;
51import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
52import org.openstreetmap.josm.gui.layer.markerlayer.PlayHeadMarker;
53import org.openstreetmap.josm.tools.AudioPlayer;
54
55/**
56 * This is a component used in the MapFrame for browsing the map. It use is to
57 * provide the MapMode's enough capabilities to operate.
58 *
59 * MapView hold meta-data about the data set currently displayed, as scale level,
60 * center point viewed, what scrolling mode or editing mode is selected or with
61 * what projection the map is viewed etc..
62 *
63 * MapView is able to administrate several layers.
64 *
65 * @author imi
66 */
67public class MapView extends NavigatableComponent implements PropertyChangeListener {
68
69 /**
70 * Interface to notify listeners of the change of the active layer.
71 * @author imi
72 */
73 public interface LayerChangeListener {
74 void activeLayerChange(Layer oldLayer, Layer newLayer);
75 void layerAdded(Layer newLayer);
76 void layerRemoved(Layer oldLayer);
77 }
78
79 public interface EditLayerChangeListener {
80 void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer);
81 }
82
83 /**
84 * the layer listeners
85 */
86 private static final CopyOnWriteArrayList<MapView.LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<MapView.LayerChangeListener>();
87 private static final CopyOnWriteArrayList<EditLayerChangeListener> editLayerChangeListeners = new CopyOnWriteArrayList<EditLayerChangeListener>();
88
89 /**
90 * Removes a layer change listener
91 *
92 * @param listener the listener. Ignored if null or already registered.
93 */
94 public static void removeLayerChangeListener(MapView.LayerChangeListener listener) {
95 layerChangeListeners.remove(listener);
96 }
97
98 public static void removeEditLayerChangeListener(EditLayerChangeListener listener) {
99 editLayerChangeListeners.remove(listener);
100 }
101
102 /**
103 * Adds a layer change listener
104 *
105 * @param listener the listener. Ignored if null or already registered.
106 */
107 public static void addLayerChangeListener(MapView.LayerChangeListener listener) {
108 if (listener != null) {
109 layerChangeListeners.addIfAbsent(listener);
110 }
111 }
112
113 /**
114 * Adds a edit layer change listener
115 *
116 * @param listener the listener. Ignored if null or already registered.
117 * @param initialFire Fire an edit-layer-changed-event right after adding the listener.
118 */
119 public static void addEditLayerChangeListener(EditLayerChangeListener listener, boolean initialFire) {
120 addEditLayerChangeListener(listener);
121 if (initialFire) {
122 if (Main.map != null && Main.map.mapView != null && Main.map.mapView.getEditLayer() != null) {
123 fireEditLayerChanged(null, Main.map.mapView.getEditLayer());
124 }
125 }
126 }
127
128 public static void addEditLayerChangeListener(EditLayerChangeListener listener) {
129 if (listener != null) {
130 editLayerChangeListeners.addIfAbsent(listener);
131 }
132 }
133
134 protected static void fireActiveLayerChanged(Layer oldLayer, Layer newLayer) {
135 for (LayerChangeListener l : layerChangeListeners) {
136 l.activeLayerChange(oldLayer, newLayer);
137 }
138 }
139
140 protected static void fireLayerAdded(Layer newLayer) {
141 for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
142 l.layerAdded(newLayer);
143 }
144 }
145
146 protected static void fireLayerRemoved(Layer layer) {
147 for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
148 l.layerRemoved(layer);
149 }
150 }
151
152 protected static void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
153 for (EditLayerChangeListener l : editLayerChangeListeners) {
154 l.editLayerChanged(oldLayer, newLayer);
155 }
156 }
157
158 /**
159 * A list of all layers currently loaded.
160 */
161 private final List<Layer> layers = new ArrayList<Layer>();
162 /**
163 * The play head marker: there is only one of these so it isn't in any specific layer
164 */
165 public PlayHeadMarker playHeadMarker = null;
166
167 /**
168 * The layer from the layers list that is currently active.
169 */
170 private Layer activeLayer;
171
172 private OsmDataLayer editLayer;
173
174 /**
175 * The last event performed by mouse.
176 */
177 public MouseEvent lastMEvent;
178
179 private LinkedList<MapViewPaintable> temporaryLayers = new LinkedList<MapViewPaintable>();
180
181 private BufferedImage offscreenBuffer;
182 // Layers that wasn't changed since last paint
183 private final List<Layer> nonChangedLayers = new ArrayList<Layer>();
184
185 public MapView() {
186 addComponentListener(new ComponentAdapter(){
187 @Override public void componentResized(ComponentEvent e) {
188 removeComponentListener(this);
189
190 MapSlider zoomSlider = new MapSlider(MapView.this);
191 add(zoomSlider);
192 zoomSlider.setBounds(3, 0, 114, 30);
193
194 MapScaler scaler = new MapScaler(MapView.this);
195 add(scaler);
196 scaler.setLocation(10,30);
197
198 OsmDataLayer layer = getEditLayer();
199 if (layer != null) {
200 if (!zoomToDataSetBoundingBox(layer.data)) {
201 // no bounding box defined
202 new AutoScaleAction("data").actionPerformed(null);
203 }
204 } else {
205 new AutoScaleAction("layer").actionPerformed(null);
206 }
207
208 new MapMover(MapView.this, Main.contentPane);
209 JosmAction mv;
210 mv = new MoveAction(MoveAction.Direction.UP);
211 if (mv.getShortcut() != null) {
212 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "UP");
213 Main.contentPane.getActionMap().put("UP", mv);
214 }
215 mv = new MoveAction(MoveAction.Direction.DOWN);
216 if (mv.getShortcut() != null) {
217 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "DOWN");
218 Main.contentPane.getActionMap().put("DOWN", mv);
219 }
220 mv = new MoveAction(MoveAction.Direction.LEFT);
221 if (mv.getShortcut() != null) {
222 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "LEFT");
223 Main.contentPane.getActionMap().put("LEFT", mv);
224 }
225 mv = new MoveAction(MoveAction.Direction.RIGHT);
226 if (mv.getShortcut() != null) {
227 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "RIGHT");
228 Main.contentPane.getActionMap().put("RIGHT", mv);
229 }
230 }
231 });
232
233 // listend to selection changes to redraw the map
234 DataSet.selListeners.add(new SelectionChangedListener(){
235 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
236 repaint();
237 }
238 });
239
240 //store the last mouse action
241 this.addMouseMotionListener(new MouseMotionListener() {
242 public void mouseDragged(MouseEvent e) {
243 mouseMoved(e);
244 }
245 public void mouseMoved(MouseEvent e) {
246 lastMEvent = e;
247 }
248 });
249 }
250
251 /**
252 * Adds a GPX layer. A GPX layer is added below the lowest data layer.
253 *
254 * @param layer the GPX layer
255 */
256 protected void addGpxLayer(GpxLayer layer) {
257 if (layers.isEmpty()) {
258 layers.add(layer);
259 return;
260 }
261 for (int i=layers.size()-1; i>= 0; i--) {
262 if (layers.get(i) instanceof OsmDataLayer) {
263 if (i == layers.size()-1) {
264 layers.add(layer);
265 } else {
266 layers.add(i+1, layer);
267 }
268 return;
269 }
270 }
271 layers.add(layer);
272 }
273
274 /**
275 * Add a layer to the current MapView. The layer will be added at topmost
276 * position.
277 */
278 public void addLayer(Layer layer) {
279 if (layer instanceof MarkerLayer && playHeadMarker == null) {
280 playHeadMarker = PlayHeadMarker.create();
281 }
282
283 if (layer instanceof GpxLayer) {
284 addGpxLayer((GpxLayer)layer);
285 } else if (layer.isBackgroundLayer() || layers.isEmpty()) {
286 layers.add(layer);
287 } else {
288 layers.add(0, layer);
289 }
290 fireLayerAdded(layer);
291 if (layer instanceof OsmDataLayer || activeLayer == null) {
292 // autoselect the new layer
293 setActiveLayer(layer);
294 }
295 layer.addPropertyChangeListener(this);
296 AudioPlayer.reset();
297 repaint();
298 }
299
300 @Override
301 protected DataSet getCurrentDataSet() {
302 if (editLayer != null)
303 return editLayer.data;
304 else
305 return null;
306 }
307
308 /**
309 * Replies true if the active layer is drawable.
310 *
311 * @return true if the active layer is drawable, false otherwise
312 */
313 public boolean isActiveLayerDrawable() {
314 return editLayer != null;
315 }
316
317 /**
318 * Replies true if the active layer is visible.
319 *
320 * @return true if the active layer is visible, false otherwise
321 */
322 public boolean isActiveLayerVisible() {
323 return isActiveLayerDrawable() && editLayer.isVisible();
324 }
325
326 /**
327 * Determines the next active data layer according to the following
328 * rules:
329 * <ul>
330 * <li>if there is at least one {@see OsmDataLayer} the first one
331 * becomes active</li>
332 * <li>otherwise, the top most layer of any type becomes active</li>
333 * </ul>
334 *
335 * @return the next active data layer
336 */
337 protected Layer determineNextActiveLayer(List<Layer> layersList) {
338 // First look for data layer
339 for (Layer layer:layersList) {
340 if (layer instanceof OsmDataLayer)
341 return layer;
342 }
343
344 // Then any layer
345 if (!layersList.isEmpty())
346 return layersList.get(0);
347
348 // and then give up
349 return null;
350
351 }
352
353 /**
354 * Remove the layer from the mapview. If the layer was in the list before,
355 * an LayerChange event is fired.
356 */
357 public void removeLayer(Layer layer) {
358 List<Layer> layersList = new ArrayList<Layer>(layers);
359
360 if (!layersList.remove(layer))
361 return;
362
363 setEditLayer(layersList);
364
365 if (layer == activeLayer) {
366 setActiveLayer(determineNextActiveLayer(layersList), false);
367 }
368
369 layers.remove(layer);
370 fireLayerRemoved(layer);
371 layer.removePropertyChangeListener(this);
372 layer.destroy();
373 AudioPlayer.reset();
374 repaint();
375 }
376
377 private boolean virtualNodesEnabled = false;
378
379 public void setVirtualNodesEnabled(boolean enabled) {
380 if(virtualNodesEnabled != enabled) {
381 virtualNodesEnabled = enabled;
382 repaint();
383 }
384 }
385 public boolean isVirtualNodesEnabled() {
386 return virtualNodesEnabled;
387 }
388
389 /**
390 * Moves the layer to the given new position. No event is fired, but repaints
391 * according to the new Z-Order of the layers.
392 *
393 * @param layer The layer to move
394 * @param pos The new position of the layer
395 */
396 public void moveLayer(Layer layer, int pos) {
397 int curLayerPos = layers.indexOf(layer);
398 if (curLayerPos == -1)
399 throw new IllegalArgumentException(tr("Layer not in list."));
400 if (pos == curLayerPos)
401 return; // already in place.
402 layers.remove(curLayerPos);
403 if (pos >= layers.size()) {
404 layers.add(layer);
405 } else {
406 layers.add(pos, layer);
407 }
408 setEditLayer(layers);
409 AudioPlayer.reset();
410 repaint();
411 }
412
413 public int getLayerPos(Layer layer) {
414 int curLayerPos = layers.indexOf(layer);
415 if (curLayerPos == -1)
416 throw new IllegalArgumentException(tr("Layer not in list."));
417 return curLayerPos;
418 }
419
420 /**
421 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
422 * first, layer with the highest Z-Order last.
423 *
424 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
425 * first, layer with the highest Z-Order last.
426 */
427 protected List<Layer> getVisibleLayersInZOrder() {
428 ArrayList<Layer> ret = new ArrayList<Layer>();
429 for (Layer l: layers) {
430 if (l.isVisible()) {
431 ret.add(l);
432 }
433 }
434 // sort according to position in the list of layers, with one exception:
435 // an active data layer always becomes a higher Z-Order than all other
436 // data layers
437 //
438 Collections.sort(
439 ret,
440 new Comparator<Layer>() {
441 public int compare(Layer l1, Layer l2) {
442 if (l1 instanceof OsmDataLayer && l2 instanceof OsmDataLayer) {
443 if (l1 == getActiveLayer()) return -1;
444 if (l2 == getActiveLayer()) return 1;
445 return Integer.valueOf(layers.indexOf(l1)).compareTo(layers.indexOf(l2));
446 } else
447 return Integer.valueOf(layers.indexOf(l1)).compareTo(layers.indexOf(l2));
448 }
449 }
450 );
451 Collections.reverse(ret);
452 return ret;
453 }
454
455 /**
456 * Draw the component.
457 */
458 @Override public void paint(Graphics g) {
459 if (center == null)
460 return; // no data loaded yet.
461
462 List<Layer> visibleLayers = getVisibleLayersInZOrder();
463
464 int nonChangedLayersCount = 0;
465 for (Layer l: visibleLayers) {
466 if (l.isChanged()) {
467 break;
468 } else {
469 nonChangedLayersCount++;
470 }
471 }
472
473 boolean canUseBuffer = nonChangedLayers.size() <= nonChangedLayersCount;
474 if (canUseBuffer) {
475 for (int i=0; i<nonChangedLayers.size(); i++) {
476 if (visibleLayers.get(i) != nonChangedLayers.get(i)) {
477 canUseBuffer = false;
478 break;
479 }
480 }
481 }
482
483 Graphics2D tempG = (Graphics2D) g;
484 Bounds box = getLatLonBounds(g.getClipBounds());
485
486 if (!canUseBuffer || offscreenBuffer == null) {
487 if (null == offscreenBuffer || offscreenBuffer.getWidth() != getWidth() || offscreenBuffer.getHeight() != getHeight()) {
488 offscreenBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
489 }
490 Graphics2D g2 = offscreenBuffer.createGraphics();
491 g2.setColor(PaintColors.BACKGROUND.get());
492 g2.fillRect(0, 0, getWidth(), getHeight());
493
494 for (int i=0; i<nonChangedLayersCount; i++) {
495 visibleLayers.get(i).paint(g2, this, box);
496 }
497 } else {
498 // Maybe there were more unchanged layers then last time - draw them to buffer
499 if (nonChangedLayers.size() != nonChangedLayersCount) {
500 Graphics2D g2 = offscreenBuffer.createGraphics();
501 for (int i=nonChangedLayers.size(); i<nonChangedLayersCount; i++) {
502 visibleLayers.get(i).paint(g2, this, box);
503 }
504 }
505 }
506
507 nonChangedLayers.clear();
508 for (int i=0; i<nonChangedLayersCount; i++) {
509 nonChangedLayers.add(visibleLayers.get(i));
510 }
511
512 tempG.drawImage(offscreenBuffer, 0, 0, null);
513
514 for (int i=nonChangedLayersCount; i<visibleLayers.size(); i++) {
515 visibleLayers.get(i).paint(tempG, this, box);
516 }
517
518 for (MapViewPaintable mvp : temporaryLayers) {
519 mvp.paint(tempG, this, box);
520 }
521
522 // draw world borders
523 tempG.setColor(Color.WHITE);
524 Bounds b = getProjection().getWorldBoundsLatLon();
525 double lat = b.getMin().lat();
526 double lon = b.getMin().lon();
527
528 Point p = getPoint(b.getMin());
529
530 GeneralPath path = new GeneralPath();
531
532 path.moveTo(p.x, p.y);
533 double max = b.getMax().lat();
534 for(; lat <= max; lat += 1.0)
535 {
536 p = getPoint(new LatLon(lat >= max ? max : lat, lon));
537 path.lineTo(p.x, p.y);
538 }
539 lat = max; max = b.getMax().lon();
540 for(; lon <= max; lon += 1.0)
541 {
542 p = getPoint(new LatLon(lat, lon >= max ? max : lon));
543 path.lineTo(p.x, p.y);
544 }
545 lon = max; max = b.getMin().lat();
546 for(; lat >= max; lat -= 1.0)
547 {
548 p = getPoint(new LatLon(lat <= max ? max : lat, lon));
549 path.lineTo(p.x, p.y);
550 }
551 lat = max; max = b.getMin().lon();
552 for(; lon >= max; lon -= 1.0)
553 {
554 p = getPoint(new LatLon(lat, lon <= max ? max : lon));
555 path.lineTo(p.x, p.y);
556 }
557
558 int w = getWidth();
559 int h = getHeight();
560
561 // Work around OpenJDK having problems when drawing out of bounds
562 final Area border = new Area(path);
563 // Make the viewport 1px larger in every direction to prevent an
564 // additional 1px border when zooming in
565 final Area viewport = new Area(new Rectangle(-1, -1, w + 2, h + 2));
566 border.intersect(viewport);
567 tempG.draw(border);
568
569 if (playHeadMarker != null) {
570 playHeadMarker.paint(tempG, this);
571 }
572
573 super.paint(g);
574 }
575
576 /**
577 * Set the new dimension to the view.
578 */
579 public void recalculateCenterScale(BoundingXYVisitor box) {
580 if (box == null) {
581 box = new BoundingXYVisitor();
582 }
583 if (box.getBounds() == null) {
584 box.visit(getProjection().getWorldBoundsLatLon());
585 }
586 if (!box.hasExtend()) {
587 box.enlargeBoundingBox();
588 }
589
590 zoomTo(box.getBounds());
591 }
592
593 /**
594 * @return An unmodifiable collection of all layers
595 */
596 public Collection<Layer> getAllLayers() {
597 return Collections.unmodifiableCollection(layers);
598 }
599
600 /**
601 * @return An unmodifiable ordered list of all layers
602 */
603 public List<Layer> getAllLayersAsList() {
604 return Collections.unmodifiableList(layers);
605 }
606
607 /**
608 * Replies an unmodifiable list of layers of a certain type.
609 *
610 * Example:
611 * <pre>
612 * List<WMSLayer> wmsLayers = getLayersOfType(WMSLayer.class);
613 * </pre>
614 *
615 * @return an unmodifiable list of layers of a certain type.
616 */
617 public <T> List<T> getLayersOfType(Class<T> ofType) {
618 ArrayList<T> ret = new ArrayList<T>();
619 for (Layer layer : getAllLayersAsList()) {
620 if (ofType.isInstance(layer)) {
621 ret.add(ofType.cast(layer));
622 }
623 }
624 return ret;
625 }
626
627 /**
628 * Replies the number of layers managed by this mav view
629 *
630 * @return the number of layers managed by this mav view
631 */
632 public int getNumLayers() {
633 return layers.size();
634 }
635
636 /**
637 * Replies true if there is at least one layer in this map view
638 *
639 * @return true if there is at least one layer in this map view
640 */
641 public boolean hasLayers() {
642 return getNumLayers() > 0;
643 }
644
645 private void setEditLayer(List<Layer> layersList) {
646 OsmDataLayer newEditLayer = layersList.contains(editLayer)?editLayer:null;
647 OsmDataLayer oldEditLayer = editLayer;
648
649 // Find new edit layer
650 if (activeLayer != editLayer || !layersList.contains(editLayer)) {
651 if (activeLayer instanceof OsmDataLayer && layersList.contains(activeLayer)) {
652 newEditLayer = (OsmDataLayer) activeLayer;
653 } else {
654 for (Layer layer:layersList) {
655 if (layer instanceof OsmDataLayer) {
656 newEditLayer = (OsmDataLayer) layer;
657 break;
658 }
659 }
660 }
661 }
662
663 // Set new edit layer
664 if (newEditLayer != editLayer) {
665 if (newEditLayer == null) {
666 getCurrentDataSet().setSelected();
667 }
668
669 editLayer = newEditLayer;
670 fireEditLayerChanged(oldEditLayer, newEditLayer);
671 refreshTitle();
672 }
673
674 }
675
676 /**
677 * Sets the active layer to <code>layer</code>. If <code>layer</code> is an instance
678 * of {@see OsmDataLayer} also sets {@see #editLayer} to <code>layer</code>.
679 *
680 * @param layer the layer to be activate; must be one of the layers in the list of layers
681 * @exception IllegalArgumentException thrown if layer is not in the lis of layers
682 */
683 public void setActiveLayer(Layer layer) {
684 setActiveLayer(layer, true);
685 }
686
687 private void setActiveLayer(Layer layer, boolean setEditLayer) {
688 if (layer != null && !layers.contains(layer))
689 throw new IllegalArgumentException(tr("Layer ''{0}'' must be in list of layers", layer.toString()));
690
691 if (layer == activeLayer)
692 return;
693
694 Layer old = activeLayer;
695 activeLayer = layer;
696 if (setEditLayer) {
697 setEditLayer(layers);
698 }
699 fireActiveLayerChanged(old, layer);
700
701 /* This only makes the buttons look disabled. Disabling the actions as well requires
702 * the user to re-select the tool after i.e. moving a layer. While testing I found
703 * that I switch layers and actions at the same time and it was annoying to mind the
704 * order. This way it works as visual clue for new users */
705 for (Enumeration<AbstractButton> e = Main.map.toolGroup.getElements() ; e.hasMoreElements() ;) {
706 AbstractButton x=e.nextElement();
707 x.setEnabled(((MapMode)x.getAction()).layerIsSupported(layer));
708 }
709 AudioPlayer.reset();
710 repaint();
711 }
712
713 /**
714 * Replies the currently active layer
715 *
716 * @return the currently active layer (may be null)
717 */
718 public Layer getActiveLayer() {
719 return activeLayer;
720 }
721
722 /**
723 * Replies the current edit layer, if any
724 *
725 * @return the current edit layer. May be null.
726 */
727 public OsmDataLayer getEditLayer() {
728 return editLayer;
729 }
730
731 /**
732 * replies true if the list of layers managed by this map view contain layer
733 *
734 * @param layer the layer
735 * @return true if the list of layers managed by this map view contain layer
736 */
737 public boolean hasLayer(Layer layer) {
738 return layers.contains(layer);
739 }
740
741 /**
742 * Tries to zoom to the download boundingbox[es] of the current edit layer
743 * (aka {@link OsmDataLayer}). If the edit layer has multiple download bounding
744 * boxes it zooms to a large virtual bounding box containing all smaller ones.
745 *
746 * @return <code>true</code> if a zoom operation has been performed
747 */
748 public boolean zoomToDataSetBoundingBox(DataSet ds) {
749 // In case we already have an existing data layer ...
750 OsmDataLayer layer= getEditLayer();
751 if (layer == null)
752 return false;
753 Collection<DataSource> dataSources = ds.dataSources;
754 // ... with bounding box[es] of data loaded from OSM or a file...
755 BoundingXYVisitor bbox = new BoundingXYVisitor();
756 for (DataSource source : dataSources) {
757 bbox.visit(source.bounds);
758 }
759 if (bbox.hasExtend()) {
760 // ... we zoom to it's bounding box
761 recalculateCenterScale(bbox);
762 return true;
763 }
764 return false;
765 }
766
767 public boolean addTemporaryLayer(MapViewPaintable mvp) {
768 if (temporaryLayers.contains(mvp)) return false;
769 return temporaryLayers.add(mvp);
770 }
771
772 public boolean removeTemporaryLayer(MapViewPaintable mvp) {
773 return temporaryLayers.remove(mvp);
774 }
775
776 public void propertyChange(PropertyChangeEvent evt) {
777 if (evt.getPropertyName().equals(Layer.VISIBLE_PROP)) {
778 repaint();
779 } else if (evt.getPropertyName().equals(OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP)
780 || evt.getPropertyName().equals(OsmDataLayer.REQUIRES_UPLOAD_TO_SERVER_PROP)) {
781 OsmDataLayer layer = (OsmDataLayer)evt.getSource();
782 if (layer == getEditLayer()) {
783 refreshTitle();
784 }
785 }
786 }
787
788 protected void refreshTitle() {
789 boolean dirty = editLayer != null && (editLayer.requiresSaveToFile() || editLayer.requiresUploadToServer());
790 if (dirty) {
791 JOptionPane.getFrameForComponent(Main.parent).setTitle("* " + tr("Java OpenStreetMap Editor"));
792 } else {
793 JOptionPane.getFrameForComponent(Main.parent).setTitle(tr("Java OpenStreetMap Editor"));
794 }
795 }
796
797}
Note: See TracBrowser for help on using the repository browser.