Index: trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java	(revision 2563)
@@ -2,6 +2,6 @@
 package org.openstreetmap.josm.actions;
 
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 
 import java.awt.event.ActionEvent;
@@ -86,5 +86,5 @@
             for (File f : files) {
                 if (cancelled) return;
-                getProgressMonitor().subTask(tr("Opening file ''{0}'' ...", f.getAbsolutePath()));
+                getProgressMonitor().indeterminateSubTask(tr("Opening file ''{0}'' ...", f.getAbsolutePath()));
                 try {
                     System.out.println("Open file: " + f.getAbsolutePath() + " (" + f.length() + " bytes)");
Index: trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java	(revision 2563)
@@ -38,5 +38,5 @@
      * to relation members) after the first phase of merging
      */
-    private Set<Long> childrenToMerge;
+    private Set<PrimitiveId> objectsWithChildrenToMerge;
     private Set<OsmPrimitive> deletedObjectsToUnlink;
 
@@ -57,5 +57,5 @@
         conflicts = new ConflictCollection();
         mergedMap = new HashMap<Long, Long>();
-        childrenToMerge = new HashSet<Long>();
+        objectsWithChildrenToMerge = new HashSet<PrimitiveId>();
         deletedObjectsToUnlink = new HashSet<OsmPrimitive>();
     }
@@ -75,5 +75,5 @@
      * @param source  the other primitive
      */
-    protected <P extends OsmPrimitive> void mergePrimitive(P source) {
+    protected void mergePrimitive(OsmPrimitive source) {
         if (!source.isNew() ) {
             // try to merge onto a matching primitive with the same
@@ -112,5 +112,5 @@
                         target.setTimestamp(source.getTimestamp());
                         target.setModified(source.isModified());
-                        childrenToMerge.add(source.getUniqueId());
+                        objectsWithChildrenToMerge.add(source.getPrimitiveId());
                     }
                     return;
@@ -131,5 +131,5 @@
         targetDataSet.addPrimitive(target);
         mergedMap.put(source.getUniqueId(), target.getUniqueId());
-        childrenToMerge.add(source.getUniqueId());
+        objectsWithChildrenToMerge.add(source.getPrimitiveId());
     }
 
@@ -154,4 +154,33 @@
 
     /**
+     * A way in the target dataset might be incomplete because at least of of its nodes is incomplete.
+     * The nodes might have become complete because a complete node was merged onto into in the
+     * merge operation.
+     * 
+     * This method loops over all parent ways of such nodes and turns them into complete ways
+     * if necessary.
+     * 
+     * @param other
+     */
+    protected void fixIncompleteParentWays(Node other) {
+        Node myNode = (Node)getMergeTarget(other);
+        if (myNode == null)
+            throw new RuntimeException(tr("Missing merge target for node with id {0}", other.getUniqueId()));
+        if (myNode.incomplete || myNode.isDeleted() || !myNode.isVisible()) return;
+        wayloop: for (Way w: OsmPrimitive.getFilteredList(myNode.getReferrers(), Way.class)) {
+            if (w.isDeleted() || ! w.isVisible() || ! w.incomplete) {
+                continue;
+            }
+            for (Node n: w.getNodes()) {
+                if (n.incomplete) {
+                    continue wayloop;
+                }
+            }
+            // all nodes are complete - set the way complete too
+            w.incomplete = false;
+        }
+    }
+
+    /**
      * Postprocess the dataset and fix all merged references to point to the actual
      * data.
@@ -159,5 +188,5 @@
     public void fixReferences() {
         for (Way w : sourceDataSet.getWays()) {
-            if (!conflicts.hasConflictForTheir(w) && childrenToMerge.contains(w.getUniqueId())) {
+            if (!conflicts.hasConflictForTheir(w) && objectsWithChildrenToMerge.contains(w.getPrimitiveId())) {
                 mergeNodeList(w);
                 fixIncomplete(w);
@@ -165,5 +194,5 @@
         }
         for (Relation r : sourceDataSet.getRelations()) {
-            if (!conflicts.hasConflictForTheir(r) && childrenToMerge.contains(r.getUniqueId())) {
+            if (!conflicts.hasConflictForTheir(r) && objectsWithChildrenToMerge.contains(r.getPrimitiveId())) {
                 mergeRelationMembers(r);
             }
@@ -175,4 +204,17 @@
             targetDataSet.unlinkReferencesToPrimitive(target);
         }
+        // objectsWithChildrenToMerge also includes complete nodes which have
+        // been merged into their incomplete equivalents.
+        //
+        for (PrimitiveId id: objectsWithChildrenToMerge) {
+            if (!id.getType().equals(OsmPrimitiveType.NODE)) {
+                continue;
+            }
+            Node n = (Node)sourceDataSet.getPrimitiveById(id);
+            if (!conflicts.hasConflictForTheir(n)) {
+                fixIncompleteParentWays(n);
+            }
+        }
+
     }
 
@@ -269,5 +311,5 @@
             //
             target.mergeFrom(source);
-            childrenToMerge.add(source.getUniqueId());
+            objectsWithChildrenToMerge.add(source.getPrimitiveId());
         } else if (!target.incomplete && source.incomplete) {
             // target is complete and source is incomplete
@@ -282,5 +324,6 @@
             // otherwise too many conflicts when refreshing from the server
         } else if (target.isDeleted() != source.isDeleted()) {
-            // differences in deleted state have to be resolved manually
+            // differences in deleted state have to be resolved manually. This can
+            // happen if one layer is merged onto another layer
             //
             conflicts.add(target,source);
@@ -288,5 +331,5 @@
             // target not modified. We can assume that source is the most recent version.
             // clone it into target. But check first, whether source is deleted. if so,
-            // make sure that target is not referenced anymore in myDataSet.
+            // make sure that target is not referenced any more in myDataSet.
             //
             if (source.isDeleted()) {
@@ -294,5 +337,5 @@
             }
             target.mergeFrom(source);
-            childrenToMerge.add(source.getUniqueId());
+            objectsWithChildrenToMerge.add(source.getPrimitiveId());
         } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) {
             // both not modified. Keep mine
@@ -302,5 +345,5 @@
             //
             target.mergeFrom(source);
-            childrenToMerge.add(source.getUniqueId());
+            objectsWithChildrenToMerge.add(source.getPrimitiveId());
         } else if (target.isModified() && ! source.isModified() && target.getVersion() == source.getVersion()) {
             // target is same as source but target is modified
@@ -318,5 +361,5 @@
             target.mergeFrom(source);
             target.setModified(true);
-            childrenToMerge.add(source.getUniqueId());
+            objectsWithChildrenToMerge.add(source.getPrimitiveId());
         }
         return true;
Index: trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java	(revision 2563)
@@ -7,4 +7,5 @@
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -199,3 +200,41 @@
         return tr("Changeset {0}",changeset.getId());
     }
+
+    /**
+     * Builds a default tooltip text for the primitive <code>primitive</code>.
+     * 
+     * @param primitive the primitmive
+     * @return the tooltip text
+     */
+    public String buildDefaultToolTip(OsmPrimitive primitive) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<html>");
+        sb.append("<strong>id</strong>=")
+        .append(primitive.getId())
+        .append("<br>");
+        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
+        Collections.sort(keyList);
+        for (int i = 0; i < keyList.size(); i++) {
+            if (i > 0) {
+                sb.append("<br>");
+            }
+            String key = keyList.get(i);
+            sb.append("<strong>")
+            .append(key)
+            .append("</strong>")
+            .append("=");
+            String value = primitive.get(key);
+            while(value.length() != 0) {
+                sb.append(value.substring(0,Math.min(50, value.length())));
+                if (value.length() > 50) {
+                    sb.append("<br>");
+                    value = value.substring(50);
+                } else {
+                    value = "";
+                }
+            }
+        }
+        sb.append("</html>");
+        return sb.toString();
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java	(revision 2563)
@@ -43,5 +43,5 @@
 import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
 import org.openstreetmap.josm.gui.SideButton;
-import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
+import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationTask;
 import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
 import org.openstreetmap.josm.gui.layer.DataChangeListener;
@@ -248,5 +248,5 @@
                 displaylist.setSelectedIndex(index);
             }
-            popupMenu.show(RelationListDialog.this, p.x, p.y-3);
+            popupMenu.show(displaylist, p.x, p.y-3);
         }
         @Override public void mousePressed(MouseEvent e) {
@@ -500,7 +500,8 @@
             if (relations.isEmpty())
                 return;
-            Main.worker.submit(new GenericRelationEditor.DownloadTask(
+            Main.worker.submit(new DownloadRelationTask(
                     model.getSelectedNonNewRelations(),
-                    Main.map.mapView.getEditLayer(), null));
+                    Main.map.mapView.getEditLayer())
+            );
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java	(revision 2563)
@@ -169,8 +169,4 @@
         BasicArrowButton arrowButton = new BasicArrowButton(SwingConstants.SOUTH, null, null, Color.BLACK, null);
         arrowButton.setBorder(BorderFactory.createEmptyBorder());
-        //        selectionHistoryMenuButton.setContentAreaFilled(false);
-        //        selectionHistoryMenuButton.setOpaque(false);
-        //        selectionHistoryMenuButton.setBorderPainted(false);
-        //        selectionHistoryMenuButton.setBackground(null);
         parentButton.setLayout(new BorderLayout());
         parentButton.add(arrowButton, BorderLayout.EAST);
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java	(revision 2563)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java	(revision 2563)
@@ -0,0 +1,120 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.Dialog;
+import java.io.IOException;
+import java.util.Collection;
+
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+
+/**
+ * The asynchronous task for downloading relation members.
+ *
+ */
+public class DownloadRelationMemberTask extends PleaseWaitRunnable {
+    private boolean cancelled;
+    private Exception lastException;
+    private Relation parent;
+    private Collection<OsmPrimitive> children;
+    private OsmDataLayer curLayer;
+    private MemberTableModel memberTableModel;
+    private MultiFetchServerObjectReader objectReader;
+
+    public DownloadRelationMemberTask(Relation parent, Collection<OsmPrimitive> children, OsmDataLayer curLayer, MemberTableModel memberTableModel, Dialog dialog) {
+        super(tr("Download relation members"), new PleaseWaitProgressMonitor(dialog), false /* don't ignore exception */);
+        this.parent = parent;
+        this.children = children;
+        this.curLayer = curLayer;
+        this.memberTableModel = memberTableModel;
+    }
+
+    public DownloadRelationMemberTask(Relation parent, Collection<OsmPrimitive> children, OsmDataLayer curLayer, MemberTableModel memberTableModel) {
+        super(tr("Download relation members"), false /* don't ignore exception */);
+        this.parent = parent;
+        this.children = children;
+        this.curLayer = curLayer;
+        this.memberTableModel = memberTableModel;
+    }
+
+    @Override
+    protected void cancel() {
+        cancelled = true;
+        synchronized(this) {
+            if (objectReader != null) {
+                objectReader.cancel();
+            }
+        }
+    }
+
+    @Override
+    protected void finish() {
+        Main.map.repaint();
+        if (cancelled)
+            return;
+        if (lastException != null) {
+            ExceptionDialogUtil.explainException(lastException);
+        }
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        try {
+            synchronized (this) {
+                if (cancelled) return;
+                objectReader = new MultiFetchServerObjectReader();
+            }
+            objectReader.append(children);
+            progressMonitor.indeterminateSubTask(
+                    trn("Downloading {0} incomplete child of relation ''{1}''",
+                            "Downloading {0} incomplete children of relation ''{1}''",
+                            children.size(),
+                            children.size(),
+                            parent.getDisplayName(DefaultNameFormatter.getInstance())
+                    )
+            );
+            final DataSet dataSet = objectReader.parseOsm(progressMonitor
+                    .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+            if (dataSet == null)
+                return;
+            synchronized (this) {
+                if (cancelled) return;
+                objectReader = null;
+            }
+
+            SwingUtilities.invokeLater(
+                    new Runnable() {
+                        public void run() {
+                            curLayer.mergeFrom(dataSet);
+                            curLayer.fireDataChange();
+                            curLayer.onPostDownloadFromServer();
+                        }
+                    }
+            );
+
+        } catch (Exception e) {
+            if (cancelled) {
+                System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
+                        .toString()));
+                return;
+            }
+            lastException = e;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java	(revision 2563)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java	(revision 2563)
@@ -0,0 +1,115 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.DataSetMerger;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.OsmServerObjectReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+
+/**
+ * The asynchronous task for fully downloading a collection of relations. Does a full download
+ * for each relations and merges the relation into an {@see OsmDataLayer}
+ *
+ */
+public class DownloadRelationTask extends PleaseWaitRunnable {
+    private boolean cancelled;
+    private Exception lastException;
+    private Collection<Relation> relations;
+    private OsmDataLayer layer;
+    private OsmServerObjectReader objectReader;
+
+    /**
+     * Creates the download task
+     * 
+     * @param relations a collection of relations. Must not be null.
+     * @param layer the layer which data is to be merged into
+     * @throws IllegalArgumentException thrown if relations is null
+     * @throws IllegalArgumentException thrown if layer is null
+     */
+    public DownloadRelationTask(Collection<Relation> relations, OsmDataLayer layer) throws IllegalArgumentException{
+        super(tr("Download relations"), false /* don't ignore exception */);
+        if (relations == null)
+            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "relations"));
+        if (layer == null)
+            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "layer"));
+        this.relations = relations;
+        this.layer = layer;
+    }
+
+    @Override
+    protected void cancel() {
+        cancelled = true;
+        synchronized(this) {
+            if (objectReader != null) {
+                objectReader.cancel();
+            }
+        }
+    }
+
+    @Override
+    protected void finish() {
+        if (cancelled)
+            return;
+        if (lastException != null) {
+            ExceptionDialogUtil.explainException(lastException);
+        }
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        try {
+            final DataSet allDownloads = new DataSet();
+            int i=0;
+            for (Relation relation: relations) {
+                progressMonitor.subTask(tr("({0}/{1}: Downloading relation ''{2}''...", i,relations.size(),relation.getDisplayName(DefaultNameFormatter.getInstance())));
+                synchronized (this) {
+                    if (cancelled) return;
+                    objectReader = new OsmServerObjectReader(relation.getPrimitiveId(), true /* full download */);
+                }
+                DataSet dataSet = objectReader.parseOsm(progressMonitor
+                        .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+                if (dataSet == null)
+                    return;
+                synchronized (this) {
+                    if (cancelled) return;
+                    objectReader = null;
+                }
+                DataSetMerger merger = new DataSetMerger(allDownloads, dataSet);
+                merger.merge();
+            }
+
+            SwingUtilities.invokeAndWait(
+                    new Runnable() {
+                        public void run() {
+                            layer.mergeFrom(allDownloads);
+                            layer.fireDataChange();
+                            layer.onPostDownloadFromServer();
+                            Main.map.repaint();
+                        }
+                    }
+            );
+        } catch (Exception e) {
+            if (cancelled) {
+                System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
+                        .toString()));
+                return;
+            }
+            lastException = e;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 2563)
@@ -5,10 +5,8 @@
 
 import java.awt.BorderLayout;
-import java.awt.Dialog;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
-import java.awt.Insets;
 import java.awt.event.ActionEvent;
 import java.awt.event.FocusAdapter;
@@ -19,5 +17,6 @@
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
-import java.io.IOException;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -30,5 +29,4 @@
 import javax.swing.AbstractAction;
 import javax.swing.BorderFactory;
-import javax.swing.JButton;
 import javax.swing.JComponent;
 import javax.swing.JLabel;
@@ -38,7 +36,6 @@
 import javax.swing.JSplitPane;
 import javax.swing.JTabbedPane;
-import javax.swing.JTable;
+import javax.swing.JToolBar;
 import javax.swing.KeyStroke;
-import javax.swing.SwingUtilities;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
@@ -57,12 +54,9 @@
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
 import org.openstreetmap.josm.gui.DefaultNameFormatter;
-import org.openstreetmap.josm.gui.ExceptionDialogUtil;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
@@ -70,16 +64,10 @@
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.gui.tagging.AutoCompletingTextField;
 import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionCache;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
-import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
-import org.openstreetmap.josm.io.OsmServerObjectReader;
-import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
-import org.xml.sax.SAXException;
 
 /**
@@ -125,5 +113,9 @@
         //
         memberTableModel = new MemberTableModel(getLayer());
+        DataSet.selListeners.add(memberTableModel);
+        getLayer().data.addDataSetListener(memberTableModel);
+        getLayer().listenerDataChanged.add(memberTableModel);
         selectionTableModel = new SelectionTableModel(getLayer());
+        DataSet.selListeners.add(selectionTableModel);
         referrerModel = new ReferringRelationsBrowserModel(relation);
 
@@ -173,4 +165,5 @@
         );
 
+        getContentPane().add(buildToolBar(), BorderLayout.NORTH);
         getContentPane().add(tabbedPane, BorderLayout.CENTER);
         getContentPane().add(buildOkCancelButtonPanel(), BorderLayout.SOUTH);
@@ -189,4 +182,20 @@
         memberTableModel.setSelectedMembers(selectedMembers);
         HelpUtil.setHelpContext(getRootPane(),ht("/Dialog/RelationEditor"));
+    }
+
+    /**
+     * Creates the toolbar
+     * 
+     * @return the toolbar
+     */
+    protected JToolBar buildToolBar() {
+        JToolBar tb  = new JToolBar();
+        tb.setFloatable(false);
+        tb.add(new ApplyAction());
+        tb.add(new DuplicateRelationAction());
+        DeleteCurrentRelationAction deleteAction = new DeleteCurrentRelationAction();
+        addPropertyChangeListener(deleteAction);
+        tb.add(deleteAction);
+        return tb;
     }
 
@@ -280,4 +289,47 @@
         pnl.add(scrollPane, gc);
 
+        // --- role editing
+        JPanel p3 = new JPanel(new FlowLayout(FlowLayout.LEFT));
+        p3.add(new JLabel(tr("Apply Role:")));
+        tfRole = new AutoCompletingTextField(10);
+        tfRole.setToolTipText(tr("Enter a role and apply it to the selected relation members"));
+        tfRole.addFocusListener(new FocusAdapter() {
+            @Override
+            public void focusGained(FocusEvent e) {
+                tfRole.selectAll();
+            }
+        });
+        tfRole.setAutoCompletionList(new AutoCompletionList());
+        tfRole.addFocusListener(
+                new FocusAdapter() {
+                    @Override
+                    public void focusGained(FocusEvent e) {
+                        AutoCompletionList list = tfRole.getAutoCompletionList();
+                        AutoCompletionCache.getCacheForLayer(Main.main.getEditLayer()).populateWithMemberRoles(list);
+                    }
+                }
+        );
+        p3.add(tfRole);
+        SetRoleAction setRoleAction = new SetRoleAction();
+        memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction);
+        tfRole.getDocument().addDocumentListener(setRoleAction);
+        tfRole.addActionListener(setRoleAction);
+        memberTableModel.getSelectionModel().addListSelectionListener(
+                new ListSelectionListener() {
+                    public void valueChanged(ListSelectionEvent e) {
+                        tfRole.setEnabled(memberTable.getSelectedRowCount() > 0);
+                    }
+                }
+        );
+        tfRole.setEnabled(memberTable.getSelectedRowCount() > 0);
+
+        gc.gridx = 1;
+        gc.gridy = 2;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 1.0;
+        gc.weighty = 0.0;
+        pnl.add(p3, gc);
+
         JPanel pnl2 = new JPanel();
         pnl2.setLayout(new GridBagLayout());
@@ -326,5 +378,4 @@
         pnl3.setLayout(new BorderLayout());
         pnl3.add(splitPane, BorderLayout.CENTER);
-        pnl3.add(buildButtonPanel(), BorderLayout.SOUTH);
         return pnl3;
     }
@@ -338,6 +389,6 @@
         JPanel pnl = new JPanel();
         pnl.setLayout(new BorderLayout());
-        JTable tbl = new JTable(selectionTableModel, new SelectionTableColumnModel(memberTableModel));
-        tbl.setEnabled(false);
+        SelectionTable tbl = new SelectionTable(selectionTableModel, new SelectionTableColumnModel(memberTableModel));
+        tbl.setMemberTableModel(memberTableModel);
         JScrollPane pane = new JScrollPane(tbl);
         pnl.add(pane, BorderLayout.CENTER);
@@ -371,61 +422,50 @@
      * @return
      */
-    protected JPanel buildLeftButtonPanel() {
-        JPanel pnl = new JPanel();
-        pnl.setLayout(new GridBagLayout());
-
-        GridBagConstraints gc = new GridBagConstraints();
-        gc.gridx = 0;
-        gc.gridy = 0;
-        gc.gridheight = 1;
-        gc.gridwidth = 1;
-        gc.insets = new Insets(0, 5, 0, 5);
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.anchor = GridBagConstraints.CENTER;
-        gc.weightx = 0.0;
-        gc.weighty = 0.0;
-
-        // -----
-        gc.gridy = 0;
+    protected JToolBar buildLeftButtonPanel() {
+        JToolBar tb = new JToolBar();
+        tb.setOrientation(JToolBar.VERTICAL);
+        tb.setFloatable(false);
+
+        // -- move up action
         MoveUpAction moveUpAction = new MoveUpAction();
         memberTableModel.getSelectionModel().addListSelectionListener(moveUpAction);
-        pnl.add(new JButton(moveUpAction), gc);
-
-        // -----
-        gc.gridy = 1;
+        tb.add(moveUpAction);
+
+        // -- move down action
         MoveDownAction moveDownAction = new MoveDownAction();
         memberTableModel.getSelectionModel().addListSelectionListener(moveDownAction);
-        pnl.add(new JButton(moveDownAction), gc);
+        tb.add(moveDownAction);
+
+        tb.addSeparator();
 
         // -- edit action
-        gc.gridy = 2;
         EditAction editAction = new EditAction();
         memberTableModel.getSelectionModel().addListSelectionListener(editAction);
-        pnl.add(new JButton(editAction),gc);
-
-        // ------
-        gc.gridy = 3;
+        tb.add(editAction);
+
+        // -- delete action
         RemoveAction removeSelectedAction = new RemoveAction();
         memberTable.getSelectionModel().addListSelectionListener(removeSelectedAction);
-        pnl.add(new JButton(removeSelectedAction), gc);
-
-        // ------
-        gc.gridy = 4;
-        SelectPrimitivesForSelectedMembersAction selectAction = new SelectPrimitivesForSelectedMembersAction();
-        memberTable.getSelectionModel().addListSelectionListener(selectAction);
-        pnl.add(new JButton(selectAction), gc);
-
-        // ------
-        gc.gridy = 5;
+        tb.add(removeSelectedAction);
+
+        tb.addSeparator();
+        // -- sort action
         SortAction sortAction = new SortAction();
-        pnl.add(new JButton(sortAction), gc);
-
-        // ------
-        // just grab the remaining space
-        gc.gridy = 6;
-        gc.weighty = 1.0;
-        gc.fill = GridBagConstraints.BOTH;
-        pnl.add(new JPanel(), gc);
-        return pnl;
+        tb.add(sortAction);
+
+        tb.addSeparator();
+
+        // -- download action
+        DownloadIncompleteMembersAction downloadIncompleteMembersAction = new DownloadIncompleteMembersAction();
+        memberTable.getModel().addTableModelListener(downloadIncompleteMembersAction);
+        tb.add(downloadIncompleteMembersAction);
+
+        // -- download selected action
+        DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction = new DownloadSelectedIncompleteMembersAction();
+        memberTable.getModel().addTableModelListener(downloadSelectedIncompleteMembersAction);
+        memberTable.getSelectionModel().addListSelectionListener(downloadSelectedIncompleteMembersAction);
+        tb.add(downloadSelectedIncompleteMembersAction);
+
+        return tb;
     }
 
@@ -435,112 +475,52 @@
      * @return
      */
-    protected JPanel buildSelectionControlButtonPanel() {
-        JPanel pnl = new JPanel();
-        pnl.setLayout(new GridBagLayout());
-
-        GridBagConstraints gc = new GridBagConstraints();
-        gc.gridx = 0;
-        gc.gridy = 0;
-        gc.gridheight = 1;
-        gc.gridwidth = 1;
-        gc.insets = new Insets(0, 5, 0, 5);
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.anchor = GridBagConstraints.CENTER;
-        gc.weightx = 0.0;
-        gc.weighty = 0.0;
+    protected JToolBar buildSelectionControlButtonPanel() {
+        JToolBar tb = new JToolBar(JToolBar.VERTICAL);
+        tb.setFloatable(false);
+
+
+        // -- add at end action
         AddSelectedAtEndAction addSelectedAtEndAction = new AddSelectedAtEndAction();
         selectionTableModel.addTableModelListener(addSelectedAtEndAction);
-        pnl.add(new JButton(addSelectedAtEndAction), gc);
-
-        // -----
-        gc.gridy = 1;
+        tb.add(addSelectedAtEndAction);
+
+        // -- select members action
         SelectedMembersForSelectionAction selectMembersForSelectionAction = new SelectedMembersForSelectionAction();
         selectionTableModel.addTableModelListener(selectMembersForSelectionAction);
         memberTableModel.addTableModelListener(selectMembersForSelectionAction);
-        pnl.add(new JButton(selectMembersForSelectionAction), gc);
-
-        // -----
-        gc.gridy = 2;
+        tb.add(selectMembersForSelectionAction);
+
+        tb.addSeparator();
+
+        // -- remove selected action
         RemoveSelectedAction removeSelectedAction = new RemoveSelectedAction();
         selectionTableModel.addTableModelListener(removeSelectedAction);
-        pnl.add(new JButton(removeSelectedAction), gc);
-
-        // ------
-        // just grab the remaining space
-        gc.gridy = 3;
-        gc.weighty = 1.0;
-        gc.fill = GridBagConstraints.BOTH;
-        pnl.add(new JPanel(), gc);
-
-        // -----
-        gc.gridy = 4;
-        gc.weighty = 0.0;
+        tb.add(removeSelectedAction);
+
+        // -- select action
+        SelectPrimitivesForSelectedMembersAction selectAction = new SelectPrimitivesForSelectedMembersAction();
+        memberTable.getSelectionModel().addListSelectionListener(selectAction);
+        tb.add(selectAction);
+
+        tb.addSeparator();
+
+        // -- add at start action
         AddSelectedAtStartAction addSelectionAction = new AddSelectedAtStartAction();
         selectionTableModel.addTableModelListener(addSelectionAction);
-        pnl.add(new JButton(addSelectionAction), gc);
-
-        // -----
-        gc.gridy = 5;
+        tb.add(addSelectionAction);
+
+        // -- add before selected action
         AddSelectedBeforeSelection addSelectedBeforeSelectionAction = new AddSelectedBeforeSelection();
         selectionTableModel.addTableModelListener(addSelectedBeforeSelectionAction);
         memberTableModel.getSelectionModel().addListSelectionListener(addSelectedBeforeSelectionAction);
-        pnl.add(new JButton(addSelectedBeforeSelectionAction), gc);
-
-        // -----
-        gc.gridy = 6;
+        tb.add(addSelectedBeforeSelectionAction);
+
+        // -- add after selected action
         AddSelectedAfterSelection addSelectedAfterSelectionAction = new AddSelectedAfterSelection();
         selectionTableModel.addTableModelListener(addSelectedAfterSelectionAction);
         memberTableModel.getSelectionModel().addListSelectionListener(addSelectedAfterSelectionAction);
-        pnl.add(new JButton(addSelectedAfterSelectionAction), gc);
-
-        return pnl;
-    }
-
-    /**
-     * Creates the buttons for the basic editing layout
-     * @return {@see JPanel} with basic buttons
-     */
-    protected JPanel buildButtonPanel() {
-        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
-
-        // --- download members
-        buttonPanel.add(new SideButton(new DownlaodAction()));
-
-        // --- role editing
-        buttonPanel.add(new JLabel(tr("Role:")));
-        tfRole = new AutoCompletingTextField(10);
-        tfRole.addFocusListener(new FocusAdapter() {
-            @Override
-            public void focusGained(FocusEvent e) {
-                tfRole.selectAll();
-            }
-        });
-        tfRole.setAutoCompletionList(new AutoCompletionList());
-        tfRole.addFocusListener(
-                new FocusAdapter() {
-                    @Override
-                    public void focusGained(FocusEvent e) {
-                        AutoCompletionList list = tfRole.getAutoCompletionList();
-                        AutoCompletionCache.getCacheForLayer(Main.main.getEditLayer()).populateWithMemberRoles(list);
-                    }
-                }
-        );
-
-        buttonPanel.add(tfRole);
-        SetRoleAction setRoleAction = new SetRoleAction();
-        memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction);
-        buttonPanel.add(new SideButton(setRoleAction));
-        tfRole.getDocument().addDocumentListener(setRoleAction);
-        tfRole.addActionListener(setRoleAction);
-
-        // --- copy relation action
-        buttonPanel.add(new SideButton(new DuplicateRelationAction()));
-
-        // --- apply relation action
-        buttonPanel.add(new SideButton(new ApplyAction()));
-
-        // --- delete relation action
-        buttonPanel.add(new SideButton(new DeleteCurrentRelationAction()));
-        return buttonPanel;
+        tb.add(addSelectedAfterSelectionAction);
+
+        return tb;
     }
 
@@ -548,12 +528,5 @@
     protected Dimension findMaxDialogSize() {
         // FIXME: Make it remember dialog size
-        return new Dimension(700, 500);
-    }
-
-    @Override
-    public void dispose() {
-        selectionTableModel.unregister();
-        DataSet.selListeners.remove(memberTableModel);
-        super.dispose();
+        return new Dimension(700, 650);
     }
 
@@ -561,8 +534,15 @@
     public void setVisible(boolean visible) {
         if (visible) {
-            tagEditorPanel.initAutoCompletion(Main.main.getEditLayer());
+            tagEditorPanel.initAutoCompletion(getLayer());
         }
         super.setVisible(visible);
         if (!visible) {
+            // make sure all registered listeners are unregistered
+            //
+            selectionTableModel.unregister();
+            DataSet.selListeners.remove(memberTableModel);
+            DataSet.selListeners.remove(selectionTableModel);
+            getLayer().data.removeDataSetListener(memberTableModel);
+            getLayer().listenerDataChanged.remove(memberTableModel);
             dispose();
         }
@@ -888,16 +868,24 @@
     }
 
-    class SortAction extends AbstractAction {
+    class SortAction extends AbstractAction implements ListSelectionListener {
         public SortAction() {
             putValue(SHORT_DESCRIPTION, tr("Sort the relation members"));
             putValue(SMALL_ICON, ImageProvider.get("dialogs", "sort"));
-            // putValue(NAME, tr("Sort"));
+            putValue(NAME, tr("Sort"));
             Shortcut.registerShortcut("relationeditor:sort", tr("Relation Editor: Sort"), KeyEvent.VK_T,
                     Shortcut.GROUP_MNEMONIC);
-            //setEnabled(false);
+            updateEnabledState();
         }
 
         public void actionPerformed(ActionEvent e) {
             memberTableModel.sort();
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(memberTable.getSelectedRowCount() > 0);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            updateEnabledState();
         }
     }
@@ -960,5 +948,5 @@
     }
 
-    class DeleteCurrentRelationAction extends AbstractAction {
+    class DeleteCurrentRelationAction extends AbstractAction implements PropertyChangeListener{
         public DeleteCurrentRelationAction() {
             putValue(SHORT_DESCRIPTION, tr("Delete the currently edited relation"));
@@ -983,5 +971,11 @@
 
         protected void updateEnabledState() {
-            setEnabled(getRelation() != null);
+            setEnabled(getRelationSnapshot() != null);
+        }
+
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (evt.getPropertyName().equals(RELATION_SNAPSHOT_PROP)) {
+                updateEnabledState();
+            }
         }
     }
@@ -1187,8 +1181,8 @@
     }
 
-    class DownlaodAction extends AbstractAction {
-        public DownlaodAction() {
-            putValue(SHORT_DESCRIPTION, tr("Download all incomplete ways and nodes in relation"));
-            putValue(SMALL_ICON, ImageProvider.get("dialogs", "downloadincomplete"));
+    class DownloadIncompleteMembersAction extends AbstractAction implements TableModelListener {
+        public DownloadIncompleteMembersAction() {
+            putValue(SHORT_DESCRIPTION, tr("Download all incomplete members"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincomplete"));
             putValue(NAME, tr("Download Members"));
             Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
@@ -1200,6 +1194,7 @@
             if (!isEnabled())
                 return;
-            Main.worker.submit(new DownloadTask(
-                    Collections.singletonList(getRelation()),
+            Main.worker.submit(new DownloadRelationMemberTask(
+                    getRelation(),
+                    memberTableModel.getIncompleteMemberPrimitives(),
                     getLayer(),
                     memberTableModel,
@@ -1209,5 +1204,52 @@
 
         protected void updateEnabledState() {
-            setEnabled(getRelation() != null && !getRelation().isNew());
+            setEnabled(
+                    getRelation() != null
+                    && !getRelation().isNew()
+                    && memberTableModel.hasIncompleteMembers()
+            );
+        }
+
+        public void tableChanged(TableModelEvent e) {
+            updateEnabledState();
+        }
+    }
+
+    class DownloadSelectedIncompleteMembersAction extends AbstractAction implements ListSelectionListener, TableModelListener{
+        public DownloadSelectedIncompleteMembersAction() {
+            putValue(SHORT_DESCRIPTION, tr("Download selected incomplete members"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincompleteselected"));
+            putValue(NAME, tr("Download Members"));
+            Shortcut.registerShortcut("relationeditor:downloadincomplete", tr("Relation Editor: Download Members"),
+                    KeyEvent.VK_K, Shortcut.GROUP_MNEMONIC);
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            if (!isEnabled())
+                return;
+            Main.worker.submit(new DownloadRelationMemberTask(
+                    getRelation(),
+                    memberTableModel.getSelectedIncompleteMemberPrimitives(),
+                    getLayer(),
+                    memberTableModel,
+                    GenericRelationEditor.this)
+            );
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(
+                    getRelation() != null
+                    && !getRelation().isNew()
+                    && memberTableModel.hasIncompleteSelectedMembers()
+            );
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            updateEnabledState();
+        }
+
+        public void tableChanged(TableModelEvent e) {
+            updateEnabledState();
         }
     }
@@ -1366,119 +1408,3 @@
         }
     }
-
-    /**
-     * The asynchronous task for downloading relation members.
-     *
-     */
-    public static class DownloadTask extends PleaseWaitRunnable {
-        private boolean cancelled;
-        private Exception lastException;
-        private List<Relation> relations;
-        private OsmDataLayer curLayer;
-        private MemberTableModel memberTableModel;
-        private OsmServerObjectReader objectReader;
-        private OsmServerBackreferenceReader parentReader;
-
-        public DownloadTask(List<Relation> relations, OsmDataLayer curLayer, MemberTableModel memberTableModel, Dialog parent) {
-            super(tr("Download relation members"), new PleaseWaitProgressMonitor(parent), false /* don't ignore exception */);
-            this.relations = relations;
-            this.curLayer = curLayer;
-            this.memberTableModel = memberTableModel;
-        }
-
-        public DownloadTask(List<Relation> relations, OsmDataLayer curLayer, MemberTableModel memberTableModel) {
-            super(tr("Download relation members"), new PleaseWaitProgressMonitor(), false /* don't ignore exception */);
-            this.relations = relations;
-            this.curLayer = curLayer;
-            this.memberTableModel = memberTableModel;
-        }
-
-        @Override
-        protected void cancel() {
-            cancelled = true;
-            synchronized(this) {
-                if (objectReader != null) {
-                    objectReader.cancel();
-                } else if (parentReader != null) {
-                    parentReader.cancel();
-                }
-            }
-        }
-
-        @Override
-        protected void finish() {
-            Main.map.repaint();
-            if (cancelled)
-                return;
-            if (memberTableModel != null) {
-                memberTableModel.fireTableDataChanged();
-            }
-            if (lastException != null) {
-                ExceptionDialogUtil.explainException(lastException);
-            }
-        }
-
-        @Override
-        protected void realRun() throws SAXException, IOException, OsmTransferException {
-            try {
-                for (Relation relation : relations) {
-                    // download the relation
-                    //
-                    progressMonitor.indeterminateSubTask(tr("Downloading relation ''{0}''", relation.getDisplayName(DefaultNameFormatter.getInstance())));
-                    synchronized(this) {
-                        if (cancelled) return;
-                        objectReader = new OsmServerObjectReader(relation.getId(), OsmPrimitiveType.RELATION, true /* full download */);
-                    }
-                    final DataSet dataSet = objectReader.parseOsm(progressMonitor
-                            .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
-                    if (dataSet == null)
-                        return;
-                    synchronized (this) {
-                        if (cancelled) return;
-                        objectReader = null;
-                    }
-
-                    // download referring objects of the downloaded member objects
-                    //
-                    // asked for in #3999, but uncommented for the time being. Could be used
-                    // later, perhaps if user explicity requests so (for instance by checking
-                    // a checkbox)
-                    //                    for (OsmPrimitive p: relation.getMemberPrimitives()) {
-                    //                        synchronized(this) {
-                    //                            if (cancelled) return;
-                    //                            parentReader = new OsmServerBackreferenceReader(p);
-                    //                        }
-                    //                        DataSet parents = parentReader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
-                    //                        synchronized(this) {
-                    //                            if (cancelled) return;
-                    //                            parentReader = null;
-                    //                        }
-                    //                        DataSetMerger merger = new DataSetMerger(dataSet, parents);
-                    //                        merger.merge();
-                    //                    }
-                    //                    if (cancelled) return;
-
-                    // has to run on the EDT because mergeFrom may trigger events
-                    // which update the UI
-                    //
-                    SwingUtilities.invokeAndWait(
-                            new Runnable() {
-                                public void run() {
-                                    curLayer.mergeFrom(dataSet);
-                                    curLayer.fireDataChange();
-                                    curLayer.onPostDownloadFromServer();
-                                }
-                            }
-                    );
-                }
-            } catch (Exception e) {
-                if (cancelled) {
-                    System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
-                            .toString()));
-                    return;
-                }
-                lastException = e;
-            }
-        }
-    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java	(revision 2563)
@@ -18,4 +18,5 @@
 import javax.swing.KeyStroke;
 import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
@@ -71,4 +72,5 @@
 
         addMouseListener(new PopupListener());
+        addMouseListener(new DblClickHandler());
     }
 
@@ -241,3 +243,32 @@
         return (MemberTableModel) getModel();
     }
+
+    class DblClickHandler extends MouseAdapter {
+        protected void setSelection(MouseEvent e) {
+            int row = rowAtPoint(e.getPoint());
+            if (row < 0) return;
+            OsmPrimitive primitive = getMemberTableModel().getReferredPrimitive(row);
+            getMemberTableModel().getLayer().data.setSelected(primitive.getPrimitiveId());
+        }
+
+        protected void addSelection(MouseEvent e) {
+            int row = rowAtPoint(e.getPoint());
+            if (row < 0) return;
+            OsmPrimitive primitive = getMemberTableModel().getReferredPrimitive(row);
+            getMemberTableModel().getSelectionModel().addSelectionInterval(row, row);
+            getMemberTableModel().getLayer().data.addSelected(primitive.getPrimitiveId());
+
+        }
+
+        @Override
+        public void mouseClicked(MouseEvent e) {
+            if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() > 1) {
+                if (e.isControlDown()) {
+                    addSelection(e);
+                } else {
+                    setSelection(e);
+                }
+            }
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableCellRenderer.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableCellRenderer.java	(revision 2563)
@@ -4,9 +4,8 @@
 import java.awt.Color;
 import java.awt.Component;
-import java.util.ArrayList;
-import java.util.Collections;
 
 import javax.swing.JLabel;
 import javax.swing.JTable;
+import javax.swing.UIManager;
 import javax.swing.table.TableCellRenderer;
 
@@ -18,9 +17,9 @@
  */
 public abstract class MemberTableCellRenderer extends JLabel implements TableCellRenderer {
-    public final static Color BGCOLOR_SELECTED = new Color(143, 170, 255);
     public final static Color BGCOLOR_EMPTY_ROW = new Color(234, 234, 234);
+    public final static Color BGCOLOR_IN_JOSM_SELECTION = new Color(235,255,177);
 
     public final static Color BGCOLOR_NOT_IN_OPPOSITE = new Color(255, 197, 197);
-    public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(255, 234, 213);
+    public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(254,226,214);
 
     /**
@@ -32,37 +31,10 @@
     }
 
-    public String buildToolTipText(OsmPrimitive primitive) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("<html>");
-        sb.append("<strong>id</strong>=").append(primitive.getId()).append("<br>");
-        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
-        Collections.sort(keyList);
-        for (int i = 0; i < keyList.size(); i++) {
-            if (i > 0) {
-                sb.append("<br>");
-            }
-            String key = keyList.get(i);
-            sb.append("<strong>").append(key).append("</strong>").append("=");
-            String value = primitive.get(key);
-            while (value.length() != 0) {
-                sb.append(value.substring(0, Math.min(50, value.length())));
-                if (value.length() > 50) {
-                    sb.append("<br>");
-                    value = value.substring(50);
-                } else {
-                    value = "";
-                }
-            }
-        }
-        sb.append("</html>");
-        return sb.toString();
-    }
-
     /**
      * reset the renderer
      */
     protected void reset() {
-        setBackground(Color.WHITE);
-        setForeground(Color.BLACK);
+        setBackground(UIManager.getColor("Table.background"));
+        setForeground(UIManager.getColor("Table.foreground"));
         setBorder(null);
         setIcon(null);
@@ -71,7 +43,9 @@
 
     protected void renderBackground(MemberTableModel model, OsmPrimitive primitive, boolean isSelected) {
-        Color bgc = Color.WHITE;
+        Color bgc = UIManager.getColor("Table.background");
         if (isSelected) {
-            bgc = BGCOLOR_SELECTED;
+            bgc = UIManager.getColor("Table.selectionBackground");
+        } else if (primitive != null && model.isInJosmSelection(primitive)) {
+            bgc = BGCOLOR_IN_JOSM_SELECTION;
         } else if (primitive != null && model.getNumMembersWithPrimitive(primitive) > 1) {
             bgc = BGCOLOR_DOUBLE_ENTRY;
@@ -81,5 +55,10 @@
 
     protected void renderForeground(boolean isSelected) {
-        Color fgc = Color.BLACK;
+        Color fgc;
+        if (isSelected) {
+            fgc = UIManager.getColor("Table.selectionForeground");
+        } else {
+            fgc = UIManager.getColor("Table.foreground");
+        }
         setForeground(fgc);
     }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableMemberCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableMemberCellRenderer.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableMemberCellRenderer.java	(revision 2563)
@@ -3,38 +3,21 @@
 
 import java.awt.Component;
-import java.util.HashMap;
 
-import javax.swing.ImageIcon;
 import javax.swing.JTable;
 
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.gui.DefaultNameFormatter;
 import org.openstreetmap.josm.tools.ImageProvider;
 
 public class MemberTableMemberCellRenderer extends MemberTableCellRenderer {
-    private HashMap<OsmPrimitiveType, ImageIcon> icons;
 
     public MemberTableMemberCellRenderer() {
         super();
-        loadIcons();
-    }
-
-    /**
-     * Load the image icon for an OSM primitive of type node
-     *
-     * @return the icon; null, if not found
-     */
-    protected void loadIcons() {
-        icons = new HashMap<OsmPrimitiveType, ImageIcon>();
-        icons.put(OsmPrimitiveType.NODE, ImageProvider.get("data", "node"));
-        icons.put(OsmPrimitiveType.WAY, ImageProvider.get("data", "way"));
-        icons.put(OsmPrimitiveType.RELATION, ImageProvider.get("data", "relation"));
     }
 
     protected void renderPrimitive(OsmPrimitive primitive) {
-        setIcon(icons.get(OsmPrimitiveType.from(primitive)));
+        setIcon(ImageProvider.get(primitive.getPrimitiveId().getType()));
         setText(primitive.getDisplayName(DefaultNameFormatter.getInstance()));
-        setToolTipText(buildToolTipText(primitive));
+        setToolTipText(DefaultNameFormatter.getInstance().buildDefaultToolTip(primitive));
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 2563)
@@ -26,5 +26,7 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.SelectionChangedListener;
 import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.DataSetListener;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -33,7 +35,8 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.dialogs.relation.WayConnectionType.Direction;
+import org.openstreetmap.josm.gui.layer.DataChangeListener;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 
-public class MemberTableModel extends AbstractTableModel implements TableModelListener {
+public class MemberTableModel extends AbstractTableModel implements TableModelListener, SelectionChangedListener, DataChangeListener, DataSetListener{
 
     /**
@@ -56,4 +59,70 @@
         addTableModelListener(this);
     }
+
+    public OsmDataLayer getLayer() {
+        return layer;
+    }
+
+    /* --------------------------------------------------------------------------- */
+    /* Interface SelectionChangedListener                                          */
+    /* --------------------------------------------------------------------------- */
+    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+        if (Main.main.getEditLayer() != this.layer) return;
+        // just trigger a repaint
+        Collection<RelationMember> sel = getSelectedMembers();
+        fireTableDataChanged();
+        setSelectedMembers(sel);
+    }
+
+    /* --------------------------------------------------------------------------- */
+    /* Interface DataChangeListener                                                */
+    /* --------------------------------------------------------------------------- */
+    public void dataChanged(OsmDataLayer l) {
+        if (l != this.layer) return;
+        // just trigger a repaint
+        Collection<RelationMember> sel = getSelectedMembers();
+        fireTableDataChanged();
+        setSelectedMembers(sel);
+    }
+    /* --------------------------------------------------------------------------- */
+    /* Interface DataSetListener                                                   */
+    /* --------------------------------------------------------------------------- */
+    public void dataChanged() {
+        // just trigger a repaint - the display name of the relation members may
+        // have changed
+        Collection<RelationMember> sel = getSelectedMembers();
+        fireTableDataChanged();
+        setSelectedMembers(sel);
+    }
+
+    public void nodeMoved(Node node) {/* ignore */}
+    public void primtivesAdded(Collection<? extends OsmPrimitive> added) {/* ignore */}
+
+    public void primtivesRemoved(Collection<? extends OsmPrimitive> removed) {
+        // ignore - the relation in the editor might become out of sync with the relation
+        // in the dataset. We will deal with it when the relation editor is closed or
+        // when the changes in the editor are applied.
+    }
+
+    public void relationMembersChanged(Relation r) {
+        // ignore - the relation in the editor might become out of sync with the relation
+        // in the dataset. We will deal with it when the relation editor is closed or
+        // when the changes in the editor are applied.
+    }
+
+    public void tagsChanged(OsmPrimitive prim) {
+        // just refresh the respective table cells
+        //
+        Collection<RelationMember> sel = getSelectedMembers();
+        for (int i=0; i < members.size();i++) {
+            if (members.get(i).getMember() == prim) {
+                fireTableCellUpdated(i, 1 /* the column with the primitive name */);
+            }
+        }
+        setSelectedMembers(sel);
+    }
+
+    public void wayNodesChanged(Way way) {/* ignore */}
+    /* --------------------------------------------------------------------------- */
 
     public void addMemberModelListener(IMemberModelListener listener) {
@@ -242,6 +311,54 @@
     }
 
+    /**
+     * Replies the set of incomplete primitives
+     * 
+     * @return the set of incomplete primitives
+     */
+    public Set<OsmPrimitive> getIncompleteMemberPrimitives() {
+        Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
+        for (RelationMember member : members) {
+            if (member.getMember().incomplete) {
+                ret.add(member.getMember());
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Replies the set of selected incomplete primitives
+     * 
+     * @return the set of selected incomplete primitives
+     */
+    public Set<OsmPrimitive> getSelectedIncompleteMemberPrimitives() {
+        Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
+        for (RelationMember member : getSelectedMembers()) {
+            if (member.getMember().incomplete) {
+                ret.add(member.getMember());
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Replies true if at least one the relation members is incomplete
+     * 
+     * @return true if at least one the relation members is incomplete
+     */
     public boolean hasIncompleteMembers() {
         for (RelationMember member : members) {
+            if (member.getMember().incomplete)
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Replies true if at least one of the selected members is incomplete
+     * 
+     * @return true if at least one of the selected members is incomplete
+     */
+    public boolean hasIncompleteSelectedMembers() {
+        for (RelationMember member : getSelectedMembers()) {
             if (member.getMember().incomplete)
                 return true;
@@ -408,16 +525,4 @@
 
     /**
-     * Replies true, if the selected {@see OsmPrimitive}s in the layer belonging
-     * to this model are in sync with the selected referers in this model.
-     *
-     * @return
-     */
-    public boolean selectionsAreInSync() {
-        HashSet<OsmPrimitive> s1 = new HashSet<OsmPrimitive>(getSelectedChildPrimitives());
-        if (s1.size() != layer.data.getSelected().size()) return false;
-        s1.removeAll(layer.data.getSelected());
-        return s1.isEmpty();
-    }
-    /**
      * Selects the members in the collection selectedMembers
      *
@@ -425,17 +530,16 @@
      */
     public void setSelectedMembers(Collection<RelationMember> selectedMembers) {
-        if (selectedMembers == null || selectedMembers.isEmpty())
+        if (selectedMembers == null || selectedMembers.isEmpty()) {
+            getSelectionModel().clearSelection();
             return;
+        }
 
         // lookup the indices for the respective members
         //
-        ArrayList<Integer> selectedIndices = new ArrayList<Integer>();
+        Set<Integer> selectedIndices = new HashSet<Integer>();
         for (RelationMember member : selectedMembers) {
-            for (int idx = 0; idx < members.size(); idx ++) {
-                if (members.get(idx).equals(member)) {
-                    if (!selectedIndices.contains(idx)) {
-                        selectedIndices.add(idx);
-                    }
-                }
+            int idx = members.indexOf(member);
+            if ( idx >= 0) {
+                selectedIndices.add(idx);
             }
         }
@@ -443,5 +547,4 @@
         // select the members
         //
-        Collections.sort(selectedIndices);
         getSelectionModel().setValueIsAdjusting(true);
         getSelectionModel().clearSelection();
@@ -450,9 +553,8 @@
         }
         getSelectionModel().setValueIsAdjusting(false);
-
         // make the first selected member visible
         //
         if (selectedIndices.size() > 0) {
-            fireMakeMemberVisible(selectedIndices.get(0));
+            fireMakeMemberVisible(Collections.min(selectedIndices));
         }
     }
@@ -523,4 +625,16 @@
             fireMakeMemberVisible(getSelectedIndices().get(0));
         }
+    }
+
+    /**
+     * Replies true if <code>primitive</code> is currently selected in the layer this
+     * model is attached to
+     * 
+     * @param primitive the primitive
+     * @return true if <code>primitive</code> is currently selected in the layer this
+     * model is attached to, false otherwise
+     */
+    public boolean isInJosmSelection(OsmPrimitive primitive) {
+        return layer.data.isSelected(primitive);
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java	(revision 2563)
@@ -4,4 +4,6 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
@@ -16,4 +18,14 @@
 
 public abstract class RelationEditor extends ExtendedDialog {
+    /** the property name for the current relation.
+     * @see #setRelation(Relation)
+     * @see #getRelation()
+     */
+    static public final String RELATION_PROP = RelationEditor.class.getName() + ".relation";
+
+    /** the property name for the current relation snapshot
+     * @see #getRelationSnapshot()
+     */
+    static public final String RELATION_SNAPSHOT_PROP = RelationEditor.class.getName() + ".relationSnapshot";
 
     /** the list of registered relation editor classes */
@@ -62,5 +74,8 @@
      * then an instance of that class will be used.
      *
+     * @param layer the data layer the relation is a member of
      * @param r the relation to be edited
+     * @param selectedMembers a collection of relation members which shall be selected when the
+     * editor is first launched
      * @return an instance of RelationEditor suitable for editing that kind of relation
      */
@@ -139,6 +154,10 @@
      */
     protected void setRelation(Relation relation) {
-        this.relationSnapshot = (relation == null) ? null : new Relation(relation);
+        setRelationSnapshot((relation == null) ? null : new Relation(relation));
+        Relation oldValue = this.relation;
         this.relation = relation;
+        if (this.relation != oldValue) {
+            support.firePropertyChange(RELATION_PROP, oldValue, this.relation);
+        }
         updateTitle();
     }
@@ -164,4 +183,12 @@
     }
 
+    protected void setRelationSnapshot(Relation snapshot) {
+        Relation oldValue = relationSnapshot;
+        relationSnapshot = snapshot;
+        if (relationSnapshot != oldValue) {
+            support.firePropertyChange(RELATION_SNAPSHOT_PROP, oldValue, relationSnapshot);
+        }
+    }
+
     /**
      * Replies true if the currently edited relation has been changed elsewhere.
@@ -175,3 +202,19 @@
         return ! relation.hasEqualSemanticAttributes(relationSnapshot);
     }
+
+
+    /* ----------------------------------------------------------------------- */
+    /* property change support                                                 */
+    /* ----------------------------------------------------------------------- */
+    final private PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+    @Override
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        this.support.addPropertyChangeListener(listener);
+    }
+
+    @Override
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        this.support.removePropertyChangeListener(listener);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTable.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTable.java	(revision 2563)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTable.java	(revision 2563)
@@ -0,0 +1,44 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Collections;
+
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+public class SelectionTable extends JTable {
+
+    private SelectionTableModel model;
+    private MemberTableModel memberTableModel;
+
+    protected void build() {
+        setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        addMouseListener(new DoubleClickAdapter());
+    }
+
+    public SelectionTable(SelectionTableModel model, SelectionTableColumnModel columnModel) {
+        super(model, columnModel);
+        this.model = model;
+        build();
+    }
+
+    public void setMemberTableModel(MemberTableModel memberTableModel) {
+        this.memberTableModel = memberTableModel;
+    }
+
+    class DoubleClickAdapter extends MouseAdapter {
+        @Override
+        public void mouseClicked(MouseEvent evt) {
+            if (! (SwingUtilities.isLeftMouseButton(evt) && evt.getClickCount() > 1))
+                return;
+            int row = rowAtPoint(evt.getPoint());
+            OsmPrimitive primitive = model.getPrimitive(row);
+            memberTableModel.selectMembersReferringTo(Collections.singleton(primitive));
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableCellRenderer.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableCellRenderer.java	(revision 2563)
@@ -4,27 +4,22 @@
 import java.awt.Color;
 import java.awt.Component;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
 
-import javax.swing.ImageIcon;
 import javax.swing.JLabel;
 import javax.swing.JTable;
+import javax.swing.UIManager;
 import javax.swing.table.TableCellRenderer;
 
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.gui.DefaultNameFormatter;
 import org.openstreetmap.josm.tools.ImageProvider;
 
 /**
- * This is the {@see TableCellRenderer} used in the tables of {@see RelationMemberMerger}.
+ * This is the {@see TableCellRenderer} used in {@see SelectionTable}.
  *
  */
 public  class SelectionTableCellRenderer extends JLabel implements TableCellRenderer {
-    public final static Color BGCOLOR_SELECTED = new Color(143,170,255);
-    public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(255,234,213);
+    public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(254,226,214);
+    public final static Color BGCOLOR_SINGLE_ENTRY = new Color(235,255,177);
 
-    private HashMap<OsmPrimitiveType, ImageIcon>  icons;
     /**
      * reference to the member table model; required, in order to check whether a
@@ -35,16 +30,4 @@
 
     /**
-     * Load the image icon for an OSM primitive of type node
-     *
-     * @return the icon; null, if not found
-     */
-    protected void loadIcons() {
-        icons = new HashMap<OsmPrimitiveType, ImageIcon>();
-        icons.put(OsmPrimitiveType.NODE,ImageProvider.get("data", "node"));
-        icons.put(OsmPrimitiveType.WAY, ImageProvider.get("data", "way"));
-        icons.put(OsmPrimitiveType.RELATION, ImageProvider.get("data", "relation"));
-    }
-
-    /**
      * constructor
      */
@@ -52,37 +35,4 @@
         setIcon(null);
         setOpaque(true);
-        loadIcons();
-    }
-
-    public String buildToolTipText(OsmPrimitive primitive) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("<html>");
-        sb.append("<strong>id</strong>=")
-        .append(primitive.getId())
-        .append("<br>");
-        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
-        Collections.sort(keyList);
-        for (int i = 0; i < keyList.size(); i++) {
-            if (i > 0) {
-                sb.append("<br>");
-            }
-            String key = keyList.get(i);
-            sb.append("<strong>")
-            .append(key)
-            .append("</strong>")
-            .append("=");
-            String value = primitive.get(key);
-            while(value.length() != 0) {
-                sb.append(value.substring(0,Math.min(50, value.length())));
-                if (value.length() > 50) {
-                    sb.append("<br>");
-                    value = value.substring(50);
-                } else {
-                    value = "";
-                }
-            }
-        }
-        sb.append("</html>");
-        return sb.toString();
     }
 
@@ -91,6 +41,6 @@
      */
     protected void reset() {
-        setBackground(Color.WHITE);
-        setForeground(Color.BLACK);
+        setBackground(UIManager.getColor("Table.background"));
+        setForeground(UIManager.getColor("Table.foreground"));
         setBorder(null);
         setIcon(null);
@@ -99,8 +49,8 @@
 
     protected void renderBackground(OsmPrimitive primitive, boolean isSelected) {
-        Color bgc = Color.WHITE;
-        if (isSelected) {
-            bgc = BGCOLOR_SELECTED;
-        } else if (primitive != null && model != null && model.getNumMembersWithPrimitive(primitive) > 0) {
+        Color bgc = UIManager.getColor("Table.background");
+        if (primitive != null && model != null && model.getNumMembersWithPrimitive(primitive) == 1) {
+            bgc = BGCOLOR_SINGLE_ENTRY;
+        } else if (primitive != null && model != null && model.getNumMembersWithPrimitive(primitive) > 1) {
             bgc = BGCOLOR_DOUBLE_ENTRY;
         }
@@ -108,13 +58,9 @@
     }
 
-    protected void renderForeground(boolean isSelected) {
-        Color fgc = Color.BLACK;
-        setForeground(fgc);
-    }
 
     protected void renderPrimitive(OsmPrimitive primitive) {
-        setIcon(icons.get(OsmPrimitiveType.from(primitive)));
+        setIcon(ImageProvider.get(primitive.getPrimitiveId().getType()));
         setText(primitive.getDisplayName(DefaultNameFormatter.getInstance()));
-        setToolTipText(buildToolTipText(primitive));
+        setToolTipText(DefaultNameFormatter.getInstance().buildDefaultToolTip(primitive));
     }
 
@@ -123,5 +69,4 @@
 
         reset();
-        renderForeground(isSelected);
         renderBackground((OsmPrimitive)value, isSelected);
         renderPrimitive((OsmPrimitive)value);
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableModel.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableModel.java	(revision 2563)
@@ -34,5 +34,4 @@
         this.layer = layer;
         cache = new ArrayList<OsmPrimitive>();
-        DataSet.selListeners.add(this);
         Layer.listeners.add(this);
         populateSelectedPrimitives(layer);
@@ -103,3 +102,13 @@
         selectionChanged(layer.data.getSelected());
     }
+
+    /**
+     * Replies the primitive at row <code>row</code> in this model
+     * 
+     * @param row the row
+     * @return  the primitive at row <code>row</code> in this model
+     */
+    public OsmPrimitive getPrimitive(int row) {
+        return cache.get(row);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 2563)
@@ -3,4 +3,5 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
 
 import java.io.InputStream;
@@ -308,8 +309,7 @@
         progressMonitor.subTask(tr("Downloading OSM data..."));
         try {
-
-            merge(
-                    OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false))
-            );
+            DataSet loaded = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+            rememberNodesOfIncompleteWaysToLoad(loaded);
+            merge(loaded);
         } catch(Exception e) {
             throw new OsmTransferException(e);
@@ -333,7 +333,7 @@
         progressMonitor.subTask(tr("Downloading OSM data..."));
         try {
-            merge(
-                    OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false))
-            );
+            DataSet loaded = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+            rememberNodesOfIncompleteWaysToLoad(loaded);
+            merge(loaded);
         } catch(Exception e) {
             throw new OsmTransferException(e);
@@ -376,4 +376,16 @@
     }
 
+    protected void rememberNodesOfIncompleteWaysToLoad(DataSet from) {
+        for (Way w: from.getWays()) {
+            if (w.incomplete) {
+                for (Node n: w.getNodes()) {
+                    if (n.incomplete) {
+                        nodes.add(n.getId());
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * merges the dataset <code>from</code> to {@see #outputDataSet}.
@@ -401,5 +413,5 @@
         case RELATION:  msg = tr("Fetching a package of relations from ''{0}''", OsmApi.getOsmApi().getBaseUrl()); break;
         }
-        progressMonitor.setCustomText(msg);
+        progressMonitor.indeterminateSubTask(msg);
         Set<Long> toFetch = new HashSet<Long>(ids);
         toFetch.addAll(ids);
@@ -428,23 +440,19 @@
      * found on  the server (the server response code was 404)
      *
-     * Invoke {@see #getSkippedWay()} to get a list of ways which this reader could not build from
-     * the fetched data because the ways refer to nodes which don't exist on the server.
-     *
      * @return the parsed data
      * @exception OsmTransferException thrown if an error occurs while communicating with the API server
      * @see #getMissingPrimitives()
-     * @see #getSkippedWays()
-     *
-
+     *
      */
     @Override
     public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
-        progressMonitor.beginTask("");
+        int n = nodes.size() + ways.size() + relations.size();
+        progressMonitor.beginTask(trn("Downloading {0} object from ''{1}''", "Downloading {0} objects from ''{1}''", n, n, OsmApi.getOsmApi().getBaseUrl()));
         try {
             missingPrimitives = new HashSet<Long>();
             if (isCanceled())return null;
+            fetchPrimitives(ways,OsmPrimitiveType.WAY, progressMonitor);
+            if (isCanceled())return null;
             fetchPrimitives(nodes,OsmPrimitiveType.NODE, progressMonitor);
-            if (isCanceled())return null;
-            fetchPrimitives(ways,OsmPrimitiveType.WAY, progressMonitor);
             if (isCanceled())return null;
             fetchPrimitives(relations,OsmPrimitiveType.RELATION, progressMonitor);
Index: trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 2563)
@@ -12,4 +12,5 @@
 import java.util.List;
 import java.util.Map;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -443,6 +444,8 @@
             w.setNodes(wayNodes);
             if (incomplete) {
-                logger.warning(tr("Marked way {0} with {1} nodes incomplete because at least one node was missing in the " +
-                        "loaded data and is therefore incomplete too.", externalWayId, w.getNodesCount()));
+                if (logger.isLoggable(Level.FINE)) {
+                    logger.fine(tr("Marked way {0} with {1} nodes incomplete because at least one node was missing in the " +
+                            "loaded data and is therefore incomplete too.", externalWayId, w.getNodesCount()));
+                }
                 w.incomplete = true;
                 ds.addPrimitive(w);
Index: trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java	(revision 2561)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java	(revision 2563)
@@ -9,21 +9,66 @@
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
+import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.xml.sax.SAXException;
 
+/**
+ * OsmServerObjectReader reads an individual object from the OSM server.
+ * 
+ * It can either download the object including or not including its immediate children.
+ * The former case is called a "full download".
+ * 
+ */
 public class OsmServerObjectReader extends OsmServerReader {
+    /** the id of the object to download */
+    private PrimitiveId id;
+    /** true if a full download is required, i.e. a download including the immediate children */
+    private boolean full;
 
-    long id;
-    OsmPrimitiveType type;
-    boolean full;
-
-    public OsmServerObjectReader(long id, OsmPrimitiveType type, boolean full) {
-        this.id = id;
-        this.type = type;
+    /**
+     * Creates a new server object reader for a given id and a primitive type.
+     * 
+     * @param id the object id. > 0 required.
+     * @param type the type. Must not be null.
+     * @param full true, if a full download is requested (i.e. a download including
+     * immediate children); false, otherwise
+     * @throws IllegalArgumentException thrown if id <= 0
+     * @throws IllegalArgumentException thrown if type is null
+     */
+    public OsmServerObjectReader(long id, OsmPrimitiveType type, boolean full) throws IllegalArgumentException {
+        if (id <= 0)
+            throw new IllegalArgumentException(tr("Expected value > 0 for parameter ''{0}'', got {1}", "id", id));
+        if (type == null)
+            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "type"));
+        this.id = new SimplePrimitiveId(id, type);
         this.full = full;
     }
+
     /**
-     * Method to download single objects from OSM server. ways, relations, nodes
-     * @return the data requested
+     * Creates a new server object reader for an object with the given <code>id</code>
+     * 
+     * @param id the object id. Must not be null. Unique id > 0 required.
+     * @param full true, if a full download is requested (i.e. a download including
+     * immediate children); false, otherwise
+     * @throws IllegalArgumentException thrown if id is null
+     * @throws IllegalArgumentException thrown if id.getUniqueId() <= 0
+     */
+    public OsmServerObjectReader(PrimitiveId id, boolean full) {
+        if (id == null)
+            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "id"));
+        if (id.getUniqueId() <= 0)
+            throw new IllegalArgumentException(tr("Expected value > 0 for parameter ''{0}'', got {1}", "id.getUniqueId()", id.getUniqueId()));
+        this.id = id;
+        this.full = full;
+    }
+
+    /**
+     * Downloads and parses the data.
+     * 
+     * @param progressMonitor the progress monitor. Set to {@see NullProgressMonitor#INSTANCE} if
+     * null
+     * @return the downloaded data
      * @throws SAXException
      * @throws IOException
@@ -31,4 +76,7 @@
     @Override
     public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
+        if (progressMonitor == null) {
+            progressMonitor = NullProgressMonitor.INSTANCE;
+        }
         progressMonitor.beginTask("", 1);
         InputStream in = null;
@@ -36,8 +84,8 @@
             progressMonitor.indeterminateSubTask(tr("Downloading OSM data..."));
             StringBuffer sb = new StringBuffer();
-            sb.append(type.getAPIName());
+            sb.append(id.getType().getAPIName());
             sb.append("/");
-            sb.append(id);
-            if (full && ! type.equals(OsmPrimitiveType.NODE)) {
+            sb.append(id.getUniqueId());
+            if (full && ! id.getType().equals(OsmPrimitiveType.NODE)) {
                 sb.append("/full");
             }
@@ -59,9 +107,8 @@
                 try {
                     in.close();
-                } catch(Exception e) {}
+                } catch(Exception e) {/* ignore this exception */}
             }
             activeConnection = null;
         }
     }
-
 }
