Index: trunk/images/overlay/active.svg
===================================================================
--- trunk/images/overlay/active.svg	(revision 1890)
+++ trunk/images/overlay/active.svg	(revision 1890)
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="active.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="C:\data\projekte\eclipse-3.4.1-ws\JOSM-new\images\overlay\active.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.2"
+     inkscape:cx="-8.3615172"
+     inkscape:cy="4.3644547"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1166"
+     inkscape:window-height="820"
+     inkscape:window-x="226"
+     inkscape:window-y="92" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline;opacity:1">
+    <path
+       style="opacity:1;fill:#cccccc;fill-rule:evenodd;stroke:#8c8c8c;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       d="M 4.4113009,8.599831 L 15.790268,8.7784025 L 12.685108,15.028402 L 0.70098386,15.028402 L 4.4113009,8.599831 z"
+       id="path3246"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="opacity:1;fill:#87bf00;fill-rule:evenodd;stroke:#8c8c8c;stroke-width:0.50000000000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline;fill-opacity:1"
+       d="M 4.1121037,5.3750002 L 15.49107,5.5535717 L 12.385911,11.803571 L 0.40178633,11.803571 L 4.1121037,5.3750002 z"
+       id="path3244"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="opacity:1;fill:#cccccc;fill-rule:evenodd;stroke:#8c8c8c;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       d="M 4.0228179,1.2678575 L 15.401785,1.446429 L 12.296626,7.6964285 L 0.31250046,7.6964285 L 4.0228179,1.2678575 z"
+       id="path3242"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="path3199"
+       sodipodi:cx="8.125"
+       sodipodi:cy="10.598214"
+       sodipodi:rx="3.6607144"
+       sodipodi:ry="3.5267856"
+       d="M 11.785714,10.598214 A 3.6607144,3.5267856 0 1 1 4.4642856,10.598214 A 3.6607144,3.5267856 0 1 1 11.785714,10.598214 z"
+       transform="matrix(1.3984284,-0.106593,0.1035917,1.4012253,-4.2011897,-5.4397748)" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.80310738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 5.0622681,9.0979784 L 6.8127321,10.848443"
+       id="path3203"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="opacity:1;fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.77164185;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+       d="M 7.2340353,11.08739 L 11.069536,6.4483211"
+       id="path3205"
+       sodipodi:nodetypes="cc" />
+  </g>
+</svg>
Index: trunk/images/overlay/invisiblenew.svg
===================================================================
--- trunk/images/overlay/invisiblenew.svg	(revision 1890)
+++ trunk/images/overlay/invisiblenew.svg	(revision 1890)
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="invisiblenew.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.2"
+     inkscape:cx="8.7366971"
+     inkscape:cy="3.025169"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="921"
+     inkscape:window-height="738"
+     inkscape:window-x="353"
+     inkscape:window-y="99" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:#800000;fill-rule:evenodd;stroke:#800000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
+       d="M 0.89285715,15.107143 L 15,1.1785714"
+       id="path2383" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#800000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 0.71428572,0.82142853 L 14.821429,15.107143 L 14.821429,15.107143"
+       id="path3155" />
+  </g>
+</svg>
Index: trunk/src/org/openstreetmap/josm/actions/AbstractMergeAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/AbstractMergeAction.java	(revision 1890)
+++ trunk/src/org/openstreetmap/josm/actions/AbstractMergeAction.java	(revision 1890)
@@ -0,0 +1,88 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.GridBagLayout;
+import java.util.List;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.Icon;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.OptionPaneUtil;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public abstract class AbstractMergeAction extends JosmAction {
+
+
+    /**
+     * the list cell renderer used to render layer list entries
+     * 
+     */
+    static public class LayerListCellRenderer extends DefaultListCellRenderer {
+
+        protected boolean isActiveLayer(Layer layer) {
+            if (Main.map == null)
+                return false;
+            if (Main.map.mapView == null)
+                return false;
+            return Main.map.mapView.getActiveLayer() == layer;
+        }
+
+        @Override
+        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+                boolean cellHasFocus) {
+            Layer layer = (Layer) value;
+            JLabel label = (JLabel) super.getListCellRendererComponent(list, layer.getName(), index, isSelected,
+                    cellHasFocus);
+            Icon icon = layer.getIcon();
+            label.setIcon(icon);
+            label.setToolTipText(layer.getToolTipText());
+            return label;
+        }
+    }
+
+    public AbstractMergeAction() {
+        super();
+    }
+
+    public AbstractMergeAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean register) {
+        super(name, iconName, tooltip, shortcut, register);
+    }
+
+    protected Layer askTargetLayer(List<Layer> targetLayers) {
+        JComboBox layerList = new JComboBox();
+        layerList.setRenderer(new LayerListCellRenderer());
+        layerList.setModel(new DefaultComboBoxModel(targetLayers.toArray()));
+        layerList.setSelectedIndex(0);
+    
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new GridBagLayout());
+        pnl.add(new JLabel(tr("Please select the target layer.")), GBC.eol());
+        pnl.add(layerList, GBC.eol());
+    
+        int decision = new ExtendedDialog(Main.parent, tr("Select target layer"), pnl, new String[] { tr("Merge"),
+            tr("Cancel") }, new String[] { "dialogs/mergedown", "cancel" }).getValue();
+        if (decision != 1)
+            return null;
+        Layer targetLayer = (Layer) layerList.getSelectedItem();
+        return targetLayer;
+    }
+
+    protected void warnNoTargetLayersForSourceLayer(Layer sourceLayer) {
+        OptionPaneUtil.showMessageDialog(Main.parent,
+                tr("<html>There are no layers the source layer<br>''{0}''<br>could be merged to.</html>"),
+                tr("No target layers"), JOptionPane.WARNING_MESSAGE);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/actions/MergeLayerAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/MergeLayerAction.java	(revision 1890)
+++ trunk/src/org/openstreetmap/josm/actions/MergeLayerAction.java	(revision 1890)
@@ -0,0 +1,55 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class MergeLayerAction extends AbstractMergeAction {
+
+    public MergeLayerAction() {
+        super(tr("Merge layer"), "dialogs/mergedown", tr("Merge the current layer into another layer"), Shortcut
+                .registerShortcut("system:merge", tr("Edit: {0}", tr("Merge")), KeyEvent.VK_M, Shortcut.GROUP_MENU),
+                true /* register */
+        );
+    }
+
+    public void merge(Layer sourceLayer) {
+        if (sourceLayer == null)
+            return;
+        List<Layer> targetLayers = LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer);
+        if (targetLayers.isEmpty()) {
+            warnNoTargetLayersForSourceLayer(sourceLayer);
+            return;
+        }
+        Layer targetLayer = askTargetLayer(targetLayers);
+        if (targetLayer == null)
+            return;
+        targetLayer.mergeFrom(sourceLayer);
+        Main.map.mapView.removeLayer(sourceLayer);
+        Main.map.mapView.setActiveLayer(targetLayer);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        Layer sourceLayer = Main.main.getEditLayer();
+        if (sourceLayer == null)
+            return;
+        merge(sourceLayer);
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        if (getEditLayer() == null) {
+            setEnabled(false);
+            return;
+        }
+        setEnabled(!LayerListDialog.getInstance().getModel().getPossibleMergeTargets(getEditLayer()).isEmpty());
+    }
+}
Index: trunk/src/org/openstreetmap/josm/actions/MergeSelectionAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/MergeSelectionAction.java	(revision 1890)
+++ trunk/src/org/openstreetmap/josm/actions/MergeSelectionAction.java	(revision 1890)
@@ -0,0 +1,57 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.visitor.MergeSourceBuildingVisitor;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class MergeSelectionAction extends AbstractMergeAction {
+    public MergeSelectionAction() {
+        super(tr("Merge selection"), "dialogs/mergedown", tr("Merge the currently selected primitives into another layer"), Shortcut
+                .registerShortcut("system:mergeselection", tr("Edit: {0}", tr("Merge selection")), KeyEvent.VK_M, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT),
+                true /* register */
+        );
+    }
+
+    public void mergeSelected(DataSet source) {
+        List<Layer> targetLayers = LayerListDialog.getInstance().getModel().getPossibleMergeTargets(getEditLayer());
+        if (targetLayers.isEmpty()) {
+            warnNoTargetLayersForSourceLayer(getEditLayer());
+            return;
+        }
+        Layer targetLayer = askTargetLayer(targetLayers);
+        if (targetLayer == null)
+            return;
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(getEditLayer().data);
+        ((OsmDataLayer)targetLayer).mergeFrom(builder.build());
+    }
+
+
+    public void actionPerformed(ActionEvent e) {
+        if (getEditLayer() == null || getEditLayer().data.getSelected().isEmpty())
+            return;
+        mergeSelected(getEditLayer().data);
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        if (getEditLayer() == null) {
+            setEnabled(false);
+            return;
+        }
+        setEnabled(!getEditLayer().data.getSelected().isEmpty());
+    }
+
+
+
+}
Index: trunk/src/org/openstreetmap/josm/actions/RenameLayerAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/RenameLayerAction.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/actions/RenameLayerAction.java	(revision 1890)
@@ -43,5 +43,5 @@
     public void actionPerformed(ActionEvent e) {
         Box panel = Box.createVerticalBox();
-        final JTextField name = new JTextField(layer.name);
+        final JTextField name = new JTextField(layer.getName());
         panel.add(name);
         JCheckBox filerename = new JCheckBox(tr("Also rename the file"));
@@ -105,5 +105,5 @@
             }
         }
-        layer.name = nameText;
+        layer.setName(nameText);
         Main.parent.repaint();
     }
Index: trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 1890)
@@ -72,5 +72,5 @@
         save(file, layer);
 
-        layer.name = file.getName();
+        layer.setName(file.getName());
         layer.setAssociatedFile(file);
         Main.parent.repaint();
Index: trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 1890)
@@ -40,5 +40,7 @@
 import org.openstreetmap.josm.actions.JoinNodeWayAction;
 import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.actions.MergeLayerAction;
 import org.openstreetmap.josm.actions.MergeNodesAction;
+import org.openstreetmap.josm.actions.MergeSelectionAction;
 import org.openstreetmap.josm.actions.MirrorAction;
 import org.openstreetmap.josm.actions.NewAction;
@@ -110,4 +112,6 @@
     public final JosmAction duplicate = new DuplicateAction();
     public final JosmAction delete = new DeleteAction();
+    public final JosmAction merge = new MergeLayerAction();
+    public final JosmAction mergeSelected = new MergeSelectionAction();
     public final JosmAction selectAll = new SelectAllAction();
     public final JosmAction unselectAll = new UnselectAllAction();
@@ -213,4 +217,7 @@
         add(editMenu, duplicate);
         add(editMenu, delete);
+        editMenu.addSeparator();
+        add(editMenu,merge);
+        add(editMenu,mergeSelected);
         editMenu.addSeparator();
         add(editMenu, selectAll);
Index: trunk/src/org/openstreetmap/josm/gui/MapFrame.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MapFrame.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/MapFrame.java	(revision 1890)
@@ -100,5 +100,6 @@
 
         toolBarToggle.setFloatable(false);
-        addToggleDialog(new LayerListDialog(this));
+        LayerListDialog.createInstance(this);
+        addToggleDialog(LayerListDialog.getInstance());
         addToggleDialog(new PropertiesDialog(this));
         addToggleDialog(new HistoryDialog());
@@ -133,12 +134,15 @@
      */
     public void destroy() {
-        for (ToggleDialog t : allDialogs)
+        for (ToggleDialog t : allDialogs) {
             t.close();
+        }
         for (int i = 0; i < toolBarActions.getComponentCount(); ++i)
-            if (toolBarActions.getComponent(i) instanceof Destroyable)
+            if (toolBarActions.getComponent(i) instanceof Destroyable) {
                 ((Destroyable)toolBarActions).destroy();
+            }
         for (int i = 0; i < toolBarToggle.getComponentCount(); ++i)
-            if (toolBarToggle.getComponent(i) instanceof Destroyable)
+            if (toolBarToggle.getComponent(i) instanceof Destroyable) {
                 ((Destroyable)toolBarToggle).destroy();
+            }
 
         // remove menu entries
@@ -195,6 +199,7 @@
         boolean old = isVisible();
         super.setVisible(aFlag);
-        if (old != aFlag)
+        if (old != aFlag) {
             firePropertyChange("visible", old, aFlag);
+        }
     }
 
@@ -209,6 +214,7 @@
         if (mapMode == this.mapMode)
             return;
-        if (this.mapMode != null)
+        if (this.mapMode != null) {
             this.mapMode.exitMode();
+        }
         this.mapMode = mapMode;
         mapMode.enterMode();
@@ -230,12 +236,14 @@
         if(Main.pref.getBoolean("sidetoolbar.visible", true))
         {
-            if(Main.pref.getBoolean("sidetoolbar.scrollable", true))
+            if(Main.pref.getBoolean("sidetoolbar.scrollable", true)) {
                 panel.add(new ScrollViewport(jb, ScrollViewport.VERTICAL_DIRECTION),
-                BorderLayout.WEST);
-            else
+                        BorderLayout.WEST);
+            } else {
                 panel.add(jb, BorderLayout.WEST);
-        }
-        if (statusLine != null && Main.pref.getBoolean("statusline.visible", true))
+            }
+        }
+        if (statusLine != null && Main.pref.getBoolean("statusline.visible", true)) {
             panel.add(statusLine, BorderLayout.SOUTH);
+        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 1890)
@@ -15,4 +15,6 @@
 import java.awt.geom.GeneralPath;
 import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -57,5 +59,5 @@
  * @author imi
  */
-public class MapView extends NavigatableComponent {
+public class MapView extends NavigatableComponent implements PropertyChangeListener {
 
 
@@ -177,4 +179,5 @@
             }
         }
+        layer.addPropertyChangeListener(this);
         AudioPlayer.reset();
         repaint();
@@ -203,5 +206,5 @@
      */
     public boolean isActiveLayerVisible() {
-        return isActiveLayerDrawable() && activeLayer.visible;
+        return isActiveLayerDrawable() && activeLayer.isVisible();
     }
 
@@ -212,4 +215,7 @@
     public void removeLayer(Layer layer) {
         if (layer == activeLayer) {
+            for (Layer.LayerChangeListener l : Layer.listeners) {
+                l.activeLayerChange(layer, null);
+            }
             activeLayer = null;
         }
@@ -219,4 +225,5 @@
             }
         }
+        layer.removePropertyChangeListener(this);
         layer.destroy();
         AudioPlayer.reset();
@@ -283,5 +290,5 @@
         for (int i = layers.size()-1; i >= 0; --i) {
             Layer l = layers.get(i);
-            if (l.visible/* && l != getActiveLayer()*/) {
+            if (l.isVisible()/* && l != getActiveLayer()*/) {
                 l.paint(tempG, this);
             }
@@ -472,3 +479,9 @@
         return temporaryLayers.remove(mvp);
     }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (evt.getPropertyName().equals(Layer.VISIBLE_PROP)) {
+            repaint();
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 1890)
@@ -9,14 +9,19 @@
 import java.awt.Point;
 import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.swing.AbstractAction;
-import javax.swing.Action;
 import javax.swing.DefaultListCellRenderer;
 import javax.swing.DefaultListModel;
+import javax.swing.DefaultListSelectionModel;
 import javax.swing.Icon;
 import javax.swing.JLabel;
@@ -25,10 +30,14 @@
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
+import javax.swing.ListModel;
 import javax.swing.ListSelectionModel;
 import javax.swing.UIManager;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.MergeLayerAction;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
 import org.openstreetmap.josm.gui.ExtendedDialog;
@@ -40,317 +49,813 @@
 import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Shortcut;
 import org.openstreetmap.josm.tools.ImageProvider.OverlayPosition;
-import org.openstreetmap.josm.tools.Shortcut;
 
 /**
- * A component that manages the list of all layers and react to selection changes
- * by setting the active layer in the mapview.
- *
- * @author imi
+ * This is a toggle dialog which displays the list of layers. Actions allow to
+ * change the ordering the layer, to hide/show layers, to activate layers
+ * and to delete layers.
+ * 
  */
-public class LayerListDialog extends ToggleDialog implements LayerChangeListener {
-
-    /**
-     * The last layerlist created. Used to update the list in the Show/Hide and Delete actions.
-     * TODO: Replace with Listener-Pattern.
-     */
-    static JList instance;
-    private JScrollPane listScrollPane;
-
-    public final static class DeleteLayerAction extends AbstractAction {
-
-        private final Layer layer;
-
+public class LayerListDialog extends ToggleDialog {
+
+    /** the unique instance of the dialog */
+    static private LayerListDialog instance;
+
+    /**
+     * Creates the instance of the dialog. It's connected to the map frame <code>mapFrame</code>
+     * 
+     * @param mapFrame the map frame
+     */
+    static public void createInstance(MapFrame mapFrame) {
+        instance = new LayerListDialog(mapFrame);
+    }
+
+    /**
+     * Replies the instance of the dialog
+     * 
+     * @return the instance of the dialog
+     * @throws IllegalStateException thrown, if the dialog is not created yet
+     * @see #createInstance(MapFrame)
+     */
+    static public LayerListDialog getInstance() throws IllegalStateException {
+        if (instance == null)
+            throw new IllegalStateException(tr("Dialog not created yet. Invoke createInstance() first"));
+        return instance;
+    }
+
+    /** the model for the layer list */
+    private LayerListModel model;
+
+    /** the selection model */
+    private DefaultListSelectionModel selectionModel;
+
+    /** the list of layers */
+    private LayerList layerList;
+
+    protected JPanel createButtonPanel() {
+        JPanel buttonPanel = new JPanel(new GridLayout(1, 5));
+
+        // -- move up action
+        MoveUpAction moveUpAction = new MoveUpAction();
+        adaptTo(moveUpAction, model);
+        adaptTo(moveUpAction,selectionModel);
+        buttonPanel.add(new SideButton(moveUpAction));
+
+        // -- move down action
+        MoveDownAction moveDownAction = new MoveDownAction();
+        adaptTo(moveDownAction, model);
+        adaptTo(moveDownAction,selectionModel);
+        buttonPanel.add(new SideButton(moveDownAction));
+
+        // -- activate action
+        ActivateLayerAction activateLayerAction = new ActivateLayerAction();
+        adaptTo(activateLayerAction, selectionModel);
+        buttonPanel.add(new SideButton(activateLayerAction, "activate"));
+
+        // -- show hide action
+        ShowHideLayerAction showHideLayerAction = new ShowHideLayerAction();
+        adaptTo(showHideLayerAction, selectionModel);
+        buttonPanel.add(new SideButton(showHideLayerAction, "showhide"));
+
+        // -- merge layer action
+        MergeAction mergeLayerAction = new MergeAction();
+        adaptTo(mergeLayerAction, model);
+        adaptTo(mergeLayerAction,selectionModel);
+        buttonPanel.add(new SideButton(mergeLayerAction));
+
+        //-- delete layer action
+        DeleteLayerAction deleteLayerAction = new DeleteLayerAction();
+        adaptTo(deleteLayerAction, selectionModel);
+        buttonPanel.add(new SideButton(deleteLayerAction, "delete"));
+
+        return buttonPanel;
+    }
+
+    /**
+     * Create an layerlist and attach it to the given mapView.
+     */
+    protected LayerListDialog(MapFrame mapFrame) {
+        super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
+                Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100);
+
+        // create the models
+        //
+        selectionModel = new DefaultListSelectionModel();
+        selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+        model = new LayerListModel(selectionModel);
+        Layer.listeners.add(model);
+
+        // create the list control
+        //
+        layerList = new LayerList(model);
+        layerList.setSelectionModel(selectionModel);
+        layerList.addMouseListener(new LayerListMouseAdapter());
+        layerList.setBackground(UIManager.getColor("Button.background"));
+        layerList.setCellRenderer(new LayerListCellRenderer());
+        add(new JScrollPane(layerList), BorderLayout.CENTER);
+
+        // init the model
+        //
+        final MapView mapView = mapFrame.mapView;
+        model.populate(mapView.getAllLayers());
+        model.setSelectedLayer(mapView.getActiveLayer());
+        model.addLayerListModelListener(
+                new LayerListModelListener() {
+                    public void makeVisible(int index, Layer layer) {
+                        layerList.ensureIndexIsVisible(index);
+                    }
+                }
+        );
+
+        add(createButtonPanel(), BorderLayout.SOUTH);
+    }
+
+    public LayerListModel getModel() {
+        return model;
+    }
+
+    private interface IEnabledStateUpdating {
+        void updateEnabledState();
+    }
+
+    protected void adaptTo(final IEnabledStateUpdating listener, ListSelectionModel listSelectionModel) {
+        listSelectionModel.addListSelectionListener(
+                new ListSelectionListener() {
+                    public void valueChanged(ListSelectionEvent e) {
+                        listener.updateEnabledState();
+                    }
+                }
+        );
+    }
+
+    protected void adaptTo(final IEnabledStateUpdating listener, ListModel listModel) {
+        listModel.addListDataListener(
+                new ListDataListener() {
+                    public void contentsChanged(ListDataEvent e) {
+                        listener.updateEnabledState();
+                    }
+
+                    public void intervalAdded(ListDataEvent e) {
+                        listener.updateEnabledState();
+                    }
+
+                    public void intervalRemoved(ListDataEvent e) {
+                        listener.updateEnabledState();
+                    }
+                }
+        );
+    }
+
+    /**
+     * The action to delete the currently selected layer
+     */
+    public final  class DeleteLayerAction extends AbstractAction implements IEnabledStateUpdating {
+
+        private  Layer layer;
+
+        /**
+         * Creates a {@see DeleteLayerAction} for a specific layer.
+         * 
+         * @param layer the layer. Must not be null.
+         * @exception IllegalArgumentException thrown, if layer is null
+         */
         public DeleteLayerAction(Layer layer) {
-            super(tr("Delete"), ImageProvider.get("dialogs", "delete"));
+            this();
+            if (layer == null)
+                throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
+            this.layer = layer;
+            putValue(NAME, tr("Delete"));
+            updateEnabledState();
+        }
+
+        /**
+         * Creates a {@see DeleteLayerAction} which will delete the currently
+         * selected layers in the layer dialog.
+         * 
+         */
+        public DeleteLayerAction() {
+            putValue(SMALL_ICON,ImageProvider.get("dialogs", "delete"));
             putValue(SHORT_DESCRIPTION, tr("Delete the selected layer."));
             putValue("help", "Action/LayerDelete");
+            updateEnabledState();
+        }
+
+        protected boolean confirmSkipSaving(OsmDataLayer layer) {
+            int result = new ExtendedDialog(Main.parent,
+                    tr("Unsaved Changes"),
+                    tr("There are unsaved changes in the layer''{0}''. Delete the layer anwyay?",layer.getName()),
+                    new String[] {tr("Delete Layer"), tr("Cancel")},
+                    new String[] {"dialogs/delete.png", "cancel.png"}).getValue();
+
+            return result != 1;
+        }
+
+        protected boolean confirmDeleteLayer(Layer layer) {
+            return ConditionalOptionPaneUtil.showConfirmationDialog(
+                    "delete_layer",
+                    Main.parent,
+                    tr("Do you really want to delete the whole layer ''{0}''?", layer.getName()),
+                    tr("Confirmation"),
+                    JOptionPane.YES_NO_OPTION,
+                    JOptionPane.QUESTION_MESSAGE,
+                    JOptionPane.YES_OPTION);
+        }
+
+        public void deleteLayer(Layer layer) {
+            if (layer == null)
+                return;
+            if (layer instanceof OsmDataLayer) {
+                OsmDataLayer dataLayer = (OsmDataLayer)layer;
+                if (dataLayer.isModified() && ! confirmSkipSaving(dataLayer))
+                    return;
+                else if (!confirmDeleteLayer(dataLayer))
+                    return;
+
+            } else {
+                if (!confirmDeleteLayer(layer))
+                    return;
+            }
+            // model and view are going to be updated via LayerChangeListener
+            //
+            Main.main.removeLayer(layer);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            if (this.layer == null) {
+                List<Layer> selectedLayers = getModel().getSelectedLayers();
+                for (Layer layer: selectedLayers) {
+                    deleteLayer(layer);
+                }
+            } else {
+                deleteLayer(this.layer);
+            }
+        }
+
+        public void updateEnabledState() {
+            if (layer == null) {
+                setEnabled(! getModel().getSelectedLayers().isEmpty());
+            } else {
+                setEnabled(true);
+            }
+        }
+    }
+
+    public final class ShowHideLayerAction extends AbstractAction implements IEnabledStateUpdating {
+        private  Layer layer;
+
+        /**
+         * Creates a {@see ShowHideLayerAction} which toggle the visibility of
+         * a specific layer.
+         * 
+         * @param layer  the layer. Must not be null.
+         * @exception IllegalArgumentException thrown, if layer is null
+         */
+        public ShowHideLayerAction(Layer layer) throws IllegalArgumentException {
+            this();
+            if (layer == null)
+                throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
             this.layer = layer;
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            int sel = instance.getSelectedIndex();
-            Layer l = layer != null ? layer : (Layer)instance.getSelectedValue();
-            if(l == null)
-                return;
-            if (l instanceof OsmDataLayer)
-            {
-                if (((OsmDataLayer)l).isModified())
-                {
-                    int result = new ExtendedDialog(Main.parent, tr("Unsaved Changes"),
-                            tr("There are unsaved changes. Delete the layer anwyay?"),
-                            new String[] {tr("Delete Layer"), tr("Cancel")},
-                            new String[] {"dialogs/delete.png", "cancel.png"}).getValue();
-
-                    if(result != 1) return;
-                }
-                else if (!ConditionalOptionPaneUtil.showConfirmationDialog(
-                        "delete_layer",
-                        Main.parent,
-                        tr("Do you really want to delete the whole layer?"),
-                        tr("Confirmation"),
-                        JOptionPane.YES_NO_OPTION,
-                        JOptionPane.QUESTION_MESSAGE,
-                        JOptionPane.YES_OPTION))
-                    return;
-            }
-            Main.main.removeLayer(l);
-            if (sel >= instance.getModel().getSize()) {
-                sel = instance.getModel().getSize()-1;
-            }
-            if (instance.getSelectedValue() == null) {
-                instance.setSelectedIndex(sel);
-            }
-            if (Main.map != null) {
-                Main.map.mapView.setActiveLayer((Layer)instance.getSelectedValue());
-            }
-        }
-    }
-
-    public final static class ShowHideLayerAction extends AbstractAction {
-        private final Layer layer;
-
-        public ShowHideLayerAction(Layer layer) {
-            super(tr("Show/Hide"), ImageProvider.get("dialogs", "showhide"));
+            putValue(NAME, tr("Show/Hide"));
+            updateEnabledState();
+        }
+
+        /**
+         * Creates a {@see ShowHideLayerAction} which will toggle the visibility of
+         * the currently selected layers
+         * 
+         */
+        public ShowHideLayerAction() {
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "showhide"));
             putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the selected layer."));
             putValue("help", "Action/LayerShowHide");
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            if (layer != null) {
+                layer.toggleVisible();
+            } else {
+                for(Layer layer: model.getSelectedLayers()) {
+                    layer.toggleVisible();
+                }
+            }
+        }
+
+        public void updateEnabledState() {
+            if (layer == null) {
+                setEnabled(! getModel().getSelectedLayers().isEmpty());
+            } else {
+                setEnabled(true);
+            }
+        }
+    }
+
+    /**
+     * The action to activate the currently selected layer
+     */
+
+    public final class ActivateLayerAction extends AbstractAction implements IEnabledStateUpdating{
+        private  Layer layer;
+
+        public ActivateLayerAction(Layer layer) throws IllegalArgumentException {
+            this();
+            if (layer == null)
+                throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
             this.layer = layer;
+            putValue(NAME, tr("Activate"));
+            updateEnabledState();
+        }
+
+        public ActivateLayerAction() {
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "activate"));
+            putValue(SHORT_DESCRIPTION, tr("Activate the selected layer"));
+            putValue("help", "Action/ActivateLayer");
+            updateEnabledState();
         }
 
         public void actionPerformed(ActionEvent e) {
-            Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer;
-            if(l == null)
+            Layer toActivate;
+            if (layer != null) {
+                toActivate = layer;
+            } else {
+                toActivate = model.getSelectedLayers().get(0);
+            }
+            getModel().activateLayer(toActivate);
+        }
+
+        protected boolean isActiveLayer(Layer layer) {
+            if (Main.map == null) return false;
+            if (Main.map.mapView == null) return false;
+            return Main.map.mapView.getActiveLayer() == layer;
+        }
+
+        public void updateEnabledState() {
+            if (layer == null) {
+                if (getModel().getSelectedLayers().size() != 1) {
+                    setEnabled(false);
+                    return;
+                }
+                Layer selectedLayer = getModel().getSelectedLayers().get(0);
+                setEnabled(!isActiveLayer(selectedLayer));
+            } else {
+                setEnabled(!isActiveLayer(layer));
+            }
+        }
+    }
+
+    /**
+     * The action to merge the currently selected layer into another layer.
+     */
+    public final class MergeAction extends AbstractAction implements IEnabledStateUpdating {
+        private  Layer layer;
+
+        public MergeAction(Layer layer) throws IllegalArgumentException {
+            this();
+            if (layer == null)
+                throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
+            this.layer = layer;
+            putValue(NAME, tr("Merge"));
+            updateEnabledState();
+        }
+
+        public MergeAction() {
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "mergedown"));
+            putValue(SHORT_DESCRIPTION, tr("Merge this layer into another layer"));
+            putValue("help", "Action/MergeLayer");
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            if (layer != null) {
+                new MergeLayerAction().merge(layer);
+            } else {
+                Layer selectedLayer = getModel().getSelectedLayers().get(0);
+                new MergeLayerAction().merge(selectedLayer);
+            }
+        }
+
+        protected boolean isActiveLayer(Layer layer) {
+            if (Main.map == null) return false;
+            if (Main.map.mapView == null) return false;
+            return Main.map.mapView.getActiveLayer() == layer;
+        }
+
+        public void updateEnabledState() {
+            if (layer == null) {
+                if (getModel().getSelectedLayers().size() != 1) {
+                    setEnabled(false);
+                    return;
+                }
+                Layer selectedLayer = getModel().getSelectedLayers().get(0);
+                List<Layer> targets = getModel().getPossibleMergeTargets(selectedLayer);
+                setEnabled(!targets.isEmpty());
+            } else {
+                List<Layer> targets = getModel().getPossibleMergeTargets(layer);
+                setEnabled(!targets.isEmpty());
+            }
+        }
+    }
+
+    /**
+     * the list cell renderer used to render layer list entries
+     *
+     */
+    class LayerListCellRenderer extends DefaultListCellRenderer {
+
+        protected boolean isActiveLayer(Layer layer) {
+            if (Main.map == null) return false;
+            if (Main.map.mapView == null) return false;
+            return Main.map.mapView.getActiveLayer() == layer;
+        }
+
+        @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+            Layer layer = (Layer)value;
+            JLabel label = (JLabel)super.getListCellRendererComponent(list,
+                    layer.getName(), index, isSelected, cellHasFocus);
+            Icon icon = layer.getIcon();
+            if (isActiveLayer(layer)) {
+                icon = ImageProvider.overlay(icon, "overlay/active", OverlayPosition.SOUTHWEST);
+            }
+            if (!layer.isVisible()) {
+                icon = ImageProvider.overlay(icon, "overlay/invisiblenew", OverlayPosition.SOUTHEAST);
+            }
+            label.setIcon(icon);
+            label.setToolTipText(layer.getToolTipText());
+            return label;
+        }
+    }
+
+    class LayerListMouseAdapter extends MouseAdapter {
+
+        private void openPopup(MouseEvent e) {
+            Point p = e.getPoint();
+            int index = layerList.locationToIndex(p);
+            if (index < 0) return;
+            if (!layerList.getCellBounds(index, index).contains(e.getPoint()))
                 return;
-            l.visible = !l.visible;
-            Main.map.mapView.repaint();
-            instance.repaint();
-        }
-    }
-
-    public final static class ShowHideMarkerText extends AbstractAction {
-        private final Layer layer;
-
-        public ShowHideMarkerText(Layer layer) {
-            super(tr("Show/Hide Text/Icons"), ImageProvider.get("dialogs", "showhide"));
-            putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the marker text and icons."));
-            putValue("help", "Action/ShowHideTextIcons");
-            this.layer = layer;
+            Layer layer = model.getLayer(index);
+            LayerListPopup menu = new LayerListPopup(layerList, layer);
+            menu.show(LayerListDialog.this, p.x, p.y-3);
+        }
+        @Override public void mousePressed(MouseEvent e) {
+            if (e.isPopupTrigger()) {
+                openPopup(e);
+            }
+        }
+        @Override public void mouseReleased(MouseEvent e) {
+            if (e.isPopupTrigger()) {
+                openPopup(e);
+            }
+        }
+        @Override public void mouseClicked(MouseEvent e) {
+            if (e.getClickCount() == 2) {
+                int index = layerList.locationToIndex(e.getPoint());
+                if (!layerList.getCellBounds(index, index).contains(e.getPoint()))
+                    return;
+                Layer layer = model.getLayer(index);
+                String current = Main.pref.get("marker.show "+layer.getName(),"show");
+                Main.pref.put("marker.show "+layer.getName(), current.equalsIgnoreCase("show") ? "hide" : "show");
+                layer.toggleVisible();
+            }
+        }
+    }
+
+    /**
+     * The action to move up the currently selected entries in the list.
+     */
+    class MoveUpAction extends AbstractAction implements  IEnabledStateUpdating{
+        public MoveUpAction() {
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "up"));
+            putValue(SHORT_DESCRIPTION, tr("Move the selected layer one row up."));
+            updateEnabledState();
+        }
+
+        public void updateEnabledState() {
+            setEnabled(model.canMoveUp());
         }
 
         public void actionPerformed(ActionEvent e) {
-            Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer;
-            String current = Main.pref.get("marker.show "+l.name,"show");
-            Main.pref.put("marker.show "+l.name, current.equalsIgnoreCase("show") ? "hide" : "show");
-            Main.map.mapView.repaint();
-            instance.repaint();
-        }
-    }
-
-    /**
-     * The data model for the list component.
-     */
-    DefaultListModel model = new DefaultListModel();
-    /**
-     * The merge action. This is only called, if the current selection and its
-     * item below are editable datasets and the merge button is clicked.
-     */
-    private final SideButton mergeButton;
-    /**
-     * Button for moving layer up.
-     */
-    private final SideButton upButton;
-    /**
-     * Button for moving layer down.
-     */
-    private final SideButton downButton;
-    /**
-     * Button for delete layer.
-     */
-    private Action deleteAction = new DeleteLayerAction(null);
-
-    /**
-     * Create an layerlist and attach it to the given mapView.
-     */
-    public LayerListDialog(MapFrame mapFrame) {
-        super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
-                Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100);
-        instance = new JList(model) {
-            @Override
-            protected void processMouseEvent(MouseEvent e) {
-                // if the layer list is embedded in a detached dialog, the last row is
-                // is selected if a user clicks in the empty space *below* the last row.
-                // This mouse event filter prevents this.
-                //
-                int idx = locationToIndex(e.getPoint());
-                if (getCellBounds(idx, idx).contains(e.getPoint())) {
-                    super.processMouseEvent(e);
-                }
-            }
-        };
-        listScrollPane = new JScrollPane(instance);
-        add(listScrollPane, BorderLayout.CENTER);
-        instance.setBackground(UIManager.getColor("Button.background"));
-        instance.setCellRenderer(new DefaultListCellRenderer(){
-            @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
-                Layer layer = (Layer)value;
-                JLabel label = (JLabel)super.getListCellRendererComponent(list,
-                        layer.name, index, isSelected, cellHasFocus);
-                Icon icon = layer.getIcon();
-                if (!layer.visible) {
-                    icon = ImageProvider.overlay(icon, "overlay/invisible", OverlayPosition.SOUTHEAST);
-                }
-                label.setIcon(icon);
-                label.setToolTipText(layer.getToolTipText());
-                return label;
-            }
-        });
-
-        final MapView mapView = mapFrame.mapView;
-
-        Collection<Layer> data = mapView.getAllLayers();
-        for (Layer l : data) {
-            model.addElement(l);
-        }
-
-        instance.setSelectedValue(mapView.getActiveLayer(), true);
-        instance.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-        instance.addListSelectionListener(new ListSelectionListener(){
-            public void valueChanged(ListSelectionEvent e) {
-                if (instance.getModel().getSize() == 0)
-                    return;
-                if (instance.getSelectedIndex() == -1) {
-                    instance.setSelectedIndex(e.getFirstIndex());
-                }
-                mapView.setActiveLayer((Layer)instance.getSelectedValue());
-            }
-        });
-        Layer.listeners.add(this);
-
-        instance.addMouseListener(new MouseAdapter(){
-            private void openPopup(MouseEvent e) {
-                Point p = e.getPoint();
-                int index = instance.locationToIndex(p);
-                Layer layer = (Layer)instance.getModel().getElementAt(index);
-                LayerListPopup menu = new LayerListPopup(instance, layer);
-                menu.show(listScrollPane, p.x, p.y-3);
-            }
-            @Override public void mousePressed(MouseEvent e) {
-                if (e.isPopupTrigger()) {
-                    openPopup(e);
-                }
-            }
-            @Override public void mouseReleased(MouseEvent e) {
-                if (e.isPopupTrigger()) {
-                    openPopup(e);
-                }
-            }
-            @Override public void mouseClicked(MouseEvent e) {
-                if (e.getClickCount() == 2) {
-                    int index = instance.locationToIndex(e.getPoint());
-                    Layer layer = (Layer)instance.getModel().getElementAt(index);
-                    String current = Main.pref.get("marker.show "+layer.name,"show");
-                    Main.pref.put("marker.show "+layer.name, current.equalsIgnoreCase("show") ? "hide" : "show");
-                    layer.visible = !layer.visible;
-                    Main.map.mapView.repaint();
-                    instance.repaint();
-                }
-            }
-        });
-
-
-        // Buttons
-        JPanel buttonPanel = new JPanel(new GridLayout(1, 5));
-
-        ActionListener upDown = new ActionListener(){
-            public void actionPerformed(ActionEvent e) {
-                Layer l = (Layer)instance.getSelectedValue();
-                int sel = instance.getSelectedIndex();
-                int selDest = e.getActionCommand().equals("up") ? sel-1 : sel+1;
-                mapView.moveLayer(l, selDest);
-                model.set(sel, model.get(selDest));
-                model.set(selDest, l);
-                instance.setSelectedIndex(selDest);
-                updateButtonEnabled();
-                mapView.repaint();
-            }
-        };
-
-        upButton = new SideButton("up", "LayerList", tr("Move the selected layer one row up."), upDown);
-        buttonPanel.add(upButton);
-
-        downButton = new SideButton("down", "LayerList", tr("Move the selected layer one row down."), upDown);
-        buttonPanel.add(downButton);
-
-        buttonPanel.add(new SideButton(new ShowHideLayerAction(null), "showhide"));
-        buttonPanel.add(new SideButton(deleteAction, "delete"));
-
-        mergeButton = new SideButton("mergedown", "LayerList", tr("Merge the layer directly below into the selected layer."),
-                new ActionListener(){
-            public void actionPerformed(ActionEvent e) {
-                Layer lTo = (Layer)instance.getSelectedValue();
-                Layer lFrom = (Layer)model.get(instance.getSelectedIndex()+1);
-                lTo.mergeFrom(lFrom);
-                mapView.removeLayer(lFrom);
-                updateButtonEnabled();
-                mapView.repaint();
-            }
-        });
-        buttonPanel.add(mergeButton);
-
-        add(buttonPanel, BorderLayout.SOUTH);
-
-        updateButtonEnabled();
-    }
-
-    /**
-     * Updates the state of the Buttons.
-     */
-    void updateButtonEnabled() {
-        int sel = instance.getSelectedIndex();
-        Layer l = (Layer)instance.getSelectedValue();
-        boolean enable = model.getSize() > 1;
-        enable = enable && sel < model.getSize()-1;
-        enable = enable && ((Layer)model.get(sel+1)).isMergable(l);
-        mergeButton.setEnabled(enable);
-        upButton.setEnabled(sel > 0);
-        downButton.setEnabled(sel >= 0 && sel < model.getSize()-1);
-        deleteAction.setEnabled(!model.isEmpty());
-
-        if(model.getSize() != 0) {
-            setTitle(tr("Layers: {0}", model.getSize()), true);
-        } else {
-            setTitle(tr("Layers"), false);
-        }
-    }
-
-    /**
-     * Add the new layer to the list.
-     */
-    public void layerAdded(Layer newLayer) {
-        int pos = Main.map.mapView.getLayerPos(newLayer);
-        model.add(pos, newLayer);
-        updateButtonEnabled();
-    }
-
-    public void layerRemoved(Layer oldLayer) {
-        model.removeElement(oldLayer);
-        if (model.isEmpty()) {
-            Layer.listeners.remove(this);
-            return;
-        }
-        if (instance.getSelectedIndex() == -1) {
-            instance.setSelectedIndex(0);
-        }
-        updateButtonEnabled();
-    }
-
-    /**
-     * If the newLayer is not the actual selection, select it.
-     */
-    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
-        if (newLayer != instance.getSelectedValue()) {
-            instance.setSelectedValue(newLayer, true);
-        }
-        updateButtonEnabled();
+            model.moveUp();
+        }
+    }
+
+    /**
+     * The action to move down the currently selected entries in the list.
+     */
+    class MoveDownAction extends AbstractAction implements IEnabledStateUpdating {
+        public MoveDownAction() {
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "down"));
+            putValue(SHORT_DESCRIPTION, tr("Move the selected layer one row down."));
+            updateEnabledState();
+        }
+
+        public void updateEnabledState() {
+            setEnabled(model.canMoveDown());
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            model.moveDown();
+        }
+    }
+
+    /**
+     * Observer interface to be implemented by views using {@see LayerListModel}
+     *
+     */
+    public interface LayerListModelListener {
+        public void makeVisible(int index, Layer layer);
+    }
+
+    /**
+     * The layer list model. The model manages a list of layers and provides methods for
+     * moving layers up and down, for toggling their visibiliy, for activating a layer.
+     * 
+     * The model is a {@see ListModel} and it provides a {@see ListSelectionModel}. It expectes
+     * to be configured with a {@see DefaultListSelectionModel}. The selection model is used
+     * to update the selection state of views depending on messages sent to the model.
+     * 
+     * The model manages a list of {@see LayerListModelListener} which are mainly notified if
+     * the model requires views to make a specific list entry visible.
+     */
+    public class LayerListModel extends DefaultListModel implements LayerChangeListener, PropertyChangeListener{
+
+        private ArrayList<Layer> layers;
+        private DefaultListSelectionModel selectionModel;
+        private CopyOnWriteArrayList<LayerListModelListener> listeners;
+
+        private LayerListModel(DefaultListSelectionModel selectionModel) {
+            layers = new ArrayList<Layer>();
+            this.selectionModel = selectionModel;
+            listeners = new CopyOnWriteArrayList<LayerListModelListener>();
+        }
+
+        public void addLayerListModelListener(LayerListModelListener listener) {
+            synchronized(listeners) {
+                if (listener != null && !listeners.contains(listener)) {
+                    listeners.add(listener);
+                }
+            }
+        }
+
+        public void removeLayerListModelListener(LayerListModelListener listener) {
+            synchronized(listeners) {
+                if (listener != null && listeners.contains(listener)) {
+                    listeners.remove(listener);
+                }
+            }
+        }
+
+        protected void fireMakeVisible(int index, Layer layer) {
+            for (LayerListModelListener listener : listeners) {
+                listener.makeVisible(index, layer);
+            }
+        }
+
+        public void populate(Collection<Layer> layers) {
+            if (layers == null)
+                return;
+            this.layers.clear();
+            this.layers.addAll(layers);
+            for (Layer layer: this.layers) {
+                layer.addPropertyChangeListener(this);
+            }
+            fireContentsChanged(this, 0, getSize());
+        }
+
+        public void setSelectedLayer(Layer layer) {
+            int idx = layers.indexOf(layer);
+            if (idx >= 0) {
+                selectionModel.setSelectionInterval(idx, idx);
+            }
+            fireContentsChanged(this, 0, getSize());
+            ensureSelectedIsVisible();
+        }
+
+        public List<Layer> getSelectedLayers() {
+            ArrayList<Layer> selected = new ArrayList<Layer>();
+            for (int i=0; i<layers.size(); i++) {
+                if (selectionModel.isSelectedIndex(i)) {
+                    selected.add(layers.get(i));
+                }
+            }
+            return selected;
+        }
+
+        public List<Integer> getSelectedRows() {
+            ArrayList<Integer> selected = new ArrayList<Integer>();
+            for (int i=0; i<layers.size();i++) {
+                if (selectionModel.isSelectedIndex(i)) {
+                    selected.add(i);
+                }
+            }
+            return selected;
+        }
+
+        public void removeLayer(Layer layer) {
+            if (layer == null)
+                return;
+            List<Integer> selectedRows = getSelectedRows();
+            int deletedRow = layers.indexOf(layer);
+            if (deletedRow < 0)
+                // layer not found in the list of layers
+                return;
+            layers.remove(layer);
+            fireContentsChanged(this, 0,getSize());
+            for (int row: selectedRows) {
+                if (row < deletedRow) {
+                    selectionModel.addSelectionInterval(row, row);
+                } else if (row == deletedRow){
+                    // do nothing
+                } else {
+                    selectionModel.addSelectionInterval(row-1, row-1);
+                }
+            }
+            ensureSelectedIsVisible();
+        }
+
+        public void addLayer(Layer layer) {
+            if (layer == null) return;
+            if (layers.contains(layer)) return;
+            layers.add(layer);
+            layer.addPropertyChangeListener(this);
+            fireContentsChanged(this, 0, getSize());
+        }
+
+        public Layer getFirstLayer() {
+            if (getSize() == 0) return null;
+            return layers.get(0);
+        }
+
+        public Layer getLayer(int index) {
+            if (index < 0 || index >= getSize())
+                return null;
+            return layers.get(index);
+        }
+
+        public boolean canMoveUp() {
+            List<Integer> sel = getSelectedRows();
+            return !sel.isEmpty() && sel.get(0) > 0;
+        }
+
+        public void moveUp() {
+            if (!canMoveUp()) return;
+            List<Integer> sel = getSelectedRows();
+            for (int row: sel) {
+                Layer l1 = layers.get(row);
+                Layer l2 = layers.get(row-1);
+                layers.set(row, l2);
+                layers.set(row-1,l1);
+                Main.map.mapView.moveLayer(l1, row-1);
+            }
+            fireContentsChanged(this, 0, getSize());
+            selectionModel.clearSelection();
+            for(int row: sel) {
+                selectionModel.addSelectionInterval(row-1, row-1);
+            }
+            ensureSelectedIsVisible();
+        }
+
+        public boolean canMoveDown() {
+            List<Integer> sel = getSelectedRows();
+            return !sel.isEmpty() && sel.get(sel.size()-1) < layers.size()-1;
+        }
+
+        public void moveDown() {
+            if (!canMoveDown()) return;
+            List<Integer> sel = getSelectedRows();
+            Collections.reverse(sel);
+            for (int row: sel) {
+                Layer l1 = layers.get(row);
+                Layer l2 = layers.get(row+1);
+                layers.set(row, l2);
+                layers.set(row+1,l1);
+                Main.map.mapView.moveLayer(l1, row+1);
+            }
+            fireContentsChanged(this, 0, getSize());
+            selectionModel.clearSelection();
+            for(int row: sel) {
+                selectionModel.addSelectionInterval(row+1, row+1);
+            }
+            ensureSelectedIsVisible();
+        }
+
+        protected void ensureSelectedIsVisible() {
+            int index = selectionModel.getMinSelectionIndex();
+            if (index <0 )return;
+            if (index >= layers.size()) return;
+            Layer layer = layers.get(index);
+            fireMakeVisible(index, layer);
+        }
+
+        public List<Layer> getPossibleMergeTargets(Layer layer) {
+            ArrayList<Layer> targets = new ArrayList<Layer>();
+            if (layer == null)
+                return targets;
+            for(Layer target: layers) {
+                if (layer == target) {
+                    continue;
+                }
+                if (target.isMergable(layer)) {
+                    targets.add(target);
+                }
+            }
+            return targets;
+        }
+
+        public void activateLayer(Layer layer) {
+            layers.remove(layer);
+            layers.add(0,layer);
+            Main.map.mapView.setActiveLayer(layer);
+            layer.setVisible(true);
+            selectionModel.setSelectionInterval(0,0);
+            ensureSelectedIsVisible();
+        }
+
+        /* ------------------------------------------------------------------------------ */
+        /* Interface ListModel                                                            */
+        /* ------------------------------------------------------------------------------ */
+        @Override
+        public Object getElementAt(int index) {
+            return layers.get(index);
+        }
+
+        @Override
+        public int getSize() {
+            return layers.size();
+        }
+
+        /* ------------------------------------------------------------------------------ */
+        /* Interface LayerChangeListener                                                  */
+        /* ------------------------------------------------------------------------------ */
+        public void activeLayerChange(Layer oldLayer, Layer newLayer) {
+            if (oldLayer != null) {
+                int idx = layers.indexOf(oldLayer);
+                if (idx >= 0) {
+                    fireContentsChanged(this, idx,idx);
+                }
+            }
+
+            if (newLayer != null) {
+                int idx = layers.indexOf(newLayer);
+                if (idx >= 0) {
+                    fireContentsChanged(this, idx,idx);
+                }
+            }
+        }
+
+        public void layerAdded(Layer newLayer) {
+            addLayer(newLayer);
+        }
+
+        public void layerRemoved(Layer oldLayer) {
+            removeLayer(oldLayer);
+        }
+
+        /* ------------------------------------------------------------------------------ */
+        /* Interface PropertyChangeListener                                               */
+        /* ------------------------------------------------------------------------------ */
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (evt.getSource() instanceof Layer) {
+                Layer layer = (Layer)evt.getSource();
+                int idx = layers.indexOf(layer);
+                if (idx < 0) return;
+                fireContentsChanged(this, idx, idx);
+            }
+        }
+    }
+
+    class LayerList extends JList {
+        public LayerList(ListModel dataModel) {
+            super(dataModel);
+        }
+
+        @Override
+        protected void processMouseEvent(MouseEvent e) {
+            // if the layer list is embedded in a detached dialog, the last row is
+            // is selected if a user clicks in the empty space *below* the last row.
+            // This mouse event filter prevents this.
+            //
+            int idx = locationToIndex(e.getPoint());
+            if (getCellBounds(idx, idx).contains(e.getPoint())) {
+                super.processMouseEvent(e);
+            }
+        }
+    }
+
+    public ShowHideLayerAction createShowHideLayerAction(Layer layer) {
+        return new ShowHideLayerAction(layer);
+    }
+
+    public DeleteLayerAction createDeleteLayerAction(Layer layer) {
+        return new DeleteLayerAction(layer);
+    }
+
+    public ActivateLayerAction createActivateLayerAction(Layer layer) {
+        return new ActivateLayerAction(layer);
+    }
+
+    public MergeAction createMergeLayerAction(Layer layer) {
+        return new MergeAction(layer);
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1890)
@@ -660,10 +660,10 @@
             );
             switch(ret) {
-            case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: return;
-            case JOptionPane.CLOSED_OPTION: return;
-            case JOptionPane.NO_OPTION: return;
-            case JOptionPane.YES_OPTION:
-                memberTableModel.removeMembersReferringTo(toCheck);
-                break;
+                case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: return;
+                case JOptionPane.CLOSED_OPTION: return;
+                case JOptionPane.NO_OPTION: return;
+                case JOptionPane.YES_OPTION:
+                    memberTableModel.removeMembersReferringTo(toCheck);
+                    break;
             }
         }
@@ -698,9 +698,9 @@
             );
             switch(ret) {
-            case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION : return true;
-            case JOptionPane.YES_OPTION: return true;
-            case JOptionPane.NO_OPTION: return false;
-            case JOptionPane.CLOSED_OPTION: return false;
-            case JOptionPane.CANCEL_OPTION: throw new AddAbortException();
+                case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION : return true;
+                case JOptionPane.YES_OPTION: return true;
+                case JOptionPane.NO_OPTION: return false;
+                case JOptionPane.CLOSED_OPTION: return false;
+                case JOptionPane.CANCEL_OPTION: throw new AddAbortException();
             }
             // should not happen
@@ -1073,7 +1073,7 @@
             );
             switch(ret) {
-            case JOptionPane.CANCEL_OPTION: return false;
-            case JOptionPane.YES_OPTION: return true;
-            case JOptionPane.NO_OPTION: return false;
+                case JOptionPane.CANCEL_OPTION: return false;
+                case JOptionPane.YES_OPTION: return true;
+                case JOptionPane.NO_OPTION: return false;
             }
             return false;
@@ -1314,8 +1314,8 @@
             );
             switch(ret) {
-            case JOptionPane.YES_OPTION: return true;
-            case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: return true;
-            default:
-                return false;
+                case JOptionPane.YES_OPTION: return true;
+                case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: return true;
+                default:
+                    return false;
             }
         }
Index: trunk/src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java	(revision 1890)
@@ -266,5 +266,5 @@
             MapFrame frame = Main.map;
             if (frame != null) {
-              frame.mapView.repaint();
+                frame.mapView.repaint();
             }
         }
@@ -276,5 +276,5 @@
         private final GpxLayer gpxLayer;
         public Loader(Collection<File> files, GpxLayer gpxLayer) {
-            super(tr("Images for {0}", gpxLayer.name));
+            super(tr("Images for {0}", gpxLayer.getName()));
             this.files = files;
             this.gpxLayer = gpxLayer;
@@ -412,5 +412,5 @@
                     return;
                 mousePressed  = true;
-                if (visible) {
+                if (isVisible()) {
                     Main.map.mapView.repaint();
                 }
@@ -420,5 +420,5 @@
                     return;
                 mousePressed = false;
-                if (!visible)
+                if (!isVisible())
                     return;
                 for (int i = data.size(); i > 0; --i) {
@@ -699,6 +699,6 @@
         });
         return new Component[]{
-                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
                 new JSeparator(),
                 sync,
Index: trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 1890)
@@ -45,6 +45,4 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.RenameLayerAction;
-import org.openstreetmap.josm.actions.SaveAction;
-import org.openstreetmap.josm.actions.SaveAsAction;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTaskList;
 import org.openstreetmap.josm.data.coor.EastNorth;
@@ -91,10 +89,10 @@
     public GpxLayer(GpxData d, String name) {
         this(d);
-        this.name = name;
+        this.setName(name);
     }
 
     public GpxLayer(GpxData d, String name, boolean isLocal) {
         this(d);
-        this.name = name;
+        this.setName(name);
         this.isLocalFile = isLocal;
     }
@@ -129,5 +127,5 @@
                     panel.add(b);
                 }
-                String propName = "draw.rawgps.lines.layer " + name;
+                String propName = "draw.rawgps.lines.layer " + getName();
                 if (Main.pref.hasKey(propName)) {
                     group.setSelected(r[Main.pref.getBoolean(propName) ? 1 : 2].getModel(), true);
@@ -157,5 +155,5 @@
         color.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
-                JColorChooser c = new JColorChooser(getColor(name));
+                JColorChooser c = new JColorChooser(getColor(getName()));
                 Object[] options = new Object[] { tr("OK"), tr("Cancel"), tr("Default") };
                 int answer = OptionPaneUtil.showOptionDialog(
@@ -169,10 +167,10 @@
                 switch (answer) {
                     case 0:
-                        Main.pref.putColor("layer " + name, c.getColor());
+                        Main.pref.putColor("layer " + getName(), c.getColor());
                         break;
                     case 1:
                         return;
                     case 2:
-                        Main.pref.putColor("layer " + name, null);
+                        Main.pref.putColor("layer " + getName(), null);
                         break;
                 }
@@ -196,5 +194,5 @@
                 }
 
-                MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", name),
+                MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", getName()),
                         getAssociatedFile(), me);
                 if (ml.data.size() > 0) {
@@ -256,5 +254,5 @@
                         names = "";
                     }
-                    MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", name) + names,
+                    MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", getName()) + names,
                             getAssociatedFile(), me);
                     if (sel != null) {
@@ -312,11 +310,11 @@
 
         if (Main.applet)
-            return new Component[] { new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), new JSeparator(), color, line,
+            return new Component[] { new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)), new JSeparator(), color, line,
                 new JMenuItem(new ConvertToDataLayerAction()), new JSeparator(),
                 new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)), new JSeparator(),
                 new JMenuItem(new LayerListPopup.InfoAction(this)) };
-        return new Component[] { new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), new JSeparator(),
+        return new Component[] { new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)), new JSeparator(),
                 new JMenuItem(new LayerSaveAction(this)), new JMenuItem(new LayerSaveAsAction(this)), color, line,
                 tagimage, importAudio, markersFromNamedTrackpoints, new JMenuItem(new ConvertToDataLayerAction()),
@@ -416,5 +414,5 @@
          ****************************************************************/
         // Long startTime = System.currentTimeMillis();
-        Color neutralColor = getColor(name);
+        Color neutralColor = getColor(getName());
         // also draw lines between points belonging to different segments
         boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force");
@@ -431,5 +429,5 @@
         boolean lines = (Main.pref.getBoolean("draw.rawgps.lines", true) || (Main.pref
                 .getBoolean("draw.rawgps.lines.localfiles") && this.isLocalFile));
-        String linesKey = "draw.rawgps.lines.layer " + name;
+        String linesKey = "draw.rawgps.lines.layer " + getName();
         // draw lines, per-layer setting
         if (Main.pref.hasKey(linesKey)) {
@@ -726,5 +724,5 @@
             }
             Main.main
-            .addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.name), getAssociatedFile()));
+            .addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.getName()), getAssociatedFile()));
             Main.main.removeLayer(GpxLayer.this);
         }
Index: trunk/src/org/openstreetmap/josm/gui/layer/Layer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 1890)
@@ -8,4 +8,6 @@
 import java.awt.Graphics;
 import java.awt.event.ActionEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
 import java.io.File;
 import java.util.Collection;
@@ -39,4 +41,9 @@
  */
 abstract public class Layer implements Destroyable, MapViewPaintable {
+    static public final String VISIBLE_PROP = Layer.class.getName() + ".visible";
+    static public final String NAME_PROP = Layer.class.getName() + ".name";
+
+    /** keeps track of property change listeners */
+    private PropertyChangeSupport propertyChangeSupport;
 
     /**
@@ -58,5 +65,9 @@
     /**
      * The visibility state of the layer.
-     */
+     * 
+     * @deprecated use {@see #setVisible(boolean)} and {@see #isVisible()} instead. This field
+     * is going to be private (or protected) in a future release.
+     */
+    @Deprecated
     public boolean visible = true;
 
@@ -68,5 +79,9 @@
     /**
      * The name of this layer.
-     */
+     * 
+     * @deprecated use {@see #getName()} and {@see #setName(String)} instead. This field
+     * is going to be private  (or protected) in the future.
+     */
+    @Deprecated
     public String name;
     /**
@@ -79,5 +94,6 @@
      */
     public Layer(String name) {
-        this.name = name;
+        this.propertyChangeSupport = new PropertyChangeSupport(this);
+        setName(name);
     }
 
@@ -140,4 +156,21 @@
     public String getName() {
         return name;
+    }
+
+    /**
+     * Sets the name of the layer
+     *
+     *@param name the name. If null, the name is set to the empty string.
+     *
+     */
+    public void setName(String name) {
+        if (name == null) {
+            name = "";
+        }
+        String oldValue = this.name;
+        this.name = name;
+        if (!oldValue.equals(this.name)) {
+            propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name);
+        }
     }
 
@@ -189,3 +222,59 @@
     }
 
+    /**
+     * Sets the visibility of this layer. Emits property change event for
+     * property {@see #VISIBLE_PROP}.
+     * 
+     * @param visible true, if the layer is visible; false, otherwise.
+     */
+    public void setVisible(boolean visible) {
+        boolean oldValue = this.visible;
+        this.visible  = visible;
+        if (oldValue != this.visible) {
+            fireVisibleChanged(oldValue, this.visible);
+        }
+    }
+
+    /**
+     * Replies true if this layer is visible. False, otherwise.
+     * @return  true if this layer is visible. False, otherwise.
+     */
+    public boolean isVisible() {
+        return visible;
+    }
+
+    /**
+     * Toggles the visibility state of this layer.
+     */
+    public void toggleVisible() {
+        setVisible(!isVisible());
+    }
+
+    /**
+     * Adds a {@see PropertyChangeListener}
+     * 
+     * @param listener the listener
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        propertyChangeSupport.addPropertyChangeListener(listener);
+    }
+
+    /**
+     * Removes a {@see PropertyChangeListener}
+     * 
+     * @param listener the listener
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        propertyChangeSupport.removePropertyChangeListener(listener);
+    }
+
+    /**
+     * fires a property change for the property {@see #VISIBLE_PROP}
+     * 
+     * @param oldValue the old value
+     * @param newValue the new value
+     */
+    protected void fireVisibleChanged(boolean oldValue, boolean newValue) {
+        propertyChangeSupport.firePropertyChange(VISIBLE_PROP, oldValue, newValue);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 1890)
@@ -407,5 +407,5 @@
         }
         final JPanel p = new JPanel(new GridBagLayout());
-        p.add(new JLabel(tr("{0} consists of:", name)), GBC.eol());
+        p.add(new JLabel(tr("{0} consists of:", getName())), GBC.eol());
         for (int i = 0; i < counter.normal.length; ++i) {
             String s = counter.normal[i]+" "+trn(counter.names[i],counter.names[i]+"s",counter.normal[i]);
@@ -423,6 +423,9 @@
         if (Main.applet)
             return new Component[]{
-                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createActivateLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
+                new JSeparator(),
+                new JMenuItem(LayerListDialog.getInstance().createMergeLayerAction(this)),
                 new JSeparator(),
                 new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)),
@@ -430,7 +433,9 @@
                 new JMenuItem(new LayerListPopup.InfoAction(this))};
         return new Component[]{
-                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createActivateLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
                 new JSeparator(),
+                new JMenuItem(LayerListDialog.getInstance().createMergeLayerAction(this)),
                 new JMenuItem(new LayerSaveAction(this)),
                 new JMenuItem(new LayerSaveAsAction(this)),
@@ -515,5 +520,5 @@
         }
         public void actionPerformed(ActionEvent e) {
-            Main.main.addLayer(new GpxLayer(toGpxData(), tr("Converted from: {0}", name)));
+            Main.main.addLayer(new GpxLayer(toGpxData(), tr("Converted from: {0}", getName())));
             Main.main.removeLayer(OsmDataLayer.this);
         }
Index: trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(revision 1890)
@@ -13,10 +13,5 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.UnknownHostException;
 import java.util.Collection;
 
@@ -25,5 +20,4 @@
 import javax.swing.ButtonGroup;
 import javax.swing.Icon;
-import javax.swing.JCheckBox;
 import javax.swing.JColorChooser;
 import javax.swing.JLabel;
@@ -33,9 +27,6 @@
 import javax.swing.JRadioButton;
 import javax.swing.JSeparator;
-import javax.swing.JTextField;
-import javax.swing.text.html.Option;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.GpxExportAction;
 import org.openstreetmap.josm.actions.RenameLayerAction;
 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
@@ -51,5 +42,4 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
-import org.openstreetmap.josm.io.MultiPartFormOutputStream;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -91,5 +81,5 @@
                 ds.ways.add(w);
             }
-            Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", RawGpsLayer.this.name), null));
+            Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", RawGpsLayer.this.getName()), null));
             Main.main.removeLayer(RawGpsLayer.this);
         }
@@ -129,10 +119,10 @@
 
     @Override public void paint(Graphics g, MapView mv) {
-        g.setColor(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));
+        g.setColor(Main.pref.getColor(marktr("gps point"), "layer "+getName(), Color.gray));
         Point old = null;
 
         boolean force = Main.pref.getBoolean("draw.rawgps.lines.force");
         boolean lines = Main.pref.getBoolean("draw.rawgps.lines", true);
-        String linesKey = "draw.rawgps.lines.layer "+name;
+        String linesKey = "draw.rawgps.lines.layer "+getName();
         if (Main.pref.hasKey(linesKey)) {
             lines = Main.pref.getBoolean(linesKey);
@@ -197,5 +187,5 @@
         }
         b.append("</html>");
-        return "<html>"+trn("{0} consists of {1} track", "{0} consists of {1} tracks", data.size(), name, data.size())+" ("+trn("{0} point", "{0} points", points, points)+")<br>"+b.toString();
+        return "<html>"+trn("{0} consists of {1} track", "{0} consists of {1} tracks", data.size(), getName(), data.size())+" ("+trn("{0} point", "{0} points", points, points)+")<br>"+b.toString();
     }
 
@@ -214,5 +204,5 @@
                     panel.add(b);
                 }
-                String propName = "draw.rawgps.lines.layer "+name;
+                String propName = "draw.rawgps.lines.layer "+getName();
                 if (Main.pref.hasKey(propName)) {
                     group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
@@ -241,5 +231,5 @@
         color.addActionListener(new ActionListener(){
             public void actionPerformed(ActionEvent e) {
-                JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));
+                JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+getName(), Color.gray));
                 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
                 int answer = OptionPaneUtil.showOptionDialog(
@@ -251,10 +241,10 @@
                 switch (answer) {
                     case 0:
-                        Main.pref.putColor("layer "+name, c.getColor());
+                        Main.pref.putColor("layer "+getName(), c.getColor());
                         break;
                     case 1:
                         return;
                     case 2:
-                        Main.pref.putColor("layer "+name, null);
+                        Main.pref.putColor("layer "+getName(), null);
                         break;
                 }
@@ -265,6 +255,6 @@
         if (Main.applet)
             return new Component[]{
-                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
                 new JSeparator(),
                 color,
@@ -276,6 +266,6 @@
                 new JMenuItem(new LayerListPopup.InfoAction(this))};
         return new Component[]{
-                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
                 new JSeparator(),
                 new JMenuItem(new LayerGpxExportAction(this)),
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 1889)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 1890)
@@ -19,4 +19,5 @@
 import java.util.Collection;
 
+import javax.swing.AbstractAction;
 import javax.swing.Icon;
 import javax.swing.JColorChooser;
@@ -62,11 +63,4 @@
     public GpxLayer fromLayer = null;
 
-    /*
-    private Icon audioTracerIcon = null;
-    private EastNorth playheadPosition = null;
-    private static Timer timer = null;
-    private static double audioAnimationInterval = 0.0; // seconds
-    private static double playheadTime = -1.0;
-     */
     public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) {
 
@@ -122,5 +116,5 @@
                             return;
                         mousePressed  = true;
-                        if (visible) {
+                        if (isVisible()) {
                             Main.map.mapView.repaint();
                         }
@@ -130,5 +124,5 @@
                             return;
                         mousePressed = false;
-                        if (!visible)
+                        if (!isVisible())
                             return;
                         if (ev.getPoint() != null) {
@@ -161,7 +155,7 @@
         boolean mousePressedTmp = mousePressed;
         Point mousePos = mv.getMousePosition();
-        String mkrTextShow = Main.pref.get("marker.show "+name, "show");
-
-        g.setColor(getColor(name));
+        String mkrTextShow = Main.pref.get("marker.show "+getName(), "show");
+
+        g.setColor(getColor(getName()));
 
         for (Marker mkr : data) {
@@ -195,5 +189,5 @@
 
     @Override public Object getInfoComponent() {
-        return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), name, data.size()) + "</html>";
+        return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), getName(), data.size()) + "</html>";
     }
 
@@ -203,5 +197,5 @@
         color.addActionListener(new ActionListener(){
             public void actionPerformed(ActionEvent e) {
-                JColorChooser c = new JColorChooser(getColor(name));
+                JColorChooser c = new JColorChooser(getColor(getName()));
                 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
                 int answer = OptionPaneUtil.showOptionDialog(
@@ -215,12 +209,12 @@
                 );
                 switch (answer) {
-                case 0:
-                    Main.pref.putColor("layer "+name, c.getColor());
-                    break;
-                case 1:
-                    return;
-                case 2:
-                    Main.pref.putColor("layer "+name, null);
-                    break;
+                    case 0:
+                        Main.pref.putColor("layer "+getName(), c.getColor());
+                        break;
+                    case 1:
+                        return;
+                    case 2:
+                        Main.pref.putColor("layer "+getName(), null);
+                        break;
                 }
                 Main.map.repaint();
@@ -282,7 +276,7 @@
 
         Collection<Component> components = new ArrayList<Component>();
-        components.add(new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)));
-        components.add(new JMenuItem(new LayerListDialog.ShowHideMarkerText(this)));
-        components.add(new JMenuItem(new LayerListDialog.DeleteLayerAction(this)));
+        components.add(new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)));
+        components.add(new JMenuItem(new ShowHideMarkerText(this)));
+        components.add(new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)));
         components.add(new JSeparator());
         components.add(color);
@@ -450,3 +444,20 @@
     }
 
+
+    public final  class ShowHideMarkerText extends AbstractAction {
+        private final Layer layer;
+
+        public ShowHideMarkerText(Layer layer) {
+            super(tr("Show/Hide Text/Icons"), ImageProvider.get("dialogs", "showhide"));
+            putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the marker text and icons."));
+            putValue("help", "Action/ShowHideTextIcons");
+            this.layer = layer;
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            String current = Main.pref.get("marker.show "+layer.getName(),"show");
+            Main.pref.put("marker.show "+layer.getName(), current.equalsIgnoreCase("show") ? "hide" : "show");
+            Main.map.mapView.repaint();
+        }
+    }
 }
Index: trunk/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java	(revision 1890)
+++ trunk/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java	(revision 1890)
@@ -0,0 +1,394 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.visitor;
+
+import java.util.Collection;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+
+import static org.junit.Assert.*;
+
+public class MergeSourceBuildingVisitorTest {
+
+    protected OsmPrimitive lookupByName(Collection<? extends OsmPrimitive> primitives, String name) {
+        if (primitives == null) return null;
+        if (name == null) return null;
+        for (OsmPrimitive primitive: primitives) {
+            if (name.equals(primitive.get("name")))
+                return primitive;
+        }
+        return null;
+    }
+
+    @Test
+    public void test_Nodes() {
+        DataSet source = new DataSet();
+        Node n1 = new Node(1);
+        Node n2 = new Node(new LatLon(10.0,10.0));
+        n2.put("name","n2");
+        Node n3 = new Node(3);
+        Node n4 = new Node(new LatLon(20.0,20.0));
+        n4.put("name","n4");
+        source.nodes.add(n1);
+        source.nodes.add(n2);
+        source.nodes.add(n3);
+        source.nodes.add(n4);
+        source.setSelected(n1,n2);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(2, hull.nodes.size());
+
+        OsmPrimitive p = hull.getPrimitiveById(1);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Node.class);
+
+        p = hull.getPrimitiveById(3);
+        assertNull(p);
+
+        p = lookupByName(hull.nodes, "n2");
+        assertNotNull(p);
+
+        p = lookupByName(hull.nodes, "n4");
+        assertNull(p);
+    }
+
+
+    @Test
+    public void test_OneWay() {
+        DataSet source = new DataSet();
+        Node n1 = new Node(1);
+        Node n2 = new Node(2);
+        Way w1 = new Way(3);
+        w1.nodes.add(n1);
+        w1.nodes.add(n2);
+        source.nodes.add(n1);
+        source.nodes.add(n2);
+        source.ways.add(w1);
+        source.setSelected(w1);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(1, hull.ways.size());
+        assertEquals(2, hull.nodes.size());
+
+        OsmPrimitive p = hull.getPrimitiveById(1);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Node.class);
+
+        p = hull.getPrimitiveById(2);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Node.class);
+
+        p = hull.getPrimitiveById(3);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Way.class);
+    }
+
+    @Test
+    public void test_OneWay_NodesSelectedToo() {
+        DataSet source = new DataSet();
+        Node n1 = new Node(1);
+        Node n2 = new Node(2);
+        Way w1 = new Way(3);
+        w1.nodes.add(n1);
+        w1.nodes.add(n2);
+        source.nodes.add(n1);
+        source.nodes.add(n2);
+        source.ways.add(w1);
+        source.setSelected(w1,n1,n2);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(1, hull.ways.size());
+        assertEquals(2, hull.nodes.size());
+
+        OsmPrimitive p = hull.getPrimitiveById(1);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Node.class);
+
+        p = hull.getPrimitiveById(2);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Node.class);
+
+        p = hull.getPrimitiveById(3);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Way.class);
+    }
+
+    @Test
+    public void test_OneWay_Incomplete() {
+        DataSet source = new DataSet();
+        Way w1 = new Way(3);
+        w1.incomplete = true;
+        source.ways.add(w1);
+        source.setSelected(w1);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(1, hull.ways.size());
+
+        OsmPrimitive p = hull.getPrimitiveById(3);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Way.class);
+        assertTrue(p.incomplete);
+    }
+
+    @Test
+    public void test_OneRelation_ExistingMembersSelected() {
+        DataSet source = new DataSet();
+        Relation r1 = new Relation(1);
+        Node n20 = new Node(20);
+        r1.members.add(new RelationMember("node-20",n20));
+        Way w30 = new Way(30);
+        Node n21;
+        w30.nodes.add(n21 = new Node(21));
+        Node n22;
+        w30.nodes.add(n22 = new Node(22));
+        r1.members.add(new RelationMember("way-30",w30));
+        Relation r40 = new Relation(40);
+        r1.members.add(new RelationMember("relation-40", r40));
+        source.nodes.add(n20);
+        source.nodes.add(n21);
+        source.nodes.add(n22);
+        source.ways.add(w30);
+        source.relations.add(r1);
+        source.relations.add(r40);
+        source.setSelected(r1,n20,w30,r40);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(1, hull.ways.size());
+        assertEquals(3, hull.nodes.size());
+        assertEquals(2, hull.relations.size());
+
+        OsmPrimitive p = hull.getPrimitiveById(1);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Relation.class);
+
+        Way w = (Way)hull.getPrimitiveById(30);
+        assertNotNull(w);
+        assertEquals(2, w.nodes.size());
+        Node n = (Node)hull.getPrimitiveById(21);
+        assertNotNull(n);
+        assertTrue(w.nodes.contains(n));
+
+        n = (Node)hull.getPrimitiveById(22);
+        assertNotNull(n);
+        assertTrue(w.nodes.contains(n));
+
+        Relation r = (Relation)hull.getPrimitiveById(40);
+        assertNotNull(r);
+
+        r = (Relation)hull.getPrimitiveById(1);
+        assertNotNull(r);
+        assertEquals(3, r.members.size());
+        RelationMember m = new RelationMember("node-20", hull.getPrimitiveById(20));
+        assertTrue(r.members.contains(m));
+        m = new RelationMember("way-30", hull.getPrimitiveById(30));
+        assertTrue(r.members.contains(m));
+        m = new RelationMember("relation-40", hull.getPrimitiveById(40));
+        assertTrue(r.members.contains(m));
+    }
+
+    @Test
+    public void test_OneRelation_ExistingMembersNotSelected() {
+        DataSet source = new DataSet();
+        Relation r1 = new Relation(1);
+        Node n20 = new Node(20);
+        r1.members.add(new RelationMember("node-20",n20));
+        Way w30 = new Way(30);
+        Node n21;
+        w30.nodes.add(n21 = new Node(21));
+        Node n22;
+        w30.nodes.add(n22 = new Node(22));
+        r1.members.add(new RelationMember("way-30",w30));
+        Relation r40 = new Relation(40);
+        r1.members.add(new RelationMember("relation-40", r40));
+        source.nodes.add(n20);
+        source.nodes.add(n21);
+        source.nodes.add(n22);
+        source.ways.add(w30);
+        source.relations.add(r1);
+        source.relations.add(r40);
+        source.setSelected(r1);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(1, hull.ways.size());
+        assertEquals(1, hull.nodes.size());
+        assertEquals(2, hull.relations.size());
+
+        OsmPrimitive p = hull.getPrimitiveById(1);
+        assertNotNull(p);
+        assertEquals(p.getClass(), Relation.class);
+
+        Way w = (Way)hull.getPrimitiveById(30);
+        assertNotNull(w);
+        assertTrue(w.incomplete);
+
+
+        Node n = (Node)hull.getPrimitiveById(21);
+        assertNull(n);
+
+        n = (Node)hull.getPrimitiveById(22);
+        assertNull(n);
+
+        Relation r = (Relation)hull.getPrimitiveById(40);
+        assertNotNull(r);
+        assertTrue(r.incomplete);
+
+        r = (Relation)hull.getPrimitiveById(1);
+        assertNotNull(r);
+        assertEquals(3, r.members.size());
+        RelationMember m = new RelationMember("node-20", hull.getPrimitiveById(20));
+        assertTrue(r.members.contains(m));
+        m = new RelationMember("way-30", hull.getPrimitiveById(30));
+        assertTrue(r.members.contains(m));
+        m = new RelationMember("relation-40", hull.getPrimitiveById(40));
+        assertTrue(r.members.contains(m));
+    }
+
+    @Test
+    public void test_OneRelation_NewMembersNotSelected() {
+        DataSet source = new DataSet();
+        Relation r1 = new Relation();
+        r1.put("name", "r1");
+        Node n20 = new Node(new LatLon(20.0,20.0));
+        n20.put("name", "n20");
+        r1.members.add(new RelationMember("node-20",n20));
+        Way w30 = new Way();
+        w30.put("name", "w30");
+        Node n21;
+        w30.nodes.add(n21 = new Node(new LatLon(21.0,21.0)));
+        n21.put("name","n21");
+        Node n22;
+        w30.nodes.add(n22 = new Node(new LatLon(22.0,22.0)));
+        n22.put("name","n22");
+        r1.members.add(new RelationMember("way-30",w30));
+        Relation r40 = new Relation();
+        r40.put("name", "r40");
+        r1.members.add(new RelationMember("relation-40", r40));
+        source.nodes.add(n20);
+        source.nodes.add(n21);
+        source.nodes.add(n22);
+        source.ways.add(w30);
+        source.relations.add(r1);
+        source.relations.add(r40);
+        source.setSelected(r1);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(1, hull.ways.size());
+        assertEquals(3, hull.nodes.size());
+        assertEquals(2, hull.relations.size());
+
+        OsmPrimitive p = lookupByName(hull.relations, "r1");
+        assertNotNull(p);
+        assertEquals(p.getClass(), Relation.class);
+
+        Way w = (Way)lookupByName(hull.ways, "w30");
+        assertNotNull(w);
+        assertEquals(2, w.nodes.size());
+
+        Node n = (Node)lookupByName(hull.nodes, "n21");
+        assertNotNull(n);
+        assertTrue(w.nodes.contains(n));
+
+        n = (Node)lookupByName(hull.nodes, "n22");
+        assertNotNull(n);
+        assertTrue(w.nodes.contains(n));
+
+        Relation r = (Relation)lookupByName(hull.relations, "r40");
+        assertNotNull(r);
+
+        r = (Relation)lookupByName(hull.relations, "r1");
+        assertNotNull(r);
+        assertEquals(3, r.members.size());
+        RelationMember m = new RelationMember("node-20", lookupByName(hull.nodes, "n20"));
+        assertTrue(r.members.contains(m));
+        m = new RelationMember("way-30", lookupByName(hull.ways, "w30"));
+        assertTrue(r.members.contains(m));
+        m = new RelationMember("relation-40", lookupByName(hull.relations, "r40"));
+        assertTrue(r.members.contains(m));
+    }
+
+    @Test
+    public void test_OneRelation_Existing_Recursive() {
+        DataSet source = new DataSet();
+        Relation r1 = new Relation(1);
+        r1.members.add(new RelationMember("relation-1",r1));
+        source.relations.add(r1);
+        source.setSelected(r1);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(1, hull.relations.size());
+
+        Relation r = (Relation)hull.getPrimitiveById(1);
+        assertNotNull(r);
+        assertEquals(1, r.members.size());
+        assertTrue(r.members.contains(new RelationMember("relation-1",r)));
+    }
+
+    @Test
+    public void test_OneRelation_New_Recursive() {
+        DataSet source = new DataSet();
+        Relation r1 = new Relation();
+        r1.put("name", "r1");
+        r1.members.add(new RelationMember("relation-1",r1));
+        source.relations.add(r1);
+        source.setSelected(r1);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(1, hull.relations.size());
+
+        Relation r = (Relation)lookupByName(hull.relations, "r1");
+        assertNotNull(r);
+        assertEquals(1, r.members.size());
+        assertTrue(r.members.contains(new RelationMember("relation-1",r)));
+    }
+
+    @Test
+    public void test_TwoRelation_Existing_Circular() {
+        DataSet source = new DataSet();
+        Relation r1 = new Relation(1);
+        Relation r2 = new Relation(2);
+        r1.members.add(new RelationMember("relation-2",r2));
+        r2.members.add(new RelationMember("relation-1",r1));
+        source.relations.add(r1);
+        source.relations.add(r2);
+        source.setSelected(r1,r2);
+
+        MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(source);
+        DataSet hull = builder.build();
+        assertNotNull(hull);
+        assertEquals(2, hull.relations.size());
+
+        r1 = (Relation)hull.getPrimitiveById(1);
+        assertNotNull(r1);
+        r2 = (Relation)hull.getPrimitiveById(2);
+        assertNotNull(r2);
+        assertEquals(1, r1.members.size());
+        assertTrue(r1.members.contains(new RelationMember("relation-2",r2)));
+        assertEquals(1, r2.members.size());
+        assertTrue(r2.members.contains(new RelationMember("relation-1",r1)));
+    }
+}
