Index: trunk/src/org/openstreetmap/josm/actions/NewAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/NewAction.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/actions/NewAction.java	(revision 1790)
@@ -16,9 +16,9 @@
     public NewAction() {
         super(tr("New"), "new", tr("Create a new map."),
-        Shortcut.registerShortcut("system:new", tr("File: {0}", tr("New")), KeyEvent.VK_N, Shortcut.GROUP_MENU), true);
+                Shortcut.registerShortcut("system:new", tr("File: {0}", tr("New")), KeyEvent.VK_N, Shortcut.GROUP_MENU), true);
     }
 
     public void actionPerformed(ActionEvent e) {
-        Main.main.addLayer(new OsmDataLayer(new DataSet(), tr("unnamed"), null));
+        Main.main.addLayer(new OsmDataLayer(new DataSet(), OsmDataLayer.createNewName(), null));
     }
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java	(revision 1790)
@@ -32,12 +32,37 @@
     }
 
-    @Override public boolean equals(Object other) {
-        if (other == null || !(other instanceof RelationMember)) return false;
-        RelationMember otherMember = (RelationMember) other;
-        return otherMember.role.equals(role) && otherMember.member.equals(member);
-    }
-
     @Override public String toString() {
         return '"' + role + "\"=" + member;
     }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((member == null) ? 0 : member.hashCode());
+        result = prime * result + ((role == null) ? 0 : role.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        RelationMember other = (RelationMember) obj;
+        if (member == null) {
+            if (other.member != null)
+                return false;
+        } else if (!member.equals(other.member))
+            return false;
+        if (role == null) {
+            if (other.role != null)
+                return false;
+        } else if (!role.equals(other.role))
+            return false;
+        return true;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java	(revision 1790)
@@ -95,8 +95,11 @@
 
             button = new JButton(action);
-            if(buttonIcons != null && buttonIcons[i] != null)
+            if(buttonIcons != null && buttonIcons[i] != null) {
                 button.setIcon(ImageProvider.get(buttonIcons[i]));
+            }
 
-            if(i == 0) rootPane.setDefaultButton(button);
+            if(i == 0) {
+                rootPane.setDefaultButton(button);
+            }
             buttonsPanel.add(button, GBC.std().insets(2,2,2,2));
             buttons.add(button);
@@ -120,10 +123,15 @@
         boolean limitedInHeight = d.height > x.height;
 
-        if(x.width  > 0 && d.width  > x.width)  d.width  = x.width;
-        if(x.height > 0 && d.height > x.height) d.height = x.height;
+        if(x.width  > 0 && d.width  > x.width) {
+            d.width  = x.width;
+        }
+        if(x.height > 0 && d.height > x.height) {
+            d.height = x.height;
+        }
 
         // We have a vertical scrollbar and enough space to prevent a horizontal one
-        if(!limitedInWidth && limitedInHeight)
+        if(!limitedInWidth && limitedInHeight) {
             d.width += new JScrollBar().getPreferredSize().width;
+        }
 
         setSize(d);
@@ -161,8 +169,9 @@
         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
         Dimension x = new Dimension(Math.round(screenSize.width*2/3),
-                                    Math.round(screenSize.height*2/3));
+                Math.round(screenSize.height*2/3));
         try {
-            if(parent != null)
+            if(parent != null) {
                 x = JOptionPane.getFrameForComponent(parent).getSize();
+            }
         } catch(NullPointerException e) { }
         return x;
@@ -184,5 +193,5 @@
 
         rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
-            .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
+        .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
         rootPane.getActionMap().put("ESCAPE", actionListener);
     }
Index: trunk/src/org/openstreetmap/josm/gui/SideButton.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/SideButton.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/gui/SideButton.java	(revision 1790)
@@ -18,5 +18,4 @@
         super(action);
         doStyle();
-        setText(null);
     }
 
@@ -46,6 +45,7 @@
         {
             shortcut.setMnemonic(this);
-            if(tooltip != null)
+            if(tooltip != null) {
                 tooltip = Main.platform.makeTooltip(tooltip, shortcut);
+            }
         }
         setup(name, property, tooltip, actionListener);
Index: trunk/src/org/openstreetmap/josm/gui/conflict/nodes/NodeListTableCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/conflict/nodes/NodeListTableCellRenderer.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/gui/conflict/nodes/NodeListTableCellRenderer.java	(revision 1790)
@@ -50,33 +50,4 @@
         rowNumberBorder = BorderFactory.createEmptyBorder(0,4,0,0);
         setOpaque(true);
-    }
-
-    /**
-     * creates the display name for a node. The name is derived from the nodes id,
-     * its name (i.e. the value of the tag with key name) and its coordinates.
-     * 
-     * @param node  the node
-     * @return the display name
-     */
-    protected String getDisplayName(Node node) {
-        StringBuilder sb = new StringBuilder();
-        if (node.get("name") != null) {
-            sb.append(node.get("name"));
-            sb.append("/");
-            sb.append(node.id);
-        } else {
-            sb.append(node.id);
-        }
-        sb.append(" (");
-
-        if (node.getCoor() != null) {
-            sb.append(COORD_FORMATTER.format(node.getCoor().lat()));
-            sb.append(",");
-            sb.append(COORD_FORMATTER.format(node.getCoor().lon()));
-        } else {
-            sb.append("?,?");
-        }
-        sb.append(")");
-        return sb.toString();
     }
 
@@ -159,5 +130,5 @@
             }
         }
-        setText(getDisplayName(node));
+        setText(node.getName());
         setToolTipText(buildToolTipText(node));
     }
Index: trunk/src/org/openstreetmap/josm/gui/conflict/relation/RelationMemberTableCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/conflict/relation/RelationMemberTableCellRenderer.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/gui/conflict/relation/RelationMemberTableCellRenderer.java	(revision 1790)
@@ -97,37 +97,4 @@
         }
         sb.append("</html>");
-        return sb.toString();
-    }
-
-    /**
-     * creates the display name for a node. The name is derived from the nodes id,
-     * its name (i.e. the value of the tag with key name) and its coordinates.
-     * 
-     * @param node  the node
-     * @return the display name
-     */
-    protected String getDisplayName(RelationMember member) {
-        StringBuilder sb = new StringBuilder();
-        OsmPrimitive primitive = member.member;
-        if (primitive.get("name") != null) {
-            sb.append(primitive.get("name"));
-            sb.append("/");
-            sb.append(primitive.id);
-        } else {
-            sb.append(primitive.id);
-        }
-
-        if (primitive instanceof Node) {
-            Node n = (Node)primitive;
-            sb.append(" (");
-            if (n.getCoor() != null) {
-                sb.append(COORD_FORMATTER.format(n.getCoor().lat()));
-                sb.append(",");
-                sb.append(COORD_FORMATTER.format(n.getCoor().lon()));
-            } else {
-                sb.append("?,?");
-            }
-            sb.append(")");
-        }
         return sb.toString();
     }
@@ -191,5 +158,5 @@
 
     protected void renderPrimitive(RelationMember member) {
-        String displayName = getDisplayName(member);
+        String displayName = member.member.getName();
         setText(displayName);
         setToolTipText(buildToolTipText(member.member));
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java	(revision 1790)
@@ -64,5 +64,4 @@
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.SideButton;
-import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
 import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
 import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1790)
@@ -1,25 +1,26 @@
 package org.openstreetmap.josm.gui.dialogs.relation;
 
-import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.BorderLayout;
 import java.awt.Dimension;
 import java.awt.EventQueue;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
-import java.awt.GridLayout;
+import java.awt.Insets;
 import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
 import java.awt.event.KeyEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.LinkedList;
-import java.util.Vector;
-
+import java.util.logging.Logger;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComponent;
 import javax.swing.JDialog;
 import javax.swing.JLabel;
@@ -27,13 +28,12 @@
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
-import javax.swing.JTabbedPane;
+import javax.swing.JSplitPane;
 import javax.swing.JTable;
+import javax.swing.KeyStroke;
 import javax.swing.ListSelectionModel;
-import javax.swing.SwingUtilities;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 import javax.swing.event.TableModelEvent;
 import javax.swing.event.TableModelListener;
-import javax.swing.table.DefaultTableModel;
 
 import org.openstreetmap.josm.Main;
@@ -42,12 +42,9 @@
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.DataSource;
-import org.openstreetmap.josm.data.osm.Node;
 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.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
-import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.SideButton;
@@ -58,38 +55,8 @@
 import org.openstreetmap.josm.io.OsmServerObjectReader;
 import org.openstreetmap.josm.io.OsmTransferException;
-import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
 import org.xml.sax.SAXException;
 
-enum WayConnectionType
-{
-    none,
-    head_to_head,
-    tail_to_tail,
-    head_to_tail,
-    tail_to_head;
-    @Override
-    public String toString()
-    {
-        String  result = "";
-        switch(this)
-        {
-        case head_to_head:
-            result = "-><-";
-            break;
-        case head_to_tail:
-            result = "->->";
-            break;
-        case tail_to_head:
-            result = "<-<-";
-            break;
-        case tail_to_tail:
-            result = "<-->";
-            break;
-        }
-
-        return result;
-    }
-}
 
 /**
@@ -106,25 +73,5 @@
 public class GenericRelationEditor extends RelationEditor {
 
-    // We need this twice, so cache result
-    protected final static String applyChangesText = tr("Apply Changes");
-
-    private JLabel status;
-
-    /**
-     * The membership data.
-     */
-    private final DefaultTableModel memberData = new DefaultTableModel() {
-        @Override public boolean isCellEditable(int row, int column) {
-            return column == 0;
-        }
-        @Override public Class<?> getColumnClass(int columnIndex) {
-            return columnIndex == 1 ? OsmPrimitive.class : String.class;
-        }
-    };
-
-    /**
-     * The properties and membership lists.
-     */
-    private final JTable memberTable = new JTable(memberData);
+    static private final Logger logger = Logger.getLogger(GenericRelationEditor.class.getName());
 
     /** the tag table and its model */
@@ -134,4 +81,10 @@
     private AutoCompletionList acList;
 
+    /** the member table */
+    private MemberTable memberTable;
+    private MemberTableModel memberTableModel;
+
+    /** the model for the selection table */
+    private SelectionTableModel selectionTableModel;
 
     /**
@@ -141,66 +94,120 @@
      * If no relation is given, will create an editor for a new relation.
      *
+     * @param layer the {@see OsmDataLayer} the new or edited relation belongs to
      * @param relation relation to edit, or null to create a new one.
+     * @param selectedMembers a collection of members which shall be selected initially
      */
     public GenericRelationEditor(OsmDataLayer layer, Relation relation, Collection<RelationMember> selectedMembers )
     {
-        // Initalizes ExtendedDialog
         super(layer, relation, selectedMembers);
+
+        // initialize the autocompletion infrastructure
+        //
         acCache = AutoCompletionCache.getCacheForLayer(Main.map.mapView.getEditLayer());
         acList = new AutoCompletionList();
 
-        JPanel bothTables = setupBasicLayout(selectedMembers);
+        // init the various models
+        //
+        tagEditorModel = new TagEditorModel();
+        memberTableModel = new MemberTableModel();
+        selectionTableModel = new SelectionTableModel(getLayer());
+
+        // populate the models
+        //
         if (relation != null) {
             this.tagEditorModel.initFromPrimitive(relation);
+            this.memberTableModel.populate(relation);
         } else {
             tagEditorModel.clear();
-        }
+            this.memberTableModel.populate(null);
+        }
+        memberTableModel.selectMembers(selectedMembers);
         tagEditorModel.ensureOneTag();
-        addWindowListener(
-                new WindowAdapter() {
-                    protected void requestFocusInTopLeftCell() {
-                        SwingUtilities.invokeLater(new Runnable(){
-                            public void run()
-                            {
-                                tagEditorModel.ensureOneTag();
-                                tagTable.requestFocusInCell(0, 0);
-                            }
-                        });
-                    }
-                    @Override
-                    public void windowDeiconified(WindowEvent e) {
-                        requestFocusInTopLeftCell();
-                    }
-                    @Override
-                    public void windowOpened(WindowEvent e) {
-                        requestFocusInTopLeftCell();
-                    }
-                }
-        );
-
-        JTabbedPane tabPane = new JTabbedPane();
-        tabPane.add(bothTables, tr("Basic"));
-
-        // This sets the minimum size before scrollbars appear on the dialog
-        tabPane.setPreferredSize(new Dimension(100, 100));
-        contentConstraints = GBC.eol().fill().insets(5,10,5,0);
-        setupDialog(tabPane, new String[] { "ok.png", "cancel.png" });
-        // FIXME: Make it remember last screen position
+
+        JSplitPane pane = buildSplitPane();
+        pane.setPreferredSize(new Dimension(100, 100));
+
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new BorderLayout());
+        pnl.add(pane,BorderLayout.CENTER);
+        pnl.setBorder(BorderFactory.createRaisedBevelBorder());
+
+        getContentPane().setLayout(new BorderLayout());
+        getContentPane().add(pnl,BorderLayout.CENTER);
+        getContentPane().add(buildOkCancelButtonPanel(), BorderLayout.SOUTH);
+
         setSize(findMaxDialogSize());
-
-        try { setAlwaysOnTop(true); } catch (SecurityException sx) {}
-        setVisible(true);
-    }
-
-
-    /**
-     * Basic Editor panel has two blocks: a tag table at the top and a membership list below
-     * @param selectedMembers
-     * @return a JPanel with the described layout
-     */
-    private JPanel setupBasicLayout(Collection<RelationMember> selectedMembers) {
+        try {
+            setAlwaysOnTop(true);
+        } catch(SecurityException e) {
+            logger.warning(tr("Caught security exception for setAlwaysOnTop(). Ignoring. Exception was: {0}", e.toString()));
+        }
+    }
+
+    /**
+     * builds the panel with the OK and  the Cancel button
+     * 
+     * @return the panel with the OK and  the Cancel button
+     */
+    protected JPanel buildOkCancelButtonPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
+
+        pnl.add(new SideButton(new OKAction()));
+        pnl.add(new SideButton(new CancelAction()));
+
+        return pnl;
+    }
+
+    /**
+     * build the panel with the buttons on the left
+     * 
+     * @return
+     */
+    protected JPanel buildTagEditorControlPanel() {
+        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;
+
+        // -----
+        AddTagAction addTagAction = new AddTagAction();
+        pnl.add(new JButton(addTagAction), gc);
+
+        // -----
+        gc.gridy = 1;
+        DeleteTagAction deleteTagAction = new DeleteTagAction();
+        tagTable.getSelectionModel().addListSelectionListener(deleteTagAction);
+        pnl.add(new JButton(deleteTagAction), gc);
+
+        // ------
+        // just grab the remaining space
+        gc.gridy = 2;
+        gc.weighty = 1.0;
+        gc.fill = GridBagConstraints.BOTH;
+        pnl.add(new JPanel(),gc);
+        return pnl;
+    }
+
+    /**
+     * builds the panel with the tag editor
+     * 
+     * @return the panel with the tag editor
+     */
+    protected JPanel buildTagEditorPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new GridBagLayout());
+
         // setting up the tag table
         //
-        tagEditorModel = new TagEditorModel();
         tagTable = new TagTable(tagEditorModel);
         acCache.initFromJOSMDataset();
@@ -212,47 +219,4 @@
         editor.setAutoCompletionList(acList);
 
-
-        // setting up the member table
-
-        memberData.setColumnIdentifiers(new String[]{tr("Role"),tr("Occupied By"), tr("linked")});
-        memberTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
-        memberTable.getColumnModel().getColumn(1).setCellRenderer(new OsmPrimitivRenderer());
-        memberData.addTableModelListener(new TableModelListener() {
-            public void tableChanged(TableModelEvent tme) {
-                if (tme.getType() == TableModelEvent.UPDATE && tme.getColumn() == 0) {
-                    int row = tme.getFirstRow();
-                    getClone().members.get(row).role = memberData.getValueAt(row, 0).toString();
-                }
-            }
-        });
-        ListSelectionModel lsm = memberTable.getSelectionModel();
-        lsm.addListSelectionListener(new ListSelectionListener() {
-            public void valueChanged(ListSelectionEvent e) {
-                ArrayList<OsmPrimitive> sel;
-                int cnt = memberTable.getSelectedRowCount();
-                if(cnt > 0)
-                {
-                    sel = new ArrayList<OsmPrimitive>(cnt);
-                    for (int i : memberTable.getSelectedRows()) {
-                        sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
-                    }
-                }
-                else
-                {
-                    cnt = memberTable.getRowCount();
-                    sel = new ArrayList<OsmPrimitive>(cnt);
-                    for (int i = 0; i < cnt; ++i) {
-                        sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
-                    }
-                }
-                Main.ds.setSelected(sel);
-            }
-        });
-        memberTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
-
-        // combine both tables and wrap them in a scrollPane
-        JPanel bothTables = new JPanel();
-        bothTables.setLayout(new GridBagLayout());
-        bothTables.add(new JLabel(tr("Tags")), GBC.eol().fill(GBC.HORIZONTAL));
         final JScrollPane scrollPane = new JScrollPane(tagTable);
 
@@ -270,313 +234,278 @@
                 }
         );
-        bothTables.add(scrollPane, GBC.eop().fill(GBC.BOTH));
-        bothTables.add(status = new JLabel(tr("Members")), GBC.eol().fill(GBC.HORIZONTAL));
-        // this is not exactly pretty but the four buttons simply don't fit in one line.
-        // we should have smaller buttons for situations like this.
-        JPanel buttonPanel = setupBasicButtons();
-
-        bothTables.add(new JScrollPane(memberTable), GBC.eol().fill(GBC.BOTH));
-        bothTables.add(buttonPanel, GBC.eop().fill(GBC.HORIZONTAL));
-        refreshTables();
-
-        if (selectedMembers != null) {
-            boolean scrolled = false;
-            for (int i = 0; i < memberData.getRowCount(); i++) {
-                for (RelationMember m : selectedMembers) {
-                    if (m.member == memberData.getValueAt(i, 1)
-                            && m.role.equals(memberData.getValueAt(i, 0))) {
-                        memberTable.addRowSelectionInterval(i, i);
-                        if (!scrolled) {
-                            // Ensure that the first member is visible
-                            memberTable.scrollRectToVisible(memberTable.getCellRect(i, 0, true));
-                            scrolled = true;
-                        }
-                        break;
+
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.gridx = 0;
+        gc.gridy = 0;
+        gc.gridheight  =1;
+        gc.gridwidth = 3;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.anchor = GridBagConstraints.FIRST_LINE_START;
+        gc.weightx = 1.0;
+        gc.weighty = 0.0;
+        pnl.add(new JLabel(tr("Tags")), gc);
+
+        gc.gridx = 0;
+        gc.gridy = 1;
+        gc.gridheight  =1;
+        gc.gridwidth = 1;
+        gc.fill = GridBagConstraints.VERTICAL;
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.weightx = 0.0;
+        gc.weighty = 1.0;
+        pnl.add(buildTagEditorControlPanel(), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.8;
+        gc.weighty = 1.0;
+        pnl.add(scrollPane, gc);
+        return pnl;
+    }
+
+    /**
+     * builds the panel for the relation member editor
+     * 
+     * @return the panel for the relation member editor
+     */
+    protected JPanel buildMemberEditorPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new GridBagLayout());
+        // setting up the member table
+        memberTable = new MemberTable(memberTableModel);
+
+        ListSelectionModel lsm = memberTable.getSelectionModel();
+        lsm.addListSelectionListener(new ListSelectionListener() {
+            public void valueChanged(ListSelectionEvent e) {
+                ArrayList<OsmPrimitive> sel;
+                int cnt = memberTable.getSelectedRowCount();
+                if(cnt > 0) {
+                    sel = new ArrayList<OsmPrimitive>(cnt);
+                    for (int i : memberTable.getSelectedRows()) {
+                        sel.add(memberTableModel.getReferredPrimitive(i));
+                    }
+                } else {
+                    cnt = memberTable.getRowCount();
+                    sel = new ArrayList<OsmPrimitive>(cnt);
+                    for (int i = 0; i < cnt; ++i) {
+                        sel.add(memberTableModel.getReferredPrimitive(i));
                     }
                 }
-
+                Main.ds.setSelected(sel);
             }
-        }
-        return bothTables;
-    }
-
-    /**
-     * Creates the buttons for the basic editing layout
-     * @return JPanel with basic buttons
-     */
-    private JPanel setupBasicButtons() {
-        JPanel buttonPanel = new JPanel(new GridLayout(2, 4));
-
-        buttonPanel.add(createButton(marktr("Move Up"), "moveup", tr("Move the currently selected members up"), KeyEvent.VK_N, new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                moveMembers(-1);
-            }
-        }));
-
-        buttonPanel.add(createButton(marktr("Add Selected"),"addselected",
-                tr("Add all currently selected objects as members"), KeyEvent.VK_D, new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                addSelected();
-            }
-        }));
-
-        buttonPanel.add(createButton(marktr("Remove Selected"),"removeselected",
-                tr("Remove all currently selected objects from relation"), KeyEvent.VK_S, new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                deleteSelected();
-            }
-        }));
-
-        buttonPanel.add(createButton(marktr("Sort"), "sort",
-                tr("Sort the selected relation members or the whole list"), KeyEvent.VK_O, new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                sort();
-            }
-        }));
-
-        buttonPanel.add(createButton(marktr("Move Down"), "movedown", tr("Move the currently selected members down"), KeyEvent.VK_J, new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                moveMembers(1);
-            }
-        }));
-
-        buttonPanel.add(createButton(marktr("Remove"),"remove",
-                tr("Remove the member in the current table row from this relation"), KeyEvent.VK_X, new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                int[] rows = memberTable.getSelectedRows();
-                RelationMember mem = new RelationMember();
-                for (int row : rows) {
-                    mem.role = memberTable.getValueAt(row, 0).toString();
-                    mem.member = (OsmPrimitive) memberTable.getValueAt(row, 1);
-                    getClone().members.remove(mem);
-                }
-                refreshTables();
-            }
-        }));
-
-        buttonPanel.add(createButton(marktr("Download Members"),"downloadincomplete",
-                tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_K, new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                downloadRelationMembers();
-                refreshTables();
-            }
-        }));
-
-        return buttonPanel;
-    }
-
-    private void sort() {
-        RelationNodeMap                    map = new RelationNodeMap(getClone());
-        Vector<LinkedList<Integer>>        segments;
-        LinkedList<Integer>                segment;
-        Node                               startSearchNode;
-        Node                               endSearchNode;
-        boolean                            something_done;
-
-        /*
-         * sort any 2 or more connected elements together
-         * may be slow with many unconnected members
-         * TODO: cleanup again, too much code in 1 method
-         */
-
-        segments = new Vector<LinkedList<Integer>>();
-        // add first member of relation, not strictly necessary
-        if (map.remove(0, getClone().members.get(0)))
-        {
-            segment = new LinkedList<Integer>();
-            segment.add(Integer.valueOf(0));
-            segments.add(segment);
-        }
-        while (!map.isEmpty())
-        {
-            segment = segments.lastElement();
-
-            do
-            {
-                something_done = false;
-                startSearchNode = null;
-                endSearchNode = null;
-                if (segment.size() == 1)
-                {
-                    RelationMember  m = getClone().members.get(segment.getFirst());
-                    try
-                    {
-                        Way             w = (Way)m.member;
-                        endSearchNode = w.lastNode();
-                        startSearchNode = w.firstNode();
-                    }
-                    catch(ClassCastException e1)
-                    {
-                        try
-                        {
-                            Node n = (Node)m.member;
-                            endSearchNode = n;
-                        }
-                        catch(ClassCastException e2)
-                        {
-                            // impossible
-                        }
+        });
+
+        final JScrollPane scrollPane = new JScrollPane(memberTable);
+        // this adapters ensures that the width of the tag table columns is adjusted
+        // to the width of the scroll pane viewport. Also tried to overwrite
+        // getPreferredViewportSize() in JTable, but did not work.
+        //
+        scrollPane.addComponentListener(
+                new ComponentAdapter() {
+                    @Override public void componentResized(ComponentEvent e) {
+                        super.componentResized(e);
+                        Dimension d = scrollPane.getViewport().getExtentSize();
+                        memberTable.adjustColumnWidth(d.width);
                     }
                 }
-                else
-                {
-                    // add unused node of first element and unused node of last element
-                    // start with the first element
-                    RelationMember element = getClone().members.get(segment.getFirst());
-                    RelationMember other_element = getClone().members.get(segment.get(1));
-
-                    try
-                    {
-                        Way w = (Way)element.member;
-                        try
-                        {
-                            Way x = (Way)other_element.member;
-                            if ((w.firstNode() == x.firstNode()) || (w.firstNode() == x.lastNode()))
-                            {
-                                startSearchNode = w.lastNode();
-                            }
-                            else
-                            {
-                                startSearchNode = w.firstNode();
-                            }
-                        }
-                        catch(ClassCastException e3)
-                        {
-                            try
-                            {
-                                Node m = (Node)other_element.member;
-                                if (w.firstNode() == m)
-                                {
-                                    startSearchNode = w.lastNode();
-                                }
-                                else
-                                {
-                                    startSearchNode = w.firstNode();
-                                }
-                            }
-                            catch(ClassCastException e4)
-                            {
-                                // impossible
-                            }
-                        }
-                    }
-                    catch(ClassCastException e1)
-                    {
-                        try
-                        {
-                            Node n = (Node)element.member;
-                            startSearchNode = n;
-                        }
-                        catch(ClassCastException e2)
-                        {
-                            // impossible
-                        }
-                    }
-                    // now the same for the last element
-                    element = getClone().members.get(segment.getLast());
-                    other_element = getClone().members.get(segment.get(segment.size() - 2));
-
-                    try
-                    {
-                        Way w = (Way)element.member;
-                        try
-                        {
-                            Way x = (Way)other_element.member;
-                            if ((w.firstNode() == x.firstNode()) || (w.firstNode() == x.lastNode()))
-                            {
-                                endSearchNode = w.lastNode();
-                            }
-                            else
-                            {
-                                endSearchNode = w.firstNode();
-                            }
-                        }
-                        catch(ClassCastException e3)
-                        {
-                            try
-                            {
-                                Node m = (Node)other_element.member;
-                                if (w.firstNode() == m)
-                                {
-                                    endSearchNode = w.lastNode();
-                                }
-                                else
-                                {
-                                    endSearchNode = w.firstNode();
-                                }
-                            }
-                            catch(ClassCastException e4)
-                            {
-                                // impossible
-                            }
-                        }
-                    }
-                    catch(ClassCastException e1)
-                    {
-                        try
-                        {
-                            Node n = (Node)element.member;
-                            endSearchNode = n;
-                        }
-                        catch(ClassCastException e2)
-                        {
-                            // impossible
-                        }
-                    }
-                }
-
-                // let's see if we can find connected elements for endSearchNode and startSearchNode
-                if (startSearchNode != null)
-                {
-                    Integer m2 = map.find(startSearchNode, segment.getFirst());
-                    if (m2 != null)
-                    {
-                        segment.add(0, m2);
-                        map.remove(m2, getClone().members.get(m2));
-                        something_done = true;
-                    }
-                }
-                if (endSearchNode != null)
-                {
-                    Integer m2 = map.find(endSearchNode, segment.getLast());
-                    if (m2 != null)
-                    {
-                        segment.add(segment.size(), m2);
-                        map.remove(m2, getClone().members.get(m2));
-                        something_done = true;
-                    }
-                }
-            } while (something_done);
-
-            Integer next = map.pop();
-            if (next == null)
-            {
-                break;
-            }
-
-            segment = new LinkedList<Integer>();
-            segment.add(next);
-            segments.add(segment);
-        }
-        // append map.remaining() to segments list (as a single segment)
-        segment = new LinkedList<Integer>();
-        segment.addAll(map.getRemaining());
-        segments.add(segment);
-
-        // now we need to actually re-order the relation members
-        ArrayList<RelationMember>  newmembers = new ArrayList<RelationMember>();
-        for (LinkedList<Integer> segment2 : segments)
-        {
-            for (Integer p : segment2)
-            {
-                newmembers.add(getClone().members.get(p));
-            }
-        }
-        getClone().members.clear();
-        getClone().members.addAll(newmembers);
-
-        refreshTables();
-    }
-
+        );
+
+
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.gridx = 0;
+        gc.gridy = 0;
+        gc.gridheight  =1;
+        gc.gridwidth = 3;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.anchor = GridBagConstraints.FIRST_LINE_START;
+        gc.weightx = 1.0;
+        gc.weighty = 0.0;
+        pnl.add(new JLabel(tr("Members")), gc);
+
+        gc.gridx = 0;
+        gc.gridy = 1;
+        gc.gridheight  =1;
+        gc.gridwidth = 1;
+        gc.fill = GridBagConstraints.VERTICAL;
+        gc.anchor = GridBagConstraints.NORTHWEST;
+        gc.weightx = 0.0;
+        gc.weighty = 1.0;
+        pnl.add(buildLeftButtonPanel(), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.8;
+        gc.weighty = 1.0;
+        pnl.add(scrollPane, gc);
+
+        gc.gridx = 2;
+        gc.gridy = 1;
+        gc.weightx = 0.0;
+        gc.weighty = 1.0;
+        pnl.add(buildSelectionControlButtonPanel(), gc);
+
+        gc.gridx = 3;
+        gc.gridy = 1;
+        gc.weightx = 0.2;
+        gc.weighty = 1.0;
+        pnl.add(buildSelectionTablePanel(), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 2;
+        gc.weightx = 1.0;
+        gc.weighty = 0.0;
+        pnl.add(buildButtonPanel(), gc);
+
+        return pnl;
+    }
+
+    /**
+     * builds the panel with the table displaying the currently selected primitives
+     * 
+     * @return
+     */
+    protected JPanel buildSelectionTablePanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new BorderLayout());
+
+        JTable tbl = new JTable(selectionTableModel,new SelectionTableColumnModel());
+        tbl.setEnabled(false);
+
+        JScrollPane pane = new JScrollPane(tbl);
+        pnl.add(pane, BorderLayout.CENTER);
+        return pnl;
+    }
+
+    /**
+     * builds the {@see JSplitPane} which divides the editor in an upper and a lower
+     * half
+     * 
+     * @return the split panel
+     */
+    protected JSplitPane buildSplitPane() {
+        JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+        pane.setTopComponent(buildTagEditorPanel());
+        pane.setBottomComponent(buildMemberEditorPanel());
+        pane.setOneTouchExpandable(true);
+        pane.setDividerLocation(150);
+        return pane;
+    }
+
+    /**
+     * build the panel with the buttons on the left
+     * 
+     * @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;
+        MoveUpAction moveUpAction = new MoveUpAction();
+        memberTableModel.getSelectionModel().addListSelectionListener(moveUpAction);
+        pnl.add(new JButton(moveUpAction), gc);
+
+        // -----
+        gc.gridy = 1;
+        MoveDownAction moveDownAction = new MoveDownAction();
+        memberTableModel.getSelectionModel().addListSelectionListener(moveDownAction);
+        pnl.add(new JButton(moveDownAction), gc);
+
+        // ------
+        gc.gridy = 2;
+        RemoveAction removeSelectedAction = new RemoveAction();
+        memberTable.getSelectionModel().addListSelectionListener(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);
+        return pnl;
+    }
+
+    /**
+     * build the panel with the buttons for adding or removing the current selection
+     * 
+     * @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;
+        AddSelectedAtStartAction addSelectionAction = new AddSelectedAtStartAction();
+        selectionTableModel.addTableModelListener(addSelectionAction);
+        pnl.add(new JButton(addSelectionAction), gc);
+
+        // -----
+        gc.gridy = 1;
+        AddSelectedBeforeSelection addSelectedBeforeSelectionAction = new AddSelectedBeforeSelection();
+        selectionTableModel.addTableModelListener(addSelectedBeforeSelectionAction);
+        memberTableModel.getSelectionModel().addListSelectionListener(addSelectedBeforeSelectionAction);
+        pnl.add(new JButton(addSelectedBeforeSelectionAction), gc);
+
+        // -----
+        gc.gridy = 2;
+        AddSelectedAfterSelection addSelectedAfterSelectionAction = new AddSelectedAfterSelection();
+        selectionTableModel.addTableModelListener(addSelectedAfterSelectionAction);
+        memberTableModel.getSelectionModel().addListSelectionListener(addSelectedAfterSelectionAction);
+        pnl.add(new JButton(addSelectedAfterSelectionAction), gc);
+
+        // -----
+        gc.gridy = 3;
+        AddSelectedAtEndAction addSelectedAtEndAction = new AddSelectedAtEndAction();
+        selectionTableModel.addTableModelListener(addSelectedAtEndAction);
+        pnl.add(new JButton(addSelectedAtEndAction), gc);
+
+        // -----
+        gc.gridy = 4;
+        RemoveSelectedAction removeSelectedAction = new RemoveSelectedAction();
+        selectionTableModel.addTableModelListener(removeSelectedAction);
+        pnl.add(new JButton(removeSelectedAction), gc);
+
+        // ------
+        // just grab the remaining space
+        gc.gridy = 5;
+        gc.weighty = 1.0;
+        gc.fill = GridBagConstraints.BOTH;
+        pnl.add(new JPanel(),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));
+        buttonPanel.add(new SideButton(new DownlaodAction()));
+        return buttonPanel;
+    }
 
     /**
@@ -587,24 +516,18 @@
             // If the user wanted to create a new relation, but hasn't added any members or
             // tags, don't add an empty relation
-            if(getClone().members.size() == 0 && tagEditorModel.getKeys().isEmpty())
+            if(memberTableModel.getRowCount() == 0 && tagEditorModel.getKeys().isEmpty())
                 return;
-            tagEditorModel.applyToPrimitive(getClone());
-            Main.main.undoRedo.add(new AddCommand(getClone()));
+            Relation clone = new Relation(getRelation());
+            tagEditorModel.applyToPrimitive(clone);
+            memberTableModel.applyToRelation(clone);
+            Main.main.undoRedo.add(new AddCommand(clone));
             DataSet.fireSelectionChanged(Main.ds.getSelected());
-        } else if (! getRelation().hasEqualSemanticAttributes(getClone()) || tagEditorModel.isDirty()) {
-            tagEditorModel.applyToPrimitive(getClone());
-            Main.main.undoRedo.add(new ChangeCommand(getRelation(), getClone()));
+        } else if (! memberTableModel.hasSameMembersAs(getRelation()) || tagEditorModel.isDirty()) {
+            Relation clone = new Relation(getRelation());
+            tagEditorModel.applyToPrimitive(clone);
+            memberTableModel.applyToRelation(clone);
+            Main.main.undoRedo.add(new ChangeCommand(getRelation(), clone));
             DataSet.fireSelectionChanged(Main.ds.getSelected());
         }
-    }
-
-    @Override
-    protected void buttonAction(ActionEvent evt) {
-        String a = evt.getActionCommand();
-        if(applyChangesText.equals(a)) {
-            applyChanges();
-        }
-
-        setVisible(false);
     }
 
@@ -616,323 +539,416 @@
 
     /**
-     * Updates the references from members of the cloned relation (see {@see #getClone())
-     * after an update from the server.
+     * Asynchronously download the members of the currently edited relation
      *
      */
-    protected void updateMemberReferencesInClone() {
-        DataSet ds = getLayer().data;
-        for (RelationMember member : getClone().members) {
-            if (member.member.id == 0) {
-                continue;
+    private void downloadRelationMembers() {
+        if (!memberTableModel.hasIncompleteMembers())
+            return;
+        Main.worker.submit(new DownloadTask());
+    }
+
+    @Override
+    public void dispose() {
+        selectionTableModel.unregister();
+        super.dispose();
+    }
+
+    @Override
+    public void setVisible(boolean b) {
+        super.setVisible(b);
+        if (!b) {
+            dispose();
+        }
+    }
+
+    class AddSelectedAtStartAction extends AbstractAction implements TableModelListener {
+        public AddSelectedAtStartAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Add all primitives selected in the current dataset before the first member"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copystartright"));
+            //putValue(NAME, tr("Add Selected"));
+            refreshEnabled();
+        }
+
+        protected void refreshEnabled() {
+            setEnabled(selectionTableModel.getRowCount() >  0);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            memberTableModel.addMembersAtBeginning(selectionTableModel.getSelection());
+        }
+
+        public void tableChanged(TableModelEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    class AddSelectedAtEndAction extends AbstractAction implements TableModelListener {
+        public AddSelectedAtEndAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Add all primitives selected in the current dataset after the last member"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copyendright"));
+            //putValue(NAME, tr("Add Selected"));
+            refreshEnabled();
+        }
+
+        protected void refreshEnabled() {
+            setEnabled(selectionTableModel.getRowCount() >  0);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            memberTableModel.addMembersAtEnd(selectionTableModel.getSelection());
+        }
+
+        public void tableChanged(TableModelEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    class AddSelectedBeforeSelection extends AbstractAction implements TableModelListener, ListSelectionListener {
+        public AddSelectedBeforeSelection() {
+            putValue(SHORT_DESCRIPTION,  tr("Add all primitives selected in the current dataset before the first selected member"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copybeforecurrentright"));
+            //putValue(NAME, tr("Add Selected"));
+            refreshEnabled();
+        }
+
+        protected void refreshEnabled() {
+            setEnabled(selectionTableModel.getRowCount() >  0
+                    && memberTableModel.getSelectionModel().getMinSelectionIndex() >= 0);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            memberTableModel.addMembersBeforeIdx(selectionTableModel.getSelection(), memberTableModel.getSelectionModel().getMinSelectionIndex());
+        }
+
+        public void tableChanged(TableModelEvent e) {
+            refreshEnabled();
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    class AddSelectedAfterSelection extends AbstractAction implements TableModelListener, ListSelectionListener {
+        public AddSelectedAfterSelection() {
+            putValue(SHORT_DESCRIPTION,  tr("Add all primitives selected in the current dataset after the last selected member"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs/conflict", "copyaftercurrentright"));
+            //putValue(NAME, tr("Add Selected"));
+            refreshEnabled();
+        }
+
+        protected void refreshEnabled() {
+            setEnabled(selectionTableModel.getRowCount() >  0
+                    && memberTableModel.getSelectionModel().getMinSelectionIndex() >= 0);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            memberTableModel.addMembersAfterIdx(selectionTableModel.getSelection(), memberTableModel.getSelectionModel().getMaxSelectionIndex());
+        }
+
+        public void tableChanged(TableModelEvent e) {
+            refreshEnabled();
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    class RemoveSelectedAction extends AbstractAction implements TableModelListener {
+        public RemoveSelectedAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Remove all currently selected objects from relation"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "removeselected"));
+            // putValue(NAME, tr("Remove Selected"));
+            Shortcut.registerShortcut("relationeditor:removeselected",
+                    tr("Relation Editor: Remove Selected"),
+                    KeyEvent.VK_S,
+                    Shortcut.GROUP_MNEMONIC);
+
+            DataSet ds = getLayer().data;
+            setEnabled(ds != null && !ds.getSelected().isEmpty());
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            memberTableModel.removeMembersReferringTo(selectionTableModel.getSelection());
+        }
+
+        public void tableChanged(TableModelEvent e) {
+            setEnabled(selectionTableModel.getRowCount() >0);
+        }
+    }
+
+    class MoveUpAction extends AbstractAction implements ListSelectionListener {
+        public MoveUpAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Move the currently selected members up"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "moveup"));
+            //putValue(NAME, tr("Move Up"));
+            Shortcut.registerShortcut("relationeditor:moveup",
+                    tr("Relation Editor: Move Up"),
+                    KeyEvent.VK_N,
+                    Shortcut.GROUP_MNEMONIC);
+            setEnabled(false);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            memberTableModel.moveUp(memberTable.getSelectedRows());
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            setEnabled(memberTableModel.canMoveUp(memberTable.getSelectedRows()));
+        }
+    }
+
+    class MoveDownAction extends AbstractAction implements ListSelectionListener {
+        public MoveDownAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Move the currently selected members down"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "movedown"));
+            //putValue(NAME, tr("Move Down"));
+            Shortcut.registerShortcut("relationeditor:moveup",
+                    tr("Relation Editor: Move Down"),
+                    KeyEvent.VK_J,
+                    Shortcut.GROUP_MNEMONIC);
+            setEnabled(false);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            memberTableModel.moveDown(memberTable.getSelectedRows());
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            setEnabled(memberTableModel.canMoveDown(memberTable.getSelectedRows()));
+        }
+    }
+
+    class RemoveAction extends AbstractAction implements ListSelectionListener {
+        public RemoveAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Remove the member in the current table row from this relation"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "remove"));
+            //putValue(NAME, tr("Remove"));
+            Shortcut.registerShortcut("relationeditor:remove",
+                    tr("Relation Editor: Remove"),
+                    KeyEvent.VK_J,
+                    Shortcut.GROUP_MNEMONIC);
+            setEnabled(false);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            memberTableModel.remove(memberTable.getSelectedRows());
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            setEnabled(memberTableModel.canRemove(memberTable.getSelectedRows()));
+        }
+    }
+
+    class OKAction extends AbstractAction {
+        public OKAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Apply the updates and close the dialog"));
+            putValue(SMALL_ICON, ImageProvider.get("ok"));
+            putValue(NAME, tr("Apply"));
+            setEnabled(true);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            applyChanges();
+            setVisible(false);
+        }
+    }
+
+    class CancelAction extends AbstractAction {
+        public CancelAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Cancel the updates and close the dialog"));
+            putValue(SMALL_ICON, ImageProvider.get("cancel"));
+            putValue(NAME, tr("Cancel"));
+
+            getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
+            .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
+            getRootPane().getActionMap().put("ESCAPE", this);
+            setEnabled(true);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            setVisible(false);
+        }
+    }
+
+    class AddTagAction extends AbstractAction {
+        public AddTagAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Add an empty tag"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs","add"));
+            //putValue(NAME, tr("Cancel"));
+            setEnabled(true);
+        }
+        public void actionPerformed(ActionEvent e) {
+            tagEditorModel.appendNewTag();
+        }
+    }
+
+    class DeleteTagAction extends AbstractAction implements ListSelectionListener{
+        public DeleteTagAction() {
+            putValue(SHORT_DESCRIPTION,  tr("Delete the currently selected tags"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs","delete"));
+            //putValue(NAME, tr("Cancel"));
+            refreshEnabled();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            run();
+        }
+
+        /**
+         * delete a selection of tag names
+         */
+        protected void deleteTagNames() {
+            int[] rows = tagTable.getSelectedRows();
+            tagEditorModel.deleteTagNames(rows);
+        }
+
+        /**
+         * delete a selection of tag values
+         */
+        protected void deleteTagValues() {
+            int[] rows = tagTable.getSelectedRows();
+            tagEditorModel.deleteTagValues(rows);
+        }
+
+        /**
+         * delete a selection of tags
+         */
+        protected void deleteTags() {
+            tagEditorModel.deleteTags(tagTable.getSelectedRows());
+        }
+
+        public void run() {
+            if (!isEnabled())
+                return;
+            if (tagTable.getSelectedColumnCount() == 1) {
+                if (tagTable.getSelectedColumn() == 0) {
+                    deleteTagNames();
+                } else if (tagTable.getSelectedColumn() == 1) {
+                    deleteTagValues();
+                } else
+                    // should not happen
+                    //
+                    throw new IllegalStateException("unexpected selected clolumn: getSelectedColumn() is " + tagTable.getSelectedColumn());
+            } else if (tagTable.getSelectedColumnCount() == 2) {
+                deleteTags();
             }
-            OsmPrimitive primitive = ds.getPrimitiveById(member.member.id);
-            if (primitive != null) {
-                member.member = primitive;
+            if (tagEditorModel.getRowCount() == 0) {
+                tagEditorModel.ensureOneTag();
             }
         }
-    }
-
-    private void refreshTables() {
-        // re-load property data
-        int numLinked = 0;
-
-        // re-load membership data
-
-        memberData.setRowCount(0);
-        for (int i=0; i<getClone().members.size(); i++) {
-
-            // this whole section is aimed at finding out whether the
-            // relation member is "linked" with the next, i.e. whether
-            // (if both are ways) these ways are connected. It should
-            // really produce a much more beautiful output (with a linkage
-            // symbol somehow places between the two member lines!), and
-            // it should cache results, so... FIXME ;-)
-
-            RelationMember em = getClone().members.get(i);
-            WayConnectionType link = WayConnectionType.none;
-            RelationMember m = em;
-            RelationMember way1 = null;
-            RelationMember way2 = null;
-            int depth = 0;
-
-            while (m != null && depth < 10) {
-                if (m.member instanceof Way) {
-                    way1 = m;
-                    break;
-                } else if (m.member instanceof Relation) {
-                    if (m.member == this.getRelation()) {
-                        break;
-                    }
-                    m = ((Relation)m.member).lastMember();
-                    depth++;
-                } else {
-                    break;
-                }
-            }
-            if (way1 != null) {
-                int next = (i+1) % getClone().members.size();
-                while (next != i) {
-                    m = getClone().members.get(next);
-                    next = (next + 1) % getClone().members.size();
-                    depth = 0;
-                    while (m != null && depth < 10) {
-                        if (m.member instanceof Way) {
-                            way2 = m;
-                            break;
-                        } else if (m.member instanceof Relation) {
-                            if (m.member == this.getRelation()) {
-                                break;
-                            }
-                            m = ((Relation)(m.member)).firstMember();
-                            depth++;
-                        } else {
-                            break;
+
+        protected void refreshEnabled() {
+            setEnabled(tagTable.getSelectedRowCount()>0 || tagTable.getSelectedColumnCount() > 0);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    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"));
+            putValue(NAME, tr("Download Members"));
+            Shortcut.registerShortcut("relationeditor:downloadincomplete",
+                    tr("Relation Editor: Download Members"),
+                    KeyEvent.VK_K,
+                    Shortcut.GROUP_MNEMONIC);
+            setEnabled(true);
+        }
+        public void actionPerformed(ActionEvent e) {
+            downloadRelationMembers();
+        }
+    }
+
+    class DownloadTask extends PleaseWaitRunnable {
+        private boolean cancelled;
+        private Exception lastException;
+
+        protected void setIndeterminateEnabled(final boolean enabled) {
+            EventQueue.invokeLater(
+                    new Runnable() {
+                        public void run() {
+                            Main.pleaseWaitDlg.setIndeterminate(enabled);
                         }
                     }
-                    if (way2 != null) {
-                        break;
+            );
+        }
+
+        public DownloadTask() {
+            super(tr("Download relation members"), false /* don't ignore exception */);
+        }
+        @Override
+        protected void cancel() {
+            cancelled = true;
+            OsmApi.getOsmApi().cancel();
+        }
+
+        protected void showLastException() {
+            String msg = lastException.getMessage();
+            if (msg == null) {
+                msg = lastException.toString();
+            }
+            JOptionPane.showMessageDialog(
+                    null,
+                    msg,
+                    tr("Error"),
+                    JOptionPane.ERROR_MESSAGE
+            );
+        }
+
+        @Override
+        protected void finish() {
+            if (cancelled) return;
+            memberTableModel.updateMemberReferences(getLayer().data);
+            if (lastException == null) return;
+            showLastException();
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            try {
+                Main.pleaseWaitDlg.setAlwaysOnTop(true);
+                Main.pleaseWaitDlg.toFront();
+                setIndeterminateEnabled(true);
+                OsmServerObjectReader reader = new OsmServerObjectReader(getRelation().id, OsmPrimitiveType.RELATION, true);
+                DataSet dataSet = reader.parseOsm();
+                if (dataSet != null) {
+                    final MergeVisitor visitor = new MergeVisitor(getLayer().data, dataSet);
+                    visitor.merge();
+
+                    // copy the merged layer's data source info
+                    for (DataSource src : dataSet.dataSources) {
+                        getLayer().data.dataSources.add(src);
                     }
+                    getLayer().fireDataChange();
+
+                    if (visitor.getConflicts().isEmpty())
+                        return;
+                    getLayer().getConflicts().add(visitor.getConflicts());
+                    JOptionPane op = new JOptionPane(
+                            tr("There were {0} conflicts during import.",
+                                    visitor.getConflicts().size()),
+                                    JOptionPane.WARNING_MESSAGE
+                    );
+                    JDialog dialog = op.createDialog(Main.pleaseWaitDlg, tr("Conflicts in data"));
+                    dialog.setAlwaysOnTop(true);
+                    dialog.setModal(true);
+                    dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                    dialog.setVisible(true);
                 }
+            } catch(Exception e) {
+                if (cancelled) {
+                    System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e.toString()));
+                    return;
+                }
+                lastException = e;
+            } finally {
+                Main.pleaseWaitDlg.setAlwaysOnTop(false);
+                setIndeterminateEnabled(false);
             }
-            if (way2 != null) {
-                Node way1first = ((Way)(way1.member)).firstNode();
-                Node way1last = ((Way)(way1.member)).lastNode();
-                Node way2first = ((Way)(way2.member)).firstNode();
-                Node way2last = ((Way)(way2.member)).lastNode();
-                /*
-                if (way1.role.equals("forward")) {
-                    way1first = null;
-                } else if (way1.role.equals("backward")) {
-                    way1last = null;
-                }
-                if (way2.role.equals("forward")) {
-                    way2last = null;
-                } else if (way2.role.equals("backward")) {
-                    way2first = null;
-                }
-                 */
-                if (way1first != null && way2first != null && way1first.equals(way2first)) {
-                    link = WayConnectionType.tail_to_tail;
-                } else if (way1first != null && way2last != null && way1first.equals(way2last)) {
-                    link = WayConnectionType.tail_to_head;
-                } else if (way1last != null && way2first != null && way1last.equals(way2first)) {
-                    link = WayConnectionType.head_to_tail;
-                } else if (way1last != null && way2last != null && way1last.equals(way2last)) {
-                    link = WayConnectionType.head_to_head;
-                }
-
-                // end of section to determine linkedness.
-                if (link != WayConnectionType.none)
-                {
-                    ++numLinked;
-                }
-
-            }
-            memberData.addRow(new Object[]{em.role, em.member, link});
-        }
-        status.setText(tr("Members: {0} (linked: {1})", getClone().members.size(), numLinked));
-    }
-
-    private SideButton createButton(String name, String iconName, String tooltip, int mnemonic, ActionListener actionListener) {
-        return
-        new SideButton(name, iconName, "relationEditor",
-                tooltip,
-                Shortcut.registerShortcut("relationeditor:"+iconName,
-                        tr("Relation Editor: {0}", name == null ? tooltip : name),
-                        mnemonic,
-                        Shortcut.GROUP_MNEMONIC),
-                        actionListener
-        );
-    }
-
-    private void addSelected() {
-        for (OsmPrimitive p : Main.ds.getSelected()) {
-            // ordered relations may have the same member multiple times.
-            // TODO: visual indication of the fact that one is there more than once?
-            RelationMember em = new RelationMember();
-            em.member = p;
-            em.role = "";
-            // when working with ordered relations, we make an effort to
-            // add the element before the first selected member.
-            int[] rows = memberTable.getSelectedRows();
-            if (rows.length > 0) {
-                getClone().members.add(rows[0], em);
-            } else {
-                getClone().members.add(em);
-            }
-        }
-        refreshTables();
-    }
-
-    private void deleteSelected() {
-        for (OsmPrimitive p : Main.ds.getSelected()) {
-            Relation c = new Relation(getClone());
-            for (RelationMember rm : c.members) {
-                if (rm.member == p)
-                {
-                    RelationMember mem = new RelationMember();
-                    mem.role = rm.role;
-                    mem.member = rm.member;
-                    getClone().members.remove(mem);
-                }
-            }
-        }
-        refreshTables();
-    }
-
-    private void moveMembers(int direction) {
-        int[] rows = memberTable.getSelectedRows();
-        if (rows.length == 0) return;
-
-        // check if user attempted to move anything beyond the boundary of the list
-        if (rows[0] + direction < 0) return;
-        if (rows[rows.length-1] + direction >= getClone().members.size()) return;
-
-        RelationMember m[] = new RelationMember[getClone().members.size()];
-
-        // first move all selected rows from the member list into a new array,
-        // displaced by the move amount
-        for (Integer i: rows) {
-            m[i+direction] = getClone().members.get(i);
-            getClone().members.set(i, null);
-        }
-
-        // now fill the empty spots in the destination array with the remaining
-        // elements.
-        int i = 0;
-        for (RelationMember rm : getClone().members) {
-            if (rm != null) {
-                while (m[i] != null) {
-                    i++;
-                }
-                m[i++] = rm;
-            }
-        }
-
-        // and write the array back into the member list.
-        getClone().members.clear();
-        getClone().members.addAll(Arrays.asList(m));
-        refreshTables();
-        ListSelectionModel lsm = memberTable.getSelectionModel();
-        lsm.setValueIsAdjusting(true);
-        for (Integer j: rows) {
-            lsm.addSelectionInterval(j + direction, j + direction);
-        }
-        lsm.setValueIsAdjusting(false);
-    }
-
-
-    /**
-     * Asynchronously download the members of the currently edited relation
-     *
-     */
-    private void downloadRelationMembers() {
-
-        class DownloadTask extends PleaseWaitRunnable {
-            private boolean cancelled;
-            private Exception lastException;
-
-            protected void setIndeterminateEnabled(final boolean enabled) {
-                EventQueue.invokeLater(
-                        new Runnable() {
-                            public void run() {
-                                Main.pleaseWaitDlg.setIndeterminate(enabled);
-                            }
-                        }
-                );
-            }
-
-            public DownloadTask() {
-                super(tr("Download relation members"), false /* don't ignore exception */);
-            }
-            @Override
-            protected void cancel() {
-                cancelled = true;
-                OsmApi.getOsmApi().cancel();
-            }
-
-            protected void showLastException() {
-                String msg = lastException.getMessage();
-                if (msg == null) {
-                    msg = lastException.toString();
-                }
-                JOptionPane.showMessageDialog(
-                        null,
-                        msg,
-                        tr("Error"),
-                        JOptionPane.ERROR_MESSAGE
-                );
-            }
-
-            @Override
-            protected void finish() {
-                if (cancelled) return;
-                updateMemberReferencesInClone();
-                refreshTables();
-                if (lastException == null) return;
-                showLastException();
-            }
-
-            @Override
-            protected void realRun() throws SAXException, IOException, OsmTransferException {
-                try {
-                    Main.pleaseWaitDlg.setAlwaysOnTop(true);
-                    Main.pleaseWaitDlg.toFront();
-                    setIndeterminateEnabled(true);
-                    OsmServerObjectReader reader = new OsmServerObjectReader(getClone().id, OsmPrimitiveType.RELATION, true);
-                    DataSet dataSet = reader.parseOsm();
-                    if (dataSet != null) {
-                        final MergeVisitor visitor = new MergeVisitor(getLayer().data, dataSet);
-                        visitor.merge();
-
-                        // copy the merged layer's data source info
-                        for (DataSource src : dataSet.dataSources) {
-                            getLayer().data.dataSources.add(src);
-                        }
-                        getLayer().fireDataChange();
-
-                        if (visitor.getConflicts().isEmpty())
-                            return;
-                        getLayer().getConflicts().add(visitor.getConflicts());
-                        JOptionPane op = new JOptionPane(
-                                tr("There were {0} conflicts during import.",
-                                        visitor.getConflicts().size()),
-                                        JOptionPane.WARNING_MESSAGE
-                        );
-                        JDialog dialog = op.createDialog(Main.pleaseWaitDlg, tr("Conflicts in data"));
-                        dialog.setAlwaysOnTop(true);
-                        dialog.setModal(true);
-                        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
-                        dialog.setVisible(true);
-                    }
-                } catch(Exception e) {
-                    if (cancelled) {
-                        System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e.toString()));
-                        return;
-                    }
-                    lastException = e;
-                } finally {
-                    Main.pleaseWaitDlg.setAlwaysOnTop(false);
-                    setIndeterminateEnabled(false);
-                }
-            }
-        }
-
-        boolean download = false;
-        for (RelationMember member : getClone().members) {
-            if (member.member.incomplete) {
-                download = true;
-                break;
-            }
-        }
-        if (! download) return;
-        Main.worker.submit(new DownloadTask());
-    }
-
-    @Override
-    public void setVisible(boolean visible) {
-        super.setVisible(visible);
-        if (!visible) {
-            dispose();
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java	(revision 1790)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java	(revision 1790)
@@ -0,0 +1,128 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JComponent;
+import javax.swing.JTable;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
+import javax.swing.table.TableColumnModel;
+
+public class MemberTable extends JTable {
+
+    /**
+     * constructor
+     * 
+     * @param model
+     * @param columnModel
+     */
+    public MemberTable(MemberTableModel model) {
+        super(model, new MemberTableColumnModel(), model.getSelectionModel());
+        init();
+    }
+
+    /**
+     * initialize the table
+     */
+    protected void init() {
+        setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+        setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+        putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
+
+        // make ENTER behave like TAB
+        //
+        getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+        .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "selectNextColumnCell");
+
+        // install custom navigation actions
+        //
+        getActionMap().put("selectNextColumnCell", new SelectNextColumnCellAction());
+        getActionMap().put("selectPreviousColumnCell", new SelectPreviousColumnCellAction());
+    }
+
+    /**
+     * adjusts the width of the columns for the tag name and the tag value
+     * to the width of the scroll panes viewport.
+     * 
+     * Note: {@see #getPreferredScrollableViewportSize()} did not work as expected
+     * 
+     * @param scrollPaneWidth the width of the scroll panes viewport
+     */
+    public void adjustColumnWidth(int scrollPaneWidth) {
+        TableColumnModel tcm = getColumnModel();
+        int width = scrollPaneWidth;
+        width = width / 3;
+        if (width > 0) {
+            tcm.getColumn(0).setMinWidth(width);
+            tcm.getColumn(0).setMaxWidth(width);
+            tcm.getColumn(1).setMinWidth(width);
+            tcm.getColumn(1).setMaxWidth(width);
+            tcm.getColumn(2).setMinWidth(width);
+            tcm.getColumn(2).setMaxWidth(width);
+
+        }
+    }
+
+    /**
+     * Action to be run when the user navigates to the next cell in the table,
+     * for instance by pressing TAB or ENTER. The action alters the standard
+     * navigation path from cell to cell:
+     * <ul>
+     *   <li>it jumps over cells in the first column</li>
+     *   <li>it automatically add a new empty row when the user leaves the
+     *   last cell in the table</li>
+     * <ul>
+     *
+     *
+     */
+    class SelectNextColumnCellAction extends AbstractAction  {
+        public void actionPerformed(ActionEvent e) {
+            run();
+        }
+
+        public void run() {
+            int col = getSelectedColumn();
+            int row = getSelectedRow();
+            if (getCellEditor() != null) {
+                getCellEditor().stopCellEditing();
+            }
+
+            if (col == 0 && row < getRowCount() - 1) {
+                row++;
+            } else if (row < getRowCount()-1) {
+                col=0;
+                row++;
+            }
+            changeSelection(row, col, false, false);
+        }
+    }
+
+
+    /**
+     * Action to be run when the user navigates to the previous cell in the table,
+     * for instance by pressing Shift-TAB
+     *
+     */
+    class SelectPreviousColumnCellAction extends AbstractAction  {
+
+        public void actionPerformed(ActionEvent e) {
+            int col = getSelectedColumn();
+            int row = getSelectedRow();
+            if (getCellEditor() != null) {
+                getCellEditor().stopCellEditing();
+            }
+
+
+            if (col == 0 && row == 0) {
+                // change nothing
+            } else if (row > 0) {
+                col = 0;
+                row--;
+            }
+            changeSelection(row, col, false, false);
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableCellRenderer.java	(revision 1790)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableCellRenderer.java	(revision 1790)
@@ -0,0 +1,163 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.border.Border;
+import javax.swing.table.TableCellRenderer;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * This is the {@see TableCellRenderer} used in the tables of {@see RelationMemberMerger}.
+ * 
+ */
+public  class MemberTableCellRenderer extends JLabel implements TableCellRenderer {
+    private final static DecimalFormat COORD_FORMATTER = new DecimalFormat("###0.0000");
+    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_NOT_IN_OPPOSITE = new Color(255,197,197);
+    public final static Color BGCOLOR_IN_OPPOSITE = new Color(255,234,213);
+    public final static Color BGCOLOR_SAME_POSITION_IN_OPPOSITE = new Color(217,255,217);
+
+    public final static Color BGCOLOR_PARTICIPAING_IN_COMPARISON = Color.BLACK;
+    public final static Color FGCOLOR_PARTICIPAING_IN_COMPARISON = Color.WHITE;
+
+    public final static Color BGCOLOR_FROZEN = new Color(234,234,234);
+
+    private HashMap<OsmPrimitiveType, ImageIcon>  icons;
+    private  Border rowNumberBorder = null;
+
+    /**
+     * 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
+     */
+    public MemberTableCellRenderer() {
+        setIcon(null);
+        setOpaque(true);
+        loadIcons();
+    }
+
+    public String buildToolTipText(OsmPrimitive primitive) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<html>");
+        sb.append("<strong>id</strong>=")
+        .append(primitive.id)
+        .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);
+        setBorder(null);
+        setIcon(null);
+        setToolTipText(null);
+    }
+
+
+    protected void renderBackground( boolean isSelected) {
+        Color bgc = Color.WHITE;
+        if (isSelected) {
+            bgc = BGCOLOR_SELECTED;
+        }
+        setBackground(bgc);
+    }
+
+    protected void renderForeground(boolean isSelected) {
+        Color fgc = Color.BLACK;
+        setForeground(fgc);
+    }
+
+    protected void renderPrimitive(OsmPrimitive primitive) {
+        NameVisitor visitor = new NameVisitor();
+        primitive.visit(visitor);
+        setIcon(icons.get(OsmPrimitiveType.from(primitive)));
+        setText(visitor.name);
+        setToolTipText(buildToolTipText(primitive));
+    }
+
+    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
+            int row, int column) {
+
+        reset();
+        renderBackground(isSelected);
+        renderForeground(isSelected);
+        switch(column) {
+        case 0:
+            String role = (String)value;
+            setText(role);
+            break;
+        case 1:
+            OsmPrimitive primitive = (OsmPrimitive)value;
+            renderPrimitive(primitive);
+            break;
+        case 2:
+            setText("");
+            break;
+        default:
+            // should not happen
+        }
+        return this;
+    }
+
+    /**
+     * replies the model
+     * @param table  the table
+     * @return the table model
+     */
+    protected MemberTableModel getModel(JTable table) {
+        return (MemberTableModel)table.getModel();
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableColumnModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableColumnModel.java	(revision 1790)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableColumnModel.java	(revision 1790)
@@ -0,0 +1,39 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.TableColumn;
+
+import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
+
+public class MemberTableColumnModel extends DefaultTableColumnModel{
+
+    public MemberTableColumnModel() {
+        TableColumn col = null;
+        MemberTableCellRenderer renderer = new MemberTableCellRenderer();
+
+        // column 0 - the member role
+        col = new TableColumn(0);
+        col.setHeaderValue(tr("Role"));
+        col.setResizable(true);
+        col.setCellRenderer(renderer);
+        addColumn(col);
+
+        // column 1 - the member
+        col = new TableColumn(1);
+        col.setHeaderValue(tr("Refers to"));
+        col.setResizable(true);
+        col.setCellRenderer(new OsmPrimitivRenderer());
+        col.setCellRenderer(renderer);
+        addColumn(col);
+
+        // column 2 -
+        col = new TableColumn(2);
+        col.setHeaderValue(tr("Linked"));
+        col.setResizable(true);
+        col.setCellRenderer(renderer);
+        addColumn(col);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 1790)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 1790)
@@ -0,0 +1,279 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.table.AbstractTableModel;
+
+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.data.osm.RelationMember;
+
+public class MemberTableModel extends AbstractTableModel{
+
+    private Relation relation;
+    private ArrayList<RelationMember> members;
+    private ArrayList<String> memberLinkingInfo;
+    private DefaultListSelectionModel listSelectionModel;
+
+    /**
+     * constructor
+     */
+    public MemberTableModel(){
+        members = new ArrayList<RelationMember>();
+        memberLinkingInfo = new ArrayList<String>();
+    }
+
+    public void populate(Relation relation) {
+        members.clear();
+        if (relation != null && relation.members != null) {
+            members.addAll(relation.members);
+        }
+        this.relation = relation;
+        fireTableDataChanged();
+    }
+
+    public int getColumnCount() {
+        return 3;
+    }
+
+    public int getRowCount() {
+        return members.size();
+    }
+
+    public Object getValueAt(int rowIndex, int columnIndex) {
+        switch(columnIndex) {
+        case 0: return members.get(rowIndex).role;
+        case 1: return members.get(rowIndex).member;
+        case 2: return "";
+        }
+        // should not happen
+        return null;
+    }
+
+    @Override
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+        return columnIndex == 0;
+    }
+
+    @Override
+    public void setValueAt(Object value, int rowIndex, int columnIndex) {
+        RelationMember member = members.get(rowIndex);
+        member.role = value.toString();
+    }
+
+
+    public OsmPrimitive getReferredPrimitive(int idx) {
+        return members.get(idx).member;
+    }
+
+    public void moveUp(int[] selectedRows) {
+        if (! canMoveUp(selectedRows))
+            return;
+
+        for (int row : selectedRows) {
+            RelationMember member1 = members.get(row);
+            RelationMember member2 = members.get(row-1);
+            members.set(row, member2);
+            members.set(row-1, member1);
+        }
+        fireTableDataChanged();
+        listSelectionModel.clearSelection();
+        for (int row : selectedRows) {
+            row--;
+            listSelectionModel.addSelectionInterval(row, row);
+        }
+    }
+
+    public void moveDown(int[] selectedRows) {
+        if (! canMoveDown(selectedRows))
+            return;
+
+        for (int i=selectedRows.length-1; i >=0; i--) {
+            int row = selectedRows[i];
+            RelationMember member1 = members.get(row);
+            RelationMember member2 = members.get(row+1);
+            members.set(row, member2);
+            members.set(row+1, member1);
+        }
+        fireTableDataChanged();
+        listSelectionModel.clearSelection();
+        for (int row : selectedRows) {
+            row++;
+            listSelectionModel.addSelectionInterval(row, row);
+        }
+    }
+
+    public void remove(int[] selectedRows) {
+        if (! canRemove(selectedRows))
+            return;
+        int offset = 0;
+        for (int row : selectedRows) {
+            row -= offset;
+            members.remove(row);
+            offset++;
+        }
+        fireTableDataChanged();
+    }
+
+    public boolean canMoveUp(int [] rows) {
+        if (rows == null || rows.length == 0) return false;
+        Arrays.sort(rows);
+        return rows[0] > 0 && members.size() > 0;
+    }
+
+    public boolean canMoveDown(int [] rows) {
+        if (rows == null || rows.length == 0) return false;
+        Arrays.sort(rows);
+        return members.size() >0 && rows[rows.length-1] < members.size() - 1;
+    }
+
+    public boolean canRemove(int [] rows) {
+        if (rows == null || rows.length == 0) return false;
+        return true;
+    }
+
+    public DefaultListSelectionModel getSelectionModel() {
+        if (listSelectionModel == null) {
+            listSelectionModel = new DefaultListSelectionModel();
+            listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+        }
+        return listSelectionModel;
+    }
+
+    public void updateMemberReferences(DataSet ds) {
+        for (RelationMember member : members) {
+            if (member.member.id == 0) {
+                continue;
+            }
+            OsmPrimitive primitive = ds.getPrimitiveById(member.member.id);
+            if (primitive != null) {
+                member.member = primitive;
+            }
+        }
+        fireTableDataChanged();
+    }
+
+    public void removeMembersReferringTo(List<? extends OsmPrimitive> primitives) {
+        if (primitives == null) return;
+        Iterator<RelationMember> it = members.iterator();
+        while(it.hasNext()) {
+            RelationMember member = it.next();
+            if (primitives.contains(member.member)) {
+                it.remove();
+            }
+        }
+        fireTableDataChanged();
+    }
+
+    public void selectMembers(Collection<RelationMember> selectedMembers) {
+        if (selectedMembers == null) return;
+        int min = Integer.MAX_VALUE;
+        for (RelationMember member: selectedMembers) {
+            int row = members.indexOf(member);
+            if (row >= 0) {
+                listSelectionModel.addSelectionInterval(row,row);
+                min = Math.min(row, min);
+            }
+        }
+        if (min < Integer.MAX_VALUE) {
+            //FIXME: scroll to min
+        }
+    }
+
+    public void applyToRelation(Relation relation) {
+        relation.members.clear();
+        relation.members.addAll(members);
+    }
+
+    public boolean hasSameMembersAs(Relation relation) {
+        if (relation == null) return false;
+        if (relation.members.size() != members.size()) return false;
+        for (int i=0; i<relation.members.size();i++) {
+            if (! relation.members.get(i).equals(members.get(i)))
+                return false;
+        }
+        return true;
+    }
+
+    public boolean hasIncompleteMembers() {
+        for (RelationMember member: members) {
+            if (member.member.incomplete)
+                return true;
+        }
+        return false;
+    }
+
+    protected List<Integer> getSelectedIndices() {
+        ArrayList<Integer> selectedIndices = new ArrayList<Integer>();
+        for (int i=0; i< members.size();i++) {
+            if (getSelectionModel().isSelectedIndex(i)) {
+                selectedIndices.add(i);
+            }
+        }
+        return selectedIndices;
+    }
+
+    public void addMembersAtBeginning(List<? extends OsmPrimitive> primitives) {
+        if (primitives == null) return;
+        for (OsmPrimitive primitive: primitives) {
+            RelationMember member = new RelationMember(null,primitive);
+            members.add(0,member);
+        }
+        fireTableDataChanged();
+        getSelectionModel().clearSelection();
+        for (int i=0; i<primitives.size();i++) {
+            getSelectionModel().addSelectionInterval(i,i);
+        }
+    }
+
+    public void addMembersAtEnd(List<? extends OsmPrimitive> primitives) {
+        if (primitives == null) return;
+
+        for (OsmPrimitive primitive: primitives) {
+            RelationMember member = new RelationMember(null,primitive);
+            members.add(member);
+        }
+        fireTableDataChanged();
+        getSelectionModel().clearSelection();
+        for (int i=0; i<primitives.size();i++) {
+            getSelectionModel().addSelectionInterval(members.size()-1-i,members.size()-1-i);
+        }
+    }
+
+    public void addMembersBeforeIdx(List<? extends OsmPrimitive> primitives, int idx) {
+        if (primitives == null) return;
+
+        for (OsmPrimitive primitive: primitives) {
+            RelationMember member = new RelationMember(null,primitive);
+            members.add(idx,member);
+        }
+        fireTableDataChanged();
+        getSelectionModel().clearSelection();
+        for (int i=0; i<primitives.size();i++) {
+            getSelectionModel().addSelectionInterval(idx+i,idx+i);
+        }
+    }
+
+    public void addMembersAfterIdx(List<? extends OsmPrimitive> primitives, int idx) {
+        if (primitives == null) return;
+        int j =1;
+        for (OsmPrimitive primitive: primitives) {
+            RelationMember member = new RelationMember(null,primitive);
+            members.add(idx+j,member);
+            j++;
+        }
+        fireTableDataChanged();
+        getSelectionModel().clearSelection();
+        for (int i=0; i<primitives.size();i++) {
+            getSelectionModel().addSelectionInterval(idx+1 + i,idx+1 +i);
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java	(revision 1790)
@@ -41,5 +41,4 @@
      */
     private Relation relation;
-    private Relation clone;
 
     /** the data layer the relation belongs to */
@@ -103,12 +102,4 @@
         this.relation = relation;
         this.layer = layer;
-
-        if (relation == null) {
-            // create a new relation
-            this.clone = new Relation();
-        } else {
-            // edit an existing relation
-            this.clone = new Relation(relation);
-        }
     }
 
@@ -117,8 +108,4 @@
     }
 
-    protected Relation getClone() {
-        return clone;
-    }
-
     protected OsmDataLayer getLayer() {
         return layer;
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableColumnModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableColumnModel.java	(revision 1790)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableColumnModel.java	(revision 1790)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.TableColumn;
+
+import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
+
+public class SelectionTableColumnModel  extends DefaultTableColumnModel {
+    public SelectionTableColumnModel() {
+        TableColumn col = null;
+        OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
+
+        // column 0 - the member role
+        col = new TableColumn(0);
+        col.setHeaderValue(tr("Selection"));
+        col.setResizable(true);
+        col.setCellRenderer(renderer);
+        addColumn(col);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableModel.java	(revision 1790)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableModel.java	(revision 1790)
@@ -0,0 +1,86 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
+
+public class SelectionTableModel extends AbstractTableModel implements SelectionChangedListener, LayerChangeListener{
+
+    /** this selection table model only displays selected primitives in this layer */
+    private OsmDataLayer layer;
+    private ArrayList<OsmPrimitive> cache;
+
+    public SelectionTableModel(OsmDataLayer layer) {
+        this.layer = layer;
+        cache = new ArrayList<OsmPrimitive>();
+        DataSet.selListeners.add(this);
+        Layer.listeners.add(this);
+    }
+
+
+    public void unregister() {
+        DataSet.selListeners.remove(this);
+        Layer.listeners.remove(this);
+    }
+
+    public int getColumnCount() {
+        return 1;
+    }
+
+    public int getRowCount() {
+        if (Main.map.mapView.getEditLayer() != layer)
+            return 0;
+        return cache.size();
+    }
+
+    public Object getValueAt(int rowIndex, int columnIndex) {
+        return cache.get(rowIndex);
+    }
+
+    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
+        if (oldLayer  == layer) {
+            cache.clear();
+        }
+        if (newLayer == layer) {
+            cache.addAll(((OsmDataLayer)newLayer).data.getSelected());
+        }
+        fireTableDataChanged();
+    }
+
+    public void layerAdded(Layer newLayer) {
+        // do nothing
+    }
+
+    public void layerRemoved(Layer oldLayer) {
+        if (oldLayer == layer) {
+            unregister();
+        }
+        this.cache.clear();
+        fireTableDataChanged();
+    }
+
+    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+        if (layer == Main.map.mapView.getEditLayer()) {
+            cache.clear();
+            cache.addAll(newSelection);
+        } else {
+            cache.clear();
+        }
+        fireTableDataChanged();
+    }
+
+    public List<? extends OsmPrimitive> getSelection() {
+        return cache;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/TagEditorModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/TagEditorModel.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/TagEditorModel.java	(revision 1790)
@@ -9,4 +9,5 @@
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.logging.Logger;
@@ -285,10 +286,23 @@
             add(key,value);
         }
+        TagModel tag = new TagModel();
         sort();
+        tags.add(tag);
         setDirty(false);
     }
 
 
+    /**
+     * applies the current state of the tag editor model to a primitive
+     * 
+     * @param primitive the primitive
+     * 
+     */
     public void applyToPrimitive(OsmPrimitive primitive) {
+        if (primitive.keys == null) {
+            primitive.keys = new HashMap<String, String>();
+        } else {
+            primitive.keys.clear();
+        }
         for (TagModel tag: tags) {
             // tag still holds an unchanged list of different values for the same key.
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/WayConnectionType.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/WayConnectionType.java	(revision 1790)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/WayConnectionType.java	(revision 1790)
@@ -0,0 +1,22 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+public enum WayConnectionType {
+
+    none (""),
+    head_to_head("-><-"),
+    tail_to_tail("->->"),
+    head_to_tail("<-<-"),
+    tail_to_head ("<-->");
+
+    private String textSymbol;
+
+    WayConnectionType(String textSymbol) {
+        this.textSymbol = textSymbol;
+    }
+
+    @Override
+    public String toString() {
+        return textSymbol;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 1790)
@@ -62,5 +62,4 @@
     private HashSet<Long> relations;
     private HashSet<Long> missingPrimitives;
-    private HashSet<Long> skippedWayIds;
     private DataSet outputDataSet;
 
@@ -320,9 +319,6 @@
         try {
             final OsmReader osm = OsmReader.parseDataSetOsm(in, Main.pleaseWaitDlg);
-            skippedWayIds.addAll(osm.getSkippedWayIds());
             merge(osm.getDs());
-        } catch(IOException e) {
-            throw new OsmTransferException(e);
-        } catch(SAXException e) {
+        } catch(Exception e) {
             throw new OsmTransferException(e);
         }
@@ -346,9 +342,6 @@
         try {
             final OsmReader osm = OsmReader.parseDataSetOsm(in,Main.pleaseWaitDlg);
-            skippedWayIds.addAll(osm.getSkippedWayIds());
             merge(osm.getDs());
-        } catch(IOException e) {
-            throw new OsmTransferException(e);
-        } catch(SAXException e) {
+        } catch(Exception e) {
             throw new OsmTransferException(e);
         }
@@ -440,5 +433,4 @@
     @Override
     public DataSet parseOsm() throws OsmTransferException {
-        skippedWayIds = new HashSet<Long>();
         missingPrimitives = new HashSet<Long>();
 
@@ -450,15 +442,4 @@
 
     /**
-     * replies the set of {@see Way}s which were present in the data fetched from the
-     * server but which were not included in the JOSM dataset because they referred
-     * to nodes not present in the dataset
-     * 
-     * @return  the set of ids of skipped ways
-     */
-    public Set<Long> getSkippedWays() {
-        return skippedWayIds;
-    }
-
-    /**
      * replies the set of ids of all primitives for which a fetch request to the
      * server was submitted but which are not available from the server (the server
Index: trunk/src/org/openstreetmap/josm/io/OsmImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmImporter.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/io/OsmImporter.java	(revision 1790)
@@ -51,14 +51,4 @@
         Main.main.addLayer(layer);
         layer.fireDataChange();
-        if (osm.getParseNotes().length() != 0) {
-            /* display at most five lines */
-            String notes = osm.getParseNotes();
-            int j = 0;
-            for (int i = 0; i < 5; i++) {
-                j = notes.indexOf('\n', j + 1);
-            }
-            j = j >= 0 ? j : notes.length();
-            JOptionPane.showMessageDialog(Main.parent, notes.substring(0, j));
-        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 1790)
@@ -16,9 +16,10 @@
 import java.util.Set;
 import java.util.Map.Entry;
-
+import java.util.logging.Logger;
+
+import javax.swing.SwingUtilities;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParserFactory;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -50,4 +51,5 @@
  */
 public class OsmReader {
+    static private final Logger logger = Logger.getLogger(OsmReader.class.getName());
 
     /**
@@ -56,19 +58,4 @@
     private DataSet ds = new DataSet();
     public DataSet getDs() { return ds; }
-
-    /**
-     * Record warnings.  If there were any data inconsistencies, append
-     * a newline-terminated string.
-     */
-    private String parseNotes = new String();
-    private int parseNotesCount = 0;
-    public String getParseNotes() {
-        return parseNotes;
-    }
-
-    /** the list of ids of skipped {@see Way}s, i.e. ways which referred to nodes
-     * not included in the parsed data
-     */
-    private Set<Long> skippedWayIds = new HashSet<Long>();
 
     /**
@@ -350,15 +337,9 @@
     protected void createWays() {
         for (Entry<OsmPrimitiveData, Collection<Long>> e : ways.entrySet()) {
-            Way w = new Way();
+            Way w = new Way(e.getKey().id);
             boolean failed = false;
             for (long id : e.getValue()) {
                 Node n = findNode(id);
                 if (n == null) {
-                    /* don't report ALL of them, just a few */
-                    if (parseNotesCount++ < 6) {
-                        parseNotes += tr("Skipping a way because it includes a node that doesn''t exist: {0}\n", id);
-                    } else if (parseNotesCount == 6) {
-                        parseNotes += "...\n";
-                    }
                     failed = true;
                     break;
@@ -367,11 +348,14 @@
             }
             if (failed) {
-                skippedWayIds.add(e.getKey().id);
-                continue;
-            }
-            e.getKey().copyTo(w);
-            adder.visit(w);
-        }
-
+                logger.warning(tr("marked way {0} incomplete because referred nodes are missing in the loaded data", e.getKey().id));
+                e.getKey().copyTo(w);
+                w.incomplete = true;
+                w.nodes.clear();
+            } else {
+                e.getKey().copyTo(w);
+                w.incomplete = false;
+                adder.visit(w);
+            }
+        }
     }
 
@@ -475,5 +459,5 @@
     }
 
-    public static OsmReader parseDataSetOsm(InputStream source,PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException {
+    public static OsmReader parseDataSetOsm(InputStream source, final PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException {
         OsmReader osm = new OsmReader();
 
@@ -487,6 +471,12 @@
         }
 
-        Main.pleaseWaitDlg.currentAction.setText(tr("Prepare OSM data..."));
-        Main.pleaseWaitDlg.setIndeterminate(true);
+        SwingUtilities.invokeLater(
+                new Runnable() {
+                    public void run() {
+                        pleaseWaitDlg.currentAction.setText(tr("Prepare OSM data..."));
+                        pleaseWaitDlg.setIndeterminate(true);
+                    }
+                }
+        );
 
         for (Node n : osm.nodes.values()) {
@@ -508,18 +498,14 @@
             }
 
-        Main.pleaseWaitDlg.setIndeterminate(false);
-        Main.pleaseWaitDlg.progress.setValue(0);
+        SwingUtilities.invokeLater(
+                new Runnable() {
+                    public void run() {
+                        pleaseWaitDlg.setIndeterminate(false);
+                        pleaseWaitDlg.progress.setValue(0);
+                    }
+                }
+        );
 
         return osm;
     }
-
-    /**
-     * replies a set of ids of skipped {@see Way}s, i.e. ways which were included in the downloaded
-     * data but which referred to nodes <strong>not</strong>  available in the downloaded data
-     * 
-     * @return the set of ids
-     */
-    public Set<Long> getSkippedWayIds() {
-        return skippedWayIds;
-    }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerHistoryReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerHistoryReader.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerHistoryReader.java	(revision 1790)
@@ -74,10 +74,4 @@
             HistoryDataSet data = reader.parse(Main.pleaseWaitDlg);
             return data;
-        } catch (IOException e) {
-            if (cancel)
-                return null;
-            throw new OsmTransferException(e);
-        } catch (SAXException e) {
-            throw new OsmTransferException(e);
         } catch(OsmTransferException e) {
             throw e;
Index: trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java	(revision 1790)
@@ -24,22 +24,14 @@
     @Override
     public DataSet parseOsm() throws OsmTransferException {
+        InputStream in = null;
         try {
             Main.pleaseWaitDlg.progress.setValue(0);
             Main.pleaseWaitDlg.currentAction.setText(tr("Contacting Server..."));
 
-            final InputStream in = getInputStreamRaw(url, Main.pleaseWaitDlg);
+            in = getInputStreamRaw(url, Main.pleaseWaitDlg);
             if (in == null)
                 return null;
             Main.pleaseWaitDlg.currentAction.setText(tr("Downloading OSM data..."));
-            final DataSet data = OsmReader.parseDataSet(in, Main.pleaseWaitDlg);
-            in.close();
-            activeConnection = null;
-            return data;
-        } catch (IOException e) {
-            if (cancel)
-                return null;
-            throw new OsmTransferException(e);
-        } catch (SAXException e) {
-            throw new OsmTransferException(e);
+            return OsmReader.parseDataSet(in, Main.pleaseWaitDlg);
         } catch(OsmTransferException e) {
             throw e;
@@ -48,4 +40,11 @@
                 return null;
             throw new OsmTransferException(e);
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+                activeConnection = null;
+            } catch(Exception e) {/* ignore it */}
         }
     }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java	(revision 1789)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java	(revision 1790)
@@ -6,6 +6,4 @@
 import java.io.IOException;
 import java.io.InputStream;
-
-import javax.swing.JOptionPane;
 
 import org.openstreetmap.josm.Main;
@@ -51,7 +49,4 @@
             final DataSet data = osm.getDs();
 
-            if (osm.getParseNotes().length() != 0) {
-                JOptionPane.showMessageDialog(Main.parent, osm.getParseNotes());
-            }
             in.close();
             activeConnection = null;
