Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java	(revision 1828)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java	(revision 1828)
@@ -0,0 +1,500 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dialog;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import java.util.logging.Logger;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.TreePath;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+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.visitor.MergeVisitor;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.PrimitiveNameFormatter;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmServerObjectReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.xml.sax.SAXException;
+
+/**
+ * ChildRelationBrowser is a UI component which provides a tree-like view on the hierarchical
+ * structure of relations
+ * 
+ *
+ */
+public class ChildRelationBrowser extends JPanel {
+    static private final Logger logger = Logger.getLogger(ChildRelationBrowser.class.getName());
+
+    /** the tree with relation children */
+    private RelationTree childTree;
+    /**  the tree model */
+    private RelationTreeModel model;
+
+    /** the osm data layer this browser is related to */
+    private OsmDataLayer layer;
+
+    /**
+     * Replies the {@see OsmDataLayer} this editor is related to
+     * 
+     * @return the osm data layer
+     */
+    protected OsmDataLayer getLayer() {
+        return layer;
+    }
+
+    /**
+     * builds the UI
+     */
+    protected void build() {
+        setLayout(new BorderLayout());
+        childTree = new RelationTree(model);
+        JScrollPane pane = new JScrollPane(childTree);
+        add(pane, BorderLayout.CENTER);
+
+        add(buildButtonPanel(), BorderLayout.SOUTH);
+    }
+
+    /**
+     * builds the panel with the command buttons
+     * 
+     * @return the button panel
+     */
+    protected JPanel buildButtonPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new FlowLayout(FlowLayout.LEFT));
+
+        // ---
+        DownloadAllChildRelationsAction downloadAction= new DownloadAllChildRelationsAction();
+        pnl.add(new JButton(downloadAction));
+
+        // ---
+        DownloadSelectedAction downloadSelectedAction= new DownloadSelectedAction();
+        childTree.addTreeSelectionListener(downloadSelectedAction);
+        pnl.add(new JButton(downloadSelectedAction));
+
+        // ---
+        EditAction editAction = new EditAction();
+        childTree.addTreeSelectionListener(editAction);
+        pnl.add(new JButton(editAction));
+
+        return pnl;
+    }
+
+    /**
+     * constructor
+     * 
+     * @param layer the {@see OsmDataLayer} this browser is related to. Must not be null.
+     * @exception IllegalArgumentException thrown, if layer is null
+     */
+    public ChildRelationBrowser(OsmDataLayer layer) throws IllegalArgumentException {
+        if (layer == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
+        this.layer = layer;
+        model = new RelationTreeModel();
+        build();
+    }
+
+    /**
+     * constructor
+     * 
+     * @param layer the {@see OsmDataLayer} this browser is related to. Must not be null.
+     * @param root the root relation
+     * @exception IllegalArgumentException thrown, if layer is null
+     */
+    public ChildRelationBrowser(OsmDataLayer layer, Relation root) throws IllegalArgumentException {
+        this(layer);
+        populate(root);
+    }
+
+    /**
+     * populates the browser with a relation
+     * 
+     * @param r the relation
+     */
+    public void populate(Relation r) {
+        model.populate(r);
+    }
+
+    /**
+     * populates the browser with a list of relation members
+     * 
+     * @param members the list of relation members
+     */
+
+    public void populate(List<RelationMember> members) {
+        model.populate(members);
+    }
+
+    /**
+     * replies the parent dialog this browser is embedded in
+     * 
+     * @return the parent dialog; null, if there is no {@see Dialog} as parent dialog
+     */
+    protected Dialog getParentDialog() {
+        Component c  = this;
+        while(c != null && ! (c instanceof Dialog)) {
+            c = c.getParent();
+        }
+        return (Dialog)c;
+    }
+
+    /**
+     * Action for editing the currently selected relation
+     * 
+     * 
+     */
+    class EditAction extends AbstractAction implements TreeSelectionListener {
+        public EditAction() {
+            putValue(SHORT_DESCRIPTION, tr("Edit the relation the currently selected relation member refers to"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
+            putValue(NAME, tr("Edit"));
+            refreshEnabled();
+        }
+
+        protected void refreshEnabled() {
+            TreePath[] selection = childTree.getSelectionPaths();
+            setEnabled(selection != null && selection.length > 0);
+        }
+
+        public void run() {
+            TreePath [] selection = childTree.getSelectionPaths();
+            if (selection == null || selection.length == 0) return;
+            // do not launch more than 10 relation editors in parallel
+            //
+            for (int i=0; i < Math.min(selection.length,10);i++) {
+                Relation r = (Relation)selection[i].getLastPathComponent();
+                if (r.incomplete) {
+                    continue;
+                }
+                RelationEditor editor = RelationEditor.getEditor(getLayer(), r, null);
+                editor.setVisible(true);
+            }
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            if (!isEnabled())
+                return;
+            run();
+        }
+
+        public void valueChanged(TreeSelectionEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    /**
+     * Action for downloading all child relations for a given parent relation.
+     * Recursively.
+     */
+    class DownloadAllChildRelationsAction extends AbstractAction{
+        public DownloadAllChildRelationsAction() {
+            putValue(SHORT_DESCRIPTION, tr("Download all child relations (recursively)"));
+            putValue(SMALL_ICON, ImageProvider.get("download"));
+            putValue(NAME, tr("Download All Children"));
+        }
+
+        public void run() {
+            Main.worker.submit(new DownloadAllChildrenTask(getParentDialog(), (Relation)model.getRoot()));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            if (!isEnabled())
+                return;
+            run();
+        }
+    }
+
+    /**
+     * Action for downloading all selected relations
+     */
+    class DownloadSelectedAction extends AbstractAction implements TreeSelectionListener {
+        public DownloadSelectedAction() {
+            putValue(SHORT_DESCRIPTION, tr("Download selected relations"));
+            // FIXME: replace with better icon
+            //
+            putValue(SMALL_ICON, ImageProvider.get("download"));
+            putValue(NAME, tr("Download Selected Children"));
+            updateEnabledState();
+        }
+
+        protected void updateEnabledState() {
+            TreePath [] selection = childTree.getSelectionPaths();
+            setEnabled(selection != null && selection.length > 0);
+        }
+
+        public void run() {
+            TreePath [] selection = childTree.getSelectionPaths();
+            if (selection == null || selection.length == 0)
+                return;
+            HashSet<Relation> relations = new HashSet<Relation>();
+            for (int i=0; i < selection.length;i++) {
+                relations.add((Relation)selection[i].getLastPathComponent());
+            }
+            Main.worker.submit(new DownloadRelationSetTask(getParentDialog(),relations));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            if (!isEnabled())
+                return;
+            run();
+        }
+
+        public void valueChanged(TreeSelectionEvent e) {
+            updateEnabledState();
+        }
+    }
+
+    /**
+     * The asynchronous task for downloading relation members.
+     * 
+     * 
+     */
+    class DownloadAllChildrenTask extends PleaseWaitRunnable {
+        private boolean cancelled;
+        private int conflictsCount;
+        private Exception lastException;
+        private Relation relation;
+        private Stack<Relation> relationsToDownload;
+        private Set<Long> downloadedRelationIds;
+
+        public DownloadAllChildrenTask(Dialog parent, Relation r) {
+            super(tr("Download relation members"), new PleaseWaitProgressMonitor(parent), false /*
+             * don't
+             * ignore
+             * exception
+             */);
+            this.relation = r;
+            relationsToDownload = new Stack<Relation>();
+            downloadedRelationIds = new HashSet<Long>();
+            relationsToDownload.push(this.relation);
+        }
+
+        @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);
+        }
+
+        protected void refreshView(Relation relation){
+            for (int i=0; i < childTree.getRowCount(); i++) {
+                Relation reference = (Relation)childTree.getPathForRow(i).getLastPathComponent();
+                if (reference == relation) {
+                    model.refreshNode(childTree.getPathForRow(i));
+                }
+            }
+        }
+
+        @Override
+        protected void finish() {
+            if (cancelled)
+                return;
+            if (lastException != null) {
+                showLastException();
+                return;
+            }
+
+            if (conflictsCount > 0) {
+                JOptionPane op = new JOptionPane(
+                        tr("There were {0} conflicts during import.", conflictsCount),
+                        JOptionPane.WARNING_MESSAGE
+                );
+                JDialog dialog = op.createDialog(ChildRelationBrowser.this, tr("Conflicts in data"));
+                dialog.setAlwaysOnTop(true);
+                dialog.setModal(true);
+                dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                dialog.setVisible(true);
+            }
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            try {
+                PrimitiveNameFormatter nameFormatter = new PrimitiveNameFormatter();
+                while(! relationsToDownload.isEmpty() && !cancelled) {
+                    Relation r = relationsToDownload.pop();
+                    downloadedRelationIds.add(r.id);
+                    for (RelationMember member: r.members) {
+                        if (member.member instanceof Relation) {
+                            Relation child = (Relation)member.member;
+                            if (!downloadedRelationIds.contains(child)) {
+                                relationsToDownload.push(child);
+                            }
+                        }
+                    }
+                    progressMonitor.setCustomText(tr("Downloading relation {0}", nameFormatter.getName(r)));
+                    OsmServerObjectReader reader = new OsmServerObjectReader(r.id, OsmPrimitiveType.RELATION,
+                            true);
+                    DataSet dataSet = reader.parseOsm(progressMonitor
+                            .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+                    if (dataSet != null) {
+                        final MergeVisitor visitor = new MergeVisitor(getLayer().data, dataSet);
+                        visitor.merge();
+                        // FIXME: this is necessary because there are dialogs listening
+                        // for DataChangeEvents which manipulate Swing components on this
+                        // thread.
+                        //
+                        SwingUtilities.invokeLater(new Runnable() {
+                            public void run() {
+                                getLayer().fireDataChange();
+                            }
+                        });
+                        if (!visitor.getConflicts().isEmpty()) {
+                            getLayer().getConflicts().add(visitor.getConflicts());
+                            conflictsCount +=  visitor.getConflicts().size();
+                        }
+                    }
+                    refreshView(r);
+                }
+            } catch (Exception e) {
+                if (cancelled) {
+                    System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
+                            .toString()));
+                    return;
+                }
+                lastException = e;
+            }
+        }
+    }
+
+
+    /**
+     * The asynchronous task for downloading a set of relations
+     */
+    class DownloadRelationSetTask extends PleaseWaitRunnable {
+        private boolean cancelled;
+        private int conflictsCount;
+        private Exception lastException;
+        private Set<Relation> relations;
+
+        public DownloadRelationSetTask(Dialog parent, Set<Relation> relations) {
+            super(tr("Download relation members"), new PleaseWaitProgressMonitor(parent), false /*
+             * don't
+             * ignore
+             * exception
+             */);
+            this.relations = relations;
+        }
+
+        @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);
+        }
+
+        protected void refreshView(Relation relation){
+            for (int i=0; i < childTree.getRowCount(); i++) {
+                Relation reference = (Relation)childTree.getPathForRow(i).getLastPathComponent();
+                if (reference == relation) {
+                    model.refreshNode(childTree.getPathForRow(i));
+                }
+            }
+        }
+
+        @Override
+        protected void finish() {
+            if (cancelled)
+                return;
+            if (lastException != null) {
+                showLastException();
+                return;
+            }
+
+            if (conflictsCount > 0) {
+                JOptionPane op = new JOptionPane(
+                        tr("There were {0} conflicts during import.", conflictsCount),
+                        JOptionPane.WARNING_MESSAGE
+                );
+                JDialog dialog = op.createDialog(ChildRelationBrowser.this, tr("Conflicts in data"));
+                dialog.setAlwaysOnTop(true);
+                dialog.setModal(true);
+                dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                dialog.setVisible(true);
+            }
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            try {
+                PrimitiveNameFormatter nameFormatter = new PrimitiveNameFormatter();
+                Iterator<Relation> it = relations.iterator();
+                while(it.hasNext() && !cancelled) {
+                    Relation r = it.next();
+                    progressMonitor.setCustomText(tr("Downloading relation {0}", nameFormatter.getName(r)));
+                    OsmServerObjectReader reader = new OsmServerObjectReader(r.id, OsmPrimitiveType.RELATION,
+                            true);
+                    DataSet dataSet = reader.parseOsm(progressMonitor
+                            .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+                    if (dataSet != null) {
+                        final MergeVisitor visitor = new MergeVisitor(getLayer().data, dataSet);
+                        visitor.merge();
+                        // FIXME: this is necessary because there are dialogs listening
+                        // for DataChangeEvents which manipulate Swing components on this
+                        // thread.
+                        //
+                        SwingUtilities.invokeLater(new Runnable() {
+                            public void run() {
+                                getLayer().fireDataChange();
+                            }
+                        });
+                        if (!visitor.getConflicts().isEmpty()) {
+                            getLayer().getConflicts().add(visitor.getConflicts());
+                            conflictsCount +=  visitor.getConflicts().size();
+                        }
+                    }
+                    refreshView(r);
+                }
+            } catch (Exception e) {
+                if (cancelled) {
+                    System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
+                            .toString()));
+                    return;
+                }
+                lastException = e;
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1827)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1828)
@@ -40,4 +40,6 @@
 import javax.swing.KeyStroke;
 import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
@@ -92,4 +94,5 @@
     private AutoCompletionCache acCache;
     private AutoCompletionList acList;
+    private ReferringRelationsBrowser referrerBrowser;
     private ReferringRelationsBrowserModel referrerModel;
 
@@ -152,6 +155,20 @@
         tabbedPane.add(tr("Tags and Members"), pnl);
         if (relation != null && relation.id > 0) {
-            tabbedPane.add(tr("Parent Relations"), new ReferringRelationsBrowser(getLayer(), referrerModel, this));
-        }
+            referrerBrowser = new ReferringRelationsBrowser(getLayer(), referrerModel, this);
+            tabbedPane.add(tr("Parent Relations"), referrerBrowser);
+        }
+        tabbedPane.add(tr("Child Relations"), new ChildRelationBrowser(getLayer(), relation));
+        tabbedPane.addChangeListener(
+                new ChangeListener() {
+                    public void stateChanged(ChangeEvent e) {
+                        JTabbedPane sourceTabbedPane = (JTabbedPane) e.getSource();
+                        int index = sourceTabbedPane.getSelectedIndex();
+                        String title = sourceTabbedPane.getTitleAt(index);
+                        if (title.equals(tr("Parent Relations"))) {
+                            referrerBrowser.init();
+                        }
+                    }
+                }
+        );
 
         getContentPane().add(tabbedPane, BorderLayout.CENTER);
@@ -765,5 +782,5 @@
             Shortcut.registerShortcut("relationeditor:sort", tr("Relation Editor: Sort"), KeyEvent.VK_T,
                     Shortcut.GROUP_MNEMONIC);
-            setEnabled(false);
+            //setEnabled(false);
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 1827)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 1828)
@@ -415,16 +415,28 @@
          */
 
-        if (map.isEmpty())
+        if (map.isEmpty()) {
             // empty relation or incomplete members
             return;
+        }
         segments = new Vector<LinkedList<Integer>>();
-        // add first member of relation, not strictly necessary
-        if (map.remove(0, members.get(0))) {
+
+        while (!map.isEmpty()) {
+            // find an element for the next segment
+            // try first element in relation if we just started
+            // otherwise, or if first element is another relation, just fetch some element from the
+            // map
+            Integer next;
+            if ((segments.size() == 0) && map.remove(0, members.get(0))) {
+                next = 0;
+            } else {
+                next = map.pop();
+                if (next == null) {
+                    break;
+                }
+            }
+
             segment = new LinkedList<Integer>();
-            segment.add(Integer.valueOf(0));
+            segment.add(next);
             segments.add(segment);
-        }
-        while (!map.isEmpty()) {
-            segment = segments.lastElement();
 
             do {
@@ -516,29 +528,23 @@
             } while (something_done);
 
-            Integer next = map.pop();
-            if (next == null) {
-                break;
-            }
-
+        }
+        if (segments.size() > 0) {
+            // append map.remaining() to segments list (as a single segment)
             segment = new LinkedList<Integer>();
-            segment.add(next);
+            segment.addAll(map.getRemaining());
             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(members.get(p));
-            }
-        }
-        members.clear();
-        members.addAll(newmembers);
-
-        fireTableDataChanged();
+
+            // 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(members.get(p));
+                }
+            }
+            members.clear();
+            members.addAll(newmembers);
+
+            fireTableDataChanged();
+        }
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java	(revision 1827)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java	(revision 1828)
@@ -58,4 +58,10 @@
     private final GenericRelationEditor relationEditor;
 
+    /** state flag for this browser. Initially initialized is false.
+     * It becomes initialized after the first download of the parent
+     * relations.
+     */
+    private boolean initialized;
+
     /**
      * build the GUI
@@ -89,4 +95,24 @@
         this.layer = layer;
         build();
+    }
+
+
+    /**
+     * Replies true this browser has initialized itself by downloading the reference relations
+     * parents.
+     * 
+     * @return true this browser has initialized itself by downloading the reference relations
+     * parents; false, otherwise
+     */
+    public boolean isInitialized() {
+        return initialized;
+    }
+
+    public void init() {
+        if (initialized) return;
+        initialized = true;
+        boolean full = cbReadFull.isSelected();
+        ReloadTask task = new ReloadTask(full, relationEditor);
+        Main.worker.submit(task);
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowserModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowserModel.java	(revision 1827)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowserModel.java	(revision 1828)
@@ -6,5 +6,4 @@
 import javax.swing.AbstractListModel;
 
-import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationDialogManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationDialogManager.java	(revision 1827)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationDialogManager.java	(revision 1828)
@@ -22,4 +22,20 @@
  */
 public class RelationDialogManager extends WindowAdapter implements LayerChangeListener{
+
+    /** keeps track of open relation editors */
+    static RelationDialogManager relationDialogManager;
+
+    /**
+     * Replies the singleton {@see RelationDialogManager}
+     * 
+     * @return the singleton {@see RelationDialogManager}
+     */
+    static public RelationDialogManager getRelationDialogManager() {
+        if (RelationDialogManager.relationDialogManager == null) {
+            RelationDialogManager.relationDialogManager = new RelationDialogManager();
+            Layer.listeners.add(RelationDialogManager.relationDialogManager);
+        }
+        return RelationDialogManager.relationDialogManager;
+    }
 
     /**
@@ -251,3 +267,4 @@
         }
     }
+
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java	(revision 1827)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java	(revision 1828)
@@ -13,26 +13,24 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 
 public abstract class RelationEditor extends ExtendedDialog {
 
-    /** keeps track of open relation editors */
-    static private RelationDialogManager relationDialogManager;
+    /** the list of registered relation editor classes */
+    private static ArrayList<Class<RelationEditor>> editors = new ArrayList<Class<RelationEditor>>();
 
     /**
-     * Replies the singleton {@see RelationDialogManager}
+     * Registers a relation editor class. Depending on the type of relation to be edited
+     * {@see #getEditor(OsmDataLayer, Relation, Collection)} will create an instance of
+     * this class.
      * 
-     * @return the singleton {@see RelationDialogManager}
+     * @param clazz the class
      */
-    static public RelationDialogManager getRelationDialogManager() {
-        if (relationDialogManager == null) {
-            relationDialogManager = new RelationDialogManager();
-            Layer.listeners.add(relationDialogManager);
+    public void registerRelationEditor(Class<RelationEditor> clazz) {
+        if (clazz == null) return;
+        if (!editors.contains(clazz)) {
+            editors.add(clazz);
         }
-        return relationDialogManager;
     }
-
-    public static ArrayList<Class<RelationEditor>> editors = new ArrayList<Class<RelationEditor>>();
 
     /**
@@ -81,10 +79,10 @@
             }
         }
-        if (getRelationDialogManager().isOpenInEditor(layer, r))
-            return getRelationDialogManager().getEditorForRelation(layer, r);
+        if (RelationDialogManager.getRelationDialogManager().isOpenInEditor(layer, r))
+            return RelationDialogManager.getRelationDialogManager().getEditorForRelation(layer, r);
         else {
             RelationEditor editor = new GenericRelationEditor(layer, r, selectedMembers);
-            getRelationDialogManager().positionOnScreen(editor);
-            getRelationDialogManager().register(layer, r, editor);
+            RelationDialogManager.getRelationDialogManager().positionOnScreen(editor);
+            RelationDialogManager.getRelationDialogManager().register(layer, r, editor);
             return editor;
         }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTree.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTree.java	(revision 1828)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTree.java	(revision 1828)
@@ -0,0 +1,168 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.Dialog;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeWillExpandListener;
+import javax.swing.tree.ExpandVetoException;
+import javax.swing.tree.TreePath;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmServerObjectReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+
+/**
+ * This is a {@se JTree} rendering the hierarchical structure of {@see Relation}s.
+ * 
+ * @see RelationTreeModel
+ */
+public class RelationTree extends JTree {
+    static private final Logger logger = Logger.getLogger(RelationTree.class.getName());
+
+    /**
+     * builds the UI
+     */
+    protected void build() {
+        setRootVisible(false);
+        setCellRenderer(new RelationTreeCellRenderer());
+        addTreeWillExpandListener(new LazyRelationLoader());
+    }
+
+    /**
+     * constructor
+     */
+    public RelationTree(){
+        super();
+        build();
+    }
+
+    /**
+     * constructor
+     * @param model the tree model
+     */
+    public RelationTree(RelationTreeModel model) {
+        super(model);
+        build();
+    }
+
+    /**
+     * replies the parent dialog this tree is embedded in.
+     * 
+     * @return the parent dialog; null, if there is no parent dialog
+     */
+    protected Dialog getParentDialog() {
+        Component c = RelationTree.this;
+        while(c != null && ! (c instanceof Dialog)) {
+            c = c.getParent();
+        }
+        return (Dialog)c;
+    }
+
+    /**
+     * An adapter for TreeWillExpand-events. If a node is to be expanded which is
+     * not loaded yet this will trigger asynchronous loading of the respective
+     * relation.
+     *
+     */
+    class LazyRelationLoader implements TreeWillExpandListener {
+
+        public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
+            // do nothing
+        }
+
+        public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
+            TreePath path  = event.getPath();
+            Relation parent = (Relation)event.getPath().getLastPathComponent();
+            if (! parent.incomplete || parent.id == 0)
+                // we don't load complete  or new relations
+                return;
+            // launch the download task
+            //
+            Main.worker.submit(new RelationLoader(getParentDialog(),parent, path));
+        }
+    }
+
+    /**
+     * Asynchronous download task for a specific relation
+     *
+     */
+    class RelationLoader extends PleaseWaitRunnable {
+        private boolean cancelled;
+        private Exception lastException;
+        private Relation relation;
+        private DataSet ds;
+        private TreePath path;
+
+        public RelationLoader(Dialog dialog, Relation relation, TreePath path) {
+            super(
+                    tr("Load relation"),
+                    new PleaseWaitProgressMonitor(
+                            dialog
+                    ),
+                    false /* don't ignore exceptions */
+            );
+            this.relation = relation;
+            this.path = path;
+        }
+        @Override
+        protected void cancel() {
+            OsmApi.getOsmApi().cancel();
+            this.cancelled = true;
+        }
+
+        @Override
+        protected void finish() {
+            if (cancelled)
+                return;
+            if (lastException != null) {
+                lastException.printStackTrace();
+                return;
+            }
+            MergeVisitor visitor = new MergeVisitor(Main.main.getEditLayer().data, ds);
+            visitor.merge();
+            if (! visitor.getConflicts().isEmpty()) {
+                Main.main.getEditLayer().getConflicts().add(visitor.getConflicts());
+            }
+            final RelationTreeModel model = (RelationTreeModel)getModel();
+            SwingUtilities.invokeLater(
+                    new Runnable() {
+                        public void run() {
+                            model.refreshNode(path);
+                        }
+                    }
+            );
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            try {
+                OsmServerObjectReader reader = new OsmServerObjectReader(relation.id, OsmPrimitiveType.from(relation), true /* full load */);
+                ds = reader.parseOsm(progressMonitor
+                        .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+            } catch(Exception e) {
+                if (cancelled) {
+                    System.out.println(tr("Warning: ignoring exception because task was cancelled. Exception was: " + e.toString()));
+                    return;
+                }
+                this.lastException = e;
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTreeCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTreeCellRenderer.java	(revision 1828)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTreeCellRenderer.java	(revision 1828)
@@ -0,0 +1,73 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.awt.Color;
+import java.awt.Component;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JTree;
+import javax.swing.tree.TreeCellRenderer;
+
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.gui.PrimitiveNameFormatter;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * This is the cell renderer used in {@see RelationTree}.
+ *
+ *
+ */
+public class RelationTreeCellRenderer extends JLabel implements TreeCellRenderer {
+    private static final PrimitiveNameFormatter NAME_FORMATTER = new PrimitiveNameFormatter();
+    public final static Color BGCOLOR_SELECTED = new Color(143,170,255);
+
+    /** the relation icon */
+    private ImageIcon icon;
+
+    /**
+     * constructor
+     */
+    public RelationTreeCellRenderer() {
+        icon = ImageProvider.get("data", "relation");
+        setOpaque(true);
+    }
+
+    /**
+     * renders the icon
+     */
+    protected void renderIcon() {
+        setIcon(icon);
+    }
+
+    /**
+     * renders the textual value. Uses the relations names as value
+     * 
+     * @param relation the relation
+     */
+    protected void renderValue(Relation relation) {
+        setText(NAME_FORMATTER.getName(relation));
+    }
+
+    /**
+     * renders the background
+     * 
+     * @param selected true, if the current node is selected
+     */
+    protected void renderBackground(boolean selected) {
+        Color bgColor = Color.WHITE;
+        if (selected) {
+            bgColor = BGCOLOR_SELECTED;
+        }
+        setBackground(bgColor);
+    }
+
+    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
+            boolean leaf, int row, boolean hasFocus) {
+        renderIcon();
+        renderValue((Relation)value);
+        renderBackground(selected);
+        return this;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTreeModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTreeModel.java	(revision 1828)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTreeModel.java	(revision 1828)
@@ -0,0 +1,233 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Logger;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+
+/**
+ * This is a {@see TreeModel} which provides the hierarchical structure of {@see Relation}s
+ * to a  {@see JTree}.
+ * 
+ * The model is initialized with a root relation or with a list of {@see RelationMember}s, see
+ * {@see #populate(Relation)} and {@see #populate(List)} respectively.
+ * 
+ *
+ */
+public class RelationTreeModel implements TreeModel {
+    private static final Logger logger = Logger.getLogger(RelationTreeModel.class.getName());
+
+    /** the root relation */
+    private Relation root;
+
+    /** the tree model listeners */
+    private CopyOnWriteArrayList<TreeModelListener> listeners;
+
+    /**
+     * constructor
+     */
+    public RelationTreeModel() {
+        this.root = null;
+        listeners = new CopyOnWriteArrayList<TreeModelListener>();
+    }
+
+    /**
+     * constructor
+     * @param root the root relation
+     */
+    public RelationTreeModel(Relation root) {
+        this.root = root;
+        listeners = new CopyOnWriteArrayList<TreeModelListener>();
+    }
+
+    /**
+     * constructor
+     * 
+     * @param members a list of members
+     */
+    public RelationTreeModel(List<RelationMember> members) {
+        if (members == null) return;
+        Relation root = new Relation();
+        root.members.addAll(members);
+        this.root = root;
+        listeners = new CopyOnWriteArrayList<TreeModelListener>();
+    }
+
+    /**
+     * Replies the number of children of type relation for a particular
+     * relation <code>parent</code>
+     * 
+     * @param parent the parent relation
+     * @return the number of children of type relation
+     */
+    protected int getNumRelationChildren(Relation parent) {
+        if (parent == null) return 0;
+        if (parent.members == null) return 0;
+        int count = 0;
+        for(RelationMember member : parent.members) {
+            if (member.member instanceof Relation) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Replies the i-th child of type relation for a particular relation
+     * <code>parent</code>.
+     * 
+     * @param parent the parent relation
+     * @param idx the index
+     * @return the i-th child of type relation for a particular relation
+     * <code>parent</code>; null, if no such child exists
+     */
+    protected Relation getRelationChildByIdx(Relation parent, int idx) {
+        if (parent == null) return null;
+        int count=0;
+        for (RelationMember member : parent.members) {
+            if (!(member.member instanceof Relation)) {
+                continue;
+            }
+            if (count == idx)
+                return (Relation)member.member;
+            count++;
+        }
+        return null;
+    }
+
+    /**
+     * Replies the index of a particular <code>child</code> with respect to its
+     * <code>parent</code>.
+     * 
+     * @param parent  the parent relation
+     * @param child the child relation
+     * @return the index of a particular <code>child</code> with respect to its
+     * <code>parent</code>; -1 if either parent or child are null or if <code>child</code>
+     * isn't a child of <code>parent</code>.
+     * 
+     */
+    protected int getIndexForRelationChild(Relation parent, Relation child) {
+        if (parent == null || child == null) return -1;
+        int idx = 0;
+        for (RelationMember member : parent.members) {
+            if (!(member.member instanceof Relation)) {
+                continue;
+            }
+            if (member.member == child) return idx;
+            idx++;
+        }
+        return -1;
+    }
+
+    /**
+     * Populates the model with a root relation
+     * 
+     * @param root the root relation
+     * @see #populate(List)
+     * 
+     */
+    public void populate(Relation root) {
+        this.root = root;
+        fireRootReplacedEvent();
+    }
+
+    /**
+     * Populates the model with a list of relation members
+     * 
+     * @param members the relation members
+     */
+    public void populate(List<RelationMember> members) {
+        if (members == null) return;
+        Relation r = new Relation();
+        r.members.addAll(members);
+        this.root = r;
+        fireRootReplacedEvent();
+    }
+
+    /**
+     * Notifies tree model listeners about a replacement of the
+     * root.
+     */
+    protected void fireRootReplacedEvent() {
+        TreeModelEvent e = new TreeModelEvent(this, new TreePath(root));
+        for (TreeModelListener l : listeners) {
+            l.treeStructureChanged(e);
+        }
+    }
+
+    /**
+     * Notifies tree model listeners about an update of the
+     * trees nodes.
+     * 
+     * @param path the tree path to the node
+     */
+    protected void fireRefreshNode(TreePath path) {
+        TreeModelEvent e = new TreeModelEvent(this, path);
+        for (TreeModelListener l : listeners) {
+            l.treeStructureChanged(e);
+        }
+
+    }
+
+    /**
+     * Invoke to notify all listeners about an update of a particular node
+     * 
+     * @param pathToNode the tree path to the node
+     */
+    public void refreshNode(TreePath pathToNode) {
+        fireRefreshNode(pathToNode);
+    }
+
+    /* ----------------------------------------------------------------------- */
+    /* interface TreeModel                                                     */
+    /* ----------------------------------------------------------------------- */
+    public Object getChild(Object parent, int index) {
+        return getRelationChildByIdx((Relation)parent, index);
+    }
+
+    public int getChildCount(Object parent) {
+        return getNumRelationChildren((Relation)parent);
+    }
+
+    public int getIndexOfChild(Object parent, Object child) {
+        return getIndexForRelationChild((Relation)parent, (Relation)child);
+    }
+
+    public Object getRoot() {
+        return root;
+    }
+
+    public boolean isLeaf(Object node) {
+        Relation r = (Relation)node;
+        if (r.incomplete) return false;
+        return getNumRelationChildren(r) == 0;
+    }
+
+    public void addTreeModelListener(TreeModelListener l) {
+        synchronized (listeners) {
+            if (l != null && !listeners.contains(l)) {
+                listeners.add(l);
+            }
+        }
+    }
+
+    public void removeTreeModelListener(TreeModelListener l) {
+        synchronized (listeners) {
+            if (l != null && listeners.contains(l)) {
+                listeners.remove(l);
+            }
+        }
+    }
+
+    public void valueForPathChanged(TreePath path, Object newValue) {
+        // do nothing
+    }
+}
