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

Revision 5219, 28.6 KB checked in by bastiK, 2 weeks ago (diff)

mapstyles: add automatic reloading of local styles when they have been edited in an extenal editor (based on mtime)

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