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

Last change on this file since 3558 was 3478, checked in by bastiK, 14 years ago

1) do not show bug report window for bugs that are thrown while bug handling is in progress; 2) do not paint the MapView while bug handling is in progress. (This prevents the cascaded error messages when exceptions is thrown by paint())

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