Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 9656)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 9657)
@@ -77,4 +77,5 @@
 import org.openstreetmap.josm.gui.dialogs.relation.actions.OKAction;
 import org.openstreetmap.josm.gui.dialogs.relation.actions.PasteMembersAction;
+import org.openstreetmap.josm.gui.dialogs.relation.actions.RefreshAction;
 import org.openstreetmap.josm.gui.dialogs.relation.actions.RemoveAction;
 import org.openstreetmap.josm.gui.dialogs.relation.actions.RemoveSelectedAction;
@@ -128,4 +129,12 @@
     private JButton sortBelowButton;
     /**
+     * Action for performing the {@link RefreshAction}
+     */
+    private RefreshAction refreshAction;
+    /**
+     * Action for performing the {@link ApplyAction}
+     */
+    private ApplyAction applyAction;
+    /**
      * Action for performing the {@link CancelAction}
      */
@@ -172,19 +181,5 @@
 
         tagEditorPanel = new TagEditorPanel(relation, presetHandler);
-
-        // populate the models
-        //
-        if (relation != null) {
-            tagEditorPanel.getModel().initFromPrimitive(relation);
-            this.memberTableModel.populate(relation);
-            if (!getLayer().data.getRelations().contains(relation)) {
-                // treat it as a new relation if it doesn't exist in the
-                // data set yet.
-                setRelation(null);
-            }
-        } else {
-            tagEditorPanel.getModel().clear();
-            this.memberTableModel.populate(null);
-        }
+        populateModels(relation);
         tagEditorPanel.getModel().ensureOneTag();
 
@@ -259,5 +254,40 @@
     }
 
-    protected void cancel() {
+    /**
+     * Reloads data from relation.
+     */
+    public void reloadDataFromRelation() {
+        setRelation(getRelation());
+        populateModels(getRelation());
+        refreshAction.updateEnabledState();
+    }
+
+    private void populateModels(Relation relation) {
+        if (relation != null) {
+            tagEditorPanel.getModel().initFromPrimitive(relation);
+            memberTableModel.populate(relation);
+            if (!getLayer().data.getRelations().contains(relation)) {
+                // treat it as a new relation if it doesn't exist in the data set yet.
+                setRelation(null);
+            }
+        } else {
+            tagEditorPanel.getModel().clear();
+            memberTableModel.populate(null);
+        }
+    }
+
+    /**
+     * Apply changes.
+     * @see ApplyAction
+     */
+    public void apply() {
+        applyAction.actionPerformed(null);
+    }
+
+    /**
+     * Cancel changes.
+     * @see CancelAction
+     */
+    public void cancel() {
         cancelAction.actionPerformed(null);
     }
@@ -269,7 +299,10 @@
      */
     protected JToolBar buildToolBar() {
-        JToolBar tb  = new JToolBar();
+        JToolBar tb = new JToolBar();
         tb.setFloatable(false);
-        tb.add(new ApplyAction(memberTable, memberTableModel, tagEditorPanel.getModel(), getLayer(), this));
+        refreshAction = new RefreshAction(memberTable, memberTableModel, tagEditorPanel.getModel(), getLayer(), this);
+        applyAction = new ApplyAction(memberTable, memberTableModel, tagEditorPanel.getModel(), getLayer(), this);
+        tb.add(refreshAction);
+        tb.add(applyAction);
         tb.add(new DuplicateRelationAction(memberTableModel, tagEditorPanel.getModel(), getLayer()));
         DeleteCurrentRelationAction deleteAction = new DeleteCurrentRelationAction(getLayer(), this);
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 9656)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 9657)
@@ -235,7 +235,11 @@
         }
         RelationMember member = members.get(rowIndex);
-        RelationMember newMember = new RelationMember(value.toString(), member.getMember());
+        String role = value.toString();
+        if (member.hasRole(role))
+            return;
+        RelationMember newMember = new RelationMember(role, member.getMember());
         members.remove(rowIndex);
         members.add(rowIndex, newMember);
+        fireTableDataChanged();
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ApplyAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ApplyAction.java	(revision 9656)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ApplyAction.java	(revision 9657)
@@ -4,7 +4,13 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.Component;
 import java.awt.event.ActionEvent;
 
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+
+import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
 import org.openstreetmap.josm.gui.dialogs.relation.MemberTable;
 import org.openstreetmap.josm.gui.dialogs.relation.MemberTableModel;
@@ -18,5 +24,5 @@
  * @since 9496
  */
-public class ApplyAction extends SavingAction {
+public class ApplyAction extends SavingAction implements PropertyChangeListener, TableModelListener {
 
     /**
@@ -34,27 +40,29 @@
         putValue(SMALL_ICON, ImageProvider.get("save"));
         putValue(NAME, tr("Apply"));
-        setEnabled(true);
+        updateEnabledState();
+        memberTableModel.addTableModelListener(this);
+        tagModel.addPropertyChangeListener(this);
     }
 
     @Override
     public void actionPerformed(ActionEvent e) {
-        if (editor.getRelation() == null) {
-            applyNewRelation(tagModel);
-        } else if (!memberTableModel.hasSameMembersAs(editor.getRelationSnapshot()) || tagModel.isDirty()) {
-            if (editor.isDirtyRelation()) {
-                if (confirmClosingBecauseOfDirtyState()) {
-                    if (layer.getConflicts().hasConflictForMy(editor.getRelation())) {
-                        warnDoubleConflict();
-                        return;
-                    }
-                    applyExistingConflictingRelation(tagModel);
-                    if (editor instanceof Component) {
-                        ((Component) editor).setVisible(false);
-                    }
-                }
-            } else {
-                applyExistingNonConflictingRelation(tagModel);
-            }
+        if (applyChanges()) {
+            ((GenericRelationEditor) editor).reloadDataFromRelation();
         }
     }
+
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(isEditorDirty());
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        updateEnabledState();
+    }
+
+    @Override
+    public void tableChanged(TableModelEvent e) {
+        updateEnabledState();
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/CancelAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/CancelAction.java	(revision 9656)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/CancelAction.java	(revision 9657)
@@ -4,5 +4,4 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.Component;
 import java.awt.event.ActionEvent;
 
@@ -66,27 +65,11 @@
                 //copied from OKAction.run()
                 Main.pref.put("relation.editor.generic.lastrole", tfRole.getText());
-                if (editor.getRelation() == null) {
-                    applyNewRelation(tagModel);
-                } else if (!memberTableModel.hasSameMembersAs(snapshot) || tagModel.isDirty()) {
-                    if (editor.isDirtyRelation()) {
-                        if (confirmClosingBecauseOfDirtyState()) {
-                            if (layer.getConflicts().hasConflictForMy(editor.getRelation())) {
-                                warnDoubleConflict();
-                                return;
-                            }
-                            applyExistingConflictingRelation(tagModel);
-                        } else
-                            return;
-                    } else {
-                        applyExistingNonConflictingRelation(tagModel);
-                    }
-                }
+                if (!applyChanges())
+                    return;
             } else if (ret == 2 || ret == JOptionPane.CLOSED_OPTION) //Cancel, continue editing
                 return;
             //in case of "No, discard", there is no extra action to be performed here.
         }
-        if (editor instanceof Component) {
-            ((Component) editor).setVisible(false);
-        }
+        hideEditor();
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/OKAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/OKAction.java	(revision 9656)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/OKAction.java	(revision 9657)
@@ -4,5 +4,4 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.Component;
 import java.awt.event.ActionEvent;
 
@@ -43,23 +42,7 @@
         Main.pref.put("relation.editor.generic.lastrole", tfRole.getText());
         memberTable.stopHighlighting();
-        if (editor.getRelation() == null) {
-            applyNewRelation(tagModel);
-        } else if (!memberTableModel.hasSameMembersAs(editor.getRelationSnapshot()) || tagModel.isDirty()) {
-            if (editor.isDirtyRelation()) {
-                if (confirmClosingBecauseOfDirtyState()) {
-                    if (layer.getConflicts().hasConflictForMy(editor.getRelation())) {
-                        warnDoubleConflict();
-                        return;
-                    }
-                    applyExistingConflictingRelation(tagModel);
-                } else
-                    return;
-            } else {
-                applyExistingNonConflictingRelation(tagModel);
-            }
-        }
-        if (editor instanceof Component) {
-            ((Component) editor).setVisible(false);
-        }
+        if (!applyChanges())
+            return;
+        hideEditor();
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/RefreshAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/RefreshAction.java	(revision 9657)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/RefreshAction.java	(revision 9657)
@@ -0,0 +1,146 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JComponent;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
+import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
+import org.openstreetmap.josm.gui.dialogs.relation.MemberTable;
+import org.openstreetmap.josm.gui.dialogs.relation.MemberTableModel;
+import org.openstreetmap.josm.gui.dialogs.relation.RelationAware;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener;
+import org.openstreetmap.josm.gui.tagging.TagEditorModel;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Refresh relation.
+ * @since 9657
+ */
+public class RefreshAction extends SavingAction implements CommandQueueListener {
+
+    /**
+     * Constructs a new {@code RefreshAction}.
+     * @param memberTable member table
+     * @param memberTableModel member table model
+     * @param layer OSM data layer
+     * @param editor relation editor
+     * @param tagModel tag editor model
+     */
+    public RefreshAction(MemberTable memberTable, MemberTableModel memberTableModel, TagEditorModel tagModel, OsmDataLayer layer,
+            RelationAware editor) {
+        super(memberTable, memberTableModel, tagModel, layer, editor, null);
+        // CHECKSTYLE.OFF: LineLength
+        Shortcut sc = Shortcut.registerShortcut("relationeditor:refresh", tr("Relation Editor: Refresh"), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE);
+        // CHECKSTYLE.ON: LineLength
+        putValue(SHORT_DESCRIPTION, Main.platform.makeTooltip(tr("Refresh relation from data layer"), sc));
+        putValue(SMALL_ICON, ImageProvider.get("dialogs/refresh"));
+        putValue(NAME, tr("Refresh"));
+        getEditor().getRootPane().getActionMap().put("refresh", this);
+        getEditor().getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), "refresh");
+        Main.main.undoRedo.addCommandQueueListener(this);
+        updateEnabledState();
+    }
+
+    private GenericRelationEditor getEditor() {
+        return (GenericRelationEditor) editor;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        Relation relation = editor.getRelation();
+        if (relation == null)
+            return;
+        if (relation.isDeleted()) {
+            if (confirmCloseDeletedRelation() == 0) {
+                hideEditor();
+            }
+            return;
+        }
+        if (isEditorDirty() && confirmDiscardDirtyData() != 0)
+            return;
+        getEditor().reloadDataFromRelation();
+    }
+
+    @Override
+    public void updateEnabledState() {
+        Relation relation = editor.getRelation();
+        Relation snapshot = editor.getRelationSnapshot();
+        setEnabled(snapshot != null && (
+            !relation.hasEqualTechnicalAttributes(snapshot) ||
+            !relation.hasEqualSemanticAttributes(snapshot)
+        ));
+    }
+
+    protected int confirmDiscardDirtyData() {
+        ButtonSpec[] options = new ButtonSpec[] {
+                new ButtonSpec(
+                        tr("Yes, discard changes and reload"),
+                        ImageProvider.get("ok"),
+                        tr("Click to discard the changes and reload data from layer"),
+                        null /* no specific help topic */
+                ),
+                new ButtonSpec(
+                        tr("Cancel, continue editing"),
+                        ImageProvider.get("cancel"),
+                        tr("Click to return to the relation editor and to resume relation editing"),
+                        null /* no specific help topic */
+                )
+        };
+
+        return HelpAwareOptionPane.showOptionDialog(
+                Main.parent,
+                tr("<html>You have unsaved changes in this editor window.<br>"+
+                   "<br>Do you want to discard these changes and reload data from layer?</html>"),
+                        tr("Unsaved changes"),
+                        JOptionPane.WARNING_MESSAGE,
+                        null,
+                        options,
+                        options[1], // Cancel is default
+                        "/Dialog/RelationEditor#Reload"
+        );
+    }
+
+    protected int confirmCloseDeletedRelation() {
+        ButtonSpec[] options = new ButtonSpec[] {
+                new ButtonSpec(
+                        tr("Yes"),
+                        ImageProvider.get("ok"),
+                        tr("Click to close window"),
+                        null /* no specific help topic */
+                ),
+                new ButtonSpec(
+                        tr("No, continue editing"),
+                        ImageProvider.get("cancel"),
+                        tr("Click to return to the relation editor and to resume relation editing"),
+                        null /* no specific help topic */
+                )
+        };
+
+        return HelpAwareOptionPane.showOptionDialog(
+                Main.parent,
+                tr("<html>Relation has been deleted outside editor.<br><br>Do you want to close this window?</html>"),
+                        tr("Deleted relation"),
+                        JOptionPane.WARNING_MESSAGE,
+                        null,
+                        options,
+                        options[0], // Yes is default
+                        "/Dialog/RelationEditor#Reload"
+        );
+    }
+
+    @Override
+    public void commandChanged(int queueSize, int redoSize) {
+        updateEnabledState();
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/SavingAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/SavingAction.java	(revision 9656)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/SavingAction.java	(revision 9657)
@@ -4,4 +4,5 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.Component;
 import java.util.ArrayList;
 import java.util.List;
@@ -111,7 +112,4 @@
         Main.main.undoRedo.add(new ChangeCommand(editor.getRelation(), editedRelation));
         layer.data.fireSelectionChanged();
-        // this will refresh the snapshot and update the dialog title
-        //
-        editor.setRelation(editor.getRelation());
     }
 
@@ -166,3 +164,36 @@
         // Do nothing
     }
+
+    protected boolean applyChanges() {
+        if (editor.getRelation() == null) {
+            applyNewRelation(tagModel);
+        } else if (isEditorDirty()) {
+            if (editor.isDirtyRelation()) {
+                if (confirmClosingBecauseOfDirtyState()) {
+                    if (layer.getConflicts().hasConflictForMy(editor.getRelation())) {
+                        warnDoubleConflict();
+                        return false;
+                    }
+                    applyExistingConflictingRelation(tagModel);
+                    hideEditor();
+                } else
+                    return false;
+            } else {
+                applyExistingNonConflictingRelation(tagModel);
+            }
+        }
+        editor.setRelation(editor.getRelation());
+        return true;
+    }
+
+    protected void hideEditor() {
+        if (editor instanceof Component) {
+            ((Component) editor).setVisible(false);
+        }
+    }
+
+    protected boolean isEditorDirty() {
+        Relation snapshot = editor.getRelationSnapshot();
+        return (snapshot != null && !memberTableModel.hasSameMembersAs(snapshot)) || tagModel.isDirty();
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java	(revision 9656)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java	(revision 9657)
@@ -166,7 +166,10 @@
      */
     public void clear() {
+        boolean wasEmpty = tags.isEmpty();
         tags.clear();
-        setDirty(true);
-        fireTableDataChanged();
+        if (!wasEmpty) {
+            setDirty(true);
+            fireTableDataChanged();
+        }
     }
 
@@ -335,5 +338,4 @@
         tags.add(tag);
         fireTableDataChanged();
-        setDirty(true);
     }
 
