Index: /trunk/src/org/openstreetmap/josm/command/CoordinateConflictResolveCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/CoordinateConflictResolveCommand.java	(revision 1654)
+++ /trunk/src/org/openstreetmap/josm/command/CoordinateConflictResolveCommand.java	(revision 1654)
@@ -0,0 +1,111 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.command;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+
+import javax.swing.JLabel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Represents a the resolution of a conflict between the coordinates of two {@see Node}s
+ *
+ */
+public class CoordinateConflictResolveCommand extends Command {
+
+    /** my node (in the local dataset). merge decisions are applied to this
+     *  node
+     */
+    private final Node my;
+    /** their node (in the server dataset) */
+    private final Node their;
+
+    /** the merge decision */
+    private final MergeDecisionType decision;
+
+
+    /**
+     * replies a (localized) display name for the type of an OSM primitive
+     * 
+     * @param primitive the primitive
+     * @return a localized display name
+     */
+    protected String getPrimitiveTypeAsString(OsmPrimitive primitive) {
+        if (primitive instanceof Node) return tr("node");
+        if (primitive instanceof Way) return tr("way");
+        if (primitive instanceof Relation) return tr("relation");
+        return "";
+    }
+
+    /**
+     * constructor
+     * 
+     * @param my  my node
+     * @param their  their node
+     * @param decision the merge decision
+     */
+    public CoordinateConflictResolveCommand(Node my, Node their, MergeDecisionType decision) {
+        this.my = my;
+        this.their = their;
+        this.decision = decision;
+    }
+
+
+    @Override
+    public MutableTreeNode description() {
+        return new DefaultMutableTreeNode(
+                new JLabel(
+                        tr("Resolve conflicts in coordinates in {0}",my.id),
+                        ImageProvider.get("data", "object"),
+                        JLabel.HORIZONTAL
+                )
+        );
+    }
+
+    @Override
+    public boolean executeCommand() {
+        // remember the current state of modified primitives, i.e. of
+        // OSM primitive 'my'
+        //
+        super.executeCommand();
+
+        if (decision.equals(MergeDecisionType.KEEP_MINE)) {
+            // do nothing
+        } else if (decision.equals(MergeDecisionType.KEEP_THEIR)) {
+            my.setCoor(their.getCoor());
+        } else
+            // should not happen
+            throw new IllegalStateException(tr("cannot resolve undecided conflict"));
+
+        return true;
+    }
+
+    @Override
+    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
+            Collection<OsmPrimitive> added) {
+        modified.add(my);
+    }
+
+    @Override
+    public void undoCommand() {
+        // restore former state of modified primitives
+        //
+        super.undoCommand();
+
+        // restore a conflict if necessary
+        //
+        if (!Main.map.conflictDialog.conflicts.containsKey(my)) {
+            Main.map.conflictDialog.addConflict(my, their);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/command/DeletedStateConflictResolveCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/DeletedStateConflictResolveCommand.java	(revision 1654)
+++ /trunk/src/org/openstreetmap/josm/command/DeletedStateConflictResolveCommand.java	(revision 1654)
@@ -0,0 +1,96 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.command;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+
+import javax.swing.JLabel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Represents a the resolution of a conflict between the coordinates of two {@see Node}s
+ *
+ */
+public class DeletedStateConflictResolveCommand extends Command {
+
+    /** my primitive (in the local dataset). merge decisions are applied to this
+     *  node
+     */
+    private final OsmPrimitive my;
+    /** their primitive (in the server dataset) */
+    private final OsmPrimitive their;
+
+    /** the merge decision */
+    private final MergeDecisionType decision;
+
+
+
+    /**
+     * constructor
+     * 
+     * @param my  my node
+     * @param their  their node
+     * @param decision the merge decision
+     */
+    public DeletedStateConflictResolveCommand(OsmPrimitive my, OsmPrimitive their, MergeDecisionType decision) {
+        this.my = my;
+        this.their = their;
+        this.decision = decision;
+    }
+
+
+    @Override
+    public MutableTreeNode description() {
+        return new DefaultMutableTreeNode(
+                new JLabel(
+                        tr("Resolve conflicts in deleted state in {0}",my.id),
+                        ImageProvider.get("data", "object"),
+                        JLabel.HORIZONTAL
+                )
+        );
+    }
+
+    @Override
+    public boolean executeCommand() {
+        // remember the current state of modified primitives, i.e. of
+        // OSM primitive 'my'
+        //
+        super.executeCommand();
+
+        if (decision.equals(MergeDecisionType.KEEP_MINE)) {
+            // do nothing
+        } else if (decision.equals(MergeDecisionType.KEEP_THEIR)) {
+            my.deleted = their.deleted;
+        } else
+            // should not happen
+            throw new IllegalStateException(tr("cannot resolve undecided conflict"));
+
+        return true;
+    }
+
+    @Override
+    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
+            Collection<OsmPrimitive> added) {
+        modified.add(my);
+    }
+
+    @Override
+    public void undoCommand() {
+        // restore former state of modified primitives
+        //
+        super.undoCommand();
+
+        // restore a conflict if necessary
+        //
+        if (!Main.map.conflictDialog.conflicts.containsKey(my)) {
+            Main.map.conflictDialog.addConflict(my, their);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/command/TagConflictResolveCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/TagConflictResolveCommand.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/command/TagConflictResolveCommand.java	(revision 1654)
@@ -16,5 +16,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.gui.conflict.tags.MergeDecisionType;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
 import org.openstreetmap.josm.gui.conflict.tags.TagMergeItem;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -121,8 +121,6 @@
         //
         if (!Main.map.conflictDialog.conflicts.containsKey(my)) {
-            Main.map.conflictDialog.conflicts.put(my,their);
+            Main.map.conflictDialog.addConflict(my, their);
         }
     }
-
-
 }
Index: /trunk/src/org/openstreetmap/josm/command/VersionConflictResolveCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/VersionConflictResolveCommand.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/command/VersionConflictResolveCommand.java	(revision 1654)
@@ -86,5 +86,3 @@
         }
     }
-
-
 }
Index: /trunk/src/org/openstreetmap/josm/command/WayNodesConflictResolverCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/WayNodesConflictResolverCommand.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/command/WayNodesConflictResolverCommand.java	(revision 1654)
@@ -25,17 +25,17 @@
 
     /** my way */
-    private Way my;
-    /** their way */ 
-    private Way their;
+    private final Way my;
+    /** their way */
+    private final Way their;
     /** the list of merged nodes. This becomes the list of news of my way after the
      *  command is executed
      */
-    private List<Node> mergedNodeList; 
-    
+    private final List<Node> mergedNodeList;
+
     /**
      * 
      * @param my my may
      * @param their their way
-     * @param mergedNodeList  the list of merged nodes 
+     * @param mergedNodeList  the list of merged nodes
      */
     public WayNodesConflictResolverCommand(Way my, Way their, List<Node> mergedNodeList) {
@@ -44,15 +44,15 @@
         this.mergedNodeList = mergedNodeList;
     }
-    
-    
+
+
     @Override
     public MutableTreeNode description() {
         return new DefaultMutableTreeNode(
                 new JLabel(
-                   tr("Resolve conflicts in node list of of way {0}", my.id), 
-                   ImageProvider.get("data", "object"), 
-                   JLabel.HORIZONTAL
+                        tr("Resolve conflicts in node list of of way {0}", my.id),
+                        ImageProvider.get("data", "object"),
+                        JLabel.HORIZONTAL
                 )
-         );
+        );
     }
 
@@ -62,7 +62,7 @@
         //
         super.executeCommand();
-        
+
         // replace the list of nodes of 'my' way by the list of merged
-        // nodes 
+        // nodes
         //
         my.nodes.clear();
@@ -74,5 +74,5 @@
             }
         }
-        return true;        
+        return true;
     }
 
@@ -80,5 +80,5 @@
     public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
             Collection<OsmPrimitive> added) {
-        modified.add(my);        
+        modified.add(my);
     }
 
@@ -88,9 +88,9 @@
         //
         super.undoCommand();
-        
+
         // restore a conflict if necessary
         //
         if (!Main.map.conflictDialog.conflicts.containsKey(my)) {
-            Main.map.conflictDialog.conflicts.put(my,their);
+            Main.map.conflictDialog.addConflict(my, their);
         }
     }
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/ConflictResolver.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/ConflictResolver.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/ConflictResolver.java	(revision 1654)
@@ -16,5 +16,4 @@
 import org.openstreetmap.josm.command.Command;
 import org.openstreetmap.josm.command.SequenceCommand;
-import org.openstreetmap.josm.command.TagConflictResolveCommand;
 import org.openstreetmap.josm.command.VersionConflictResolveCommand;
 import org.openstreetmap.josm.data.osm.Node;
@@ -24,4 +23,6 @@
 import org.openstreetmap.josm.gui.conflict.nodes.NodeListMergeModel;
 import org.openstreetmap.josm.gui.conflict.nodes.NodeListMerger;
+import org.openstreetmap.josm.gui.conflict.properties.PropertiesMergeModel;
+import org.openstreetmap.josm.gui.conflict.properties.PropertiesMerger;
 import org.openstreetmap.josm.gui.conflict.relation.RelationMemberListMergeModel;
 import org.openstreetmap.josm.gui.conflict.relation.RelationMemberMerger;
@@ -42,4 +43,5 @@
     private NodeListMerger nodeListMerger;
     private RelationMemberMerger relationMemberMerger;
+    private PropertiesMerger propertiesMerger;
     private OsmPrimitive my;
     private OsmPrimitive their;
@@ -56,18 +58,23 @@
         tabbedPane = new JTabbedPane();
 
+        propertiesMerger = new PropertiesMerger();
+        propertiesMerger.setName("panel.propertiesmerger");
+        propertiesMerger.getModel().addPropertyChangeListener(this);
+        tabbedPane.add(tr("Properties"), propertiesMerger);
+
         tagMerger = new TagMerger();
         tagMerger.setName("panel.tagmerger");
         tagMerger.getModel().addPropertyChangeListener(this);
-        tabbedPane.add("Tags", tagMerger);
+        tabbedPane.add(tr("Tags"), tagMerger);
 
         nodeListMerger = new NodeListMerger();
         nodeListMerger.setName("panel.nodelistmerger");
         nodeListMerger.getModel().addPropertyChangeListener(this);
-        tabbedPane.add("Nodes", nodeListMerger);
+        tabbedPane.add(tr("Nodes"), nodeListMerger);
 
         relationMemberMerger = new RelationMemberMerger();
         relationMemberMerger.setName("panel.relationmembermerger");
         relationMemberMerger.getModel().addPropertyChangeListener(this);
-        tabbedPane.add("Members", relationMemberMerger);
+        tabbedPane.add(tr("Members"), relationMemberMerger);
 
         setLayout(new BorderLayout());
@@ -81,38 +88,49 @@
 
     public void propertyChange(PropertyChangeEvent evt) {
-
         if (evt.getPropertyName().equals(TagMergeModel.PROP_NUM_UNDECIDED_TAGS)) {
             int newValue = (Integer)evt.getNewValue();
             if (newValue == 0) {
-                tabbedPane.setTitleAt(0, tr("Tags"));
-                tabbedPane.setToolTipTextAt(0, tr("No pending tag conflicts to be resolved"));
-                tabbedPane.setIconAt(0, mergeComplete);
-            } else {
-                tabbedPane.setTitleAt(0, tr("Tags({0} conflicts)", newValue));
-                tabbedPane.setToolTipTextAt(0, tr("{0} pending tag conflicts to be resolved"));
-                tabbedPane.setIconAt(0, mergeIncomplete);
+                tabbedPane.setTitleAt(1, tr("Tags"));
+                tabbedPane.setToolTipTextAt(1, tr("No pending tag conflicts to be resolved"));
+                tabbedPane.setIconAt(1, mergeComplete);
+            } else {
+                tabbedPane.setTitleAt(1, tr("Tags({0} conflicts)", newValue));
+                tabbedPane.setToolTipTextAt(1, tr("{0} pending tag conflicts to be resolved"));
+                tabbedPane.setIconAt(1, mergeIncomplete);
             }
         } else if (evt.getPropertyName().equals(ListMergeModel.FROZEN_PROP)) {
             boolean frozen = (Boolean)evt.getNewValue();
             if (frozen && evt.getSource() == nodeListMerger.getModel()) {
-                tabbedPane.setTitleAt(1, tr("Nodes(resolved)"));
-                tabbedPane.setToolTipTextAt(1, tr("Merged node list frozen. No pending conflicts in the node list of this way"));
-                tabbedPane.setIconAt(1, mergeComplete);
-            } else {
-                tabbedPane.setTitleAt(1, tr("Nodes(with conflicts)"));
-                tabbedPane.setToolTipTextAt(1,tr("Pending conflicts in the node list of this way"));
-                tabbedPane.setIconAt(1, mergeIncomplete);
+                tabbedPane.setTitleAt(2, tr("Nodes(resolved)"));
+                tabbedPane.setToolTipTextAt(2, tr("Merged node list frozen. No pending conflicts in the node list of this way"));
+                tabbedPane.setIconAt(2, mergeComplete);
+            } else {
+                tabbedPane.setTitleAt(2, tr("Nodes(with conflicts)"));
+                tabbedPane.setToolTipTextAt(2,tr("Pending conflicts in the node list of this way"));
+                tabbedPane.setIconAt(2, mergeIncomplete);
             }
             if (frozen && evt.getSource() == relationMemberMerger.getModel()) {
-                tabbedPane.setTitleAt(2, tr("Members(resolved)"));
-                tabbedPane.setToolTipTextAt(2, tr("Merged member list frozen. No pending conflicts in the member list of this relation"));
-                tabbedPane.setIconAt(2, mergeComplete);
-            } else {
-                tabbedPane.setTitleAt(2, tr("Members(with conflicts)"));
-                tabbedPane.setToolTipTextAt(2, tr("Pending conflicts in the member list of this relation"));
-                tabbedPane.setIconAt(2, mergeIncomplete);
-            }
-        }
-    }
+                tabbedPane.setTitleAt(3, tr("Members(resolved)"));
+                tabbedPane.setToolTipTextAt(3, tr("Merged member list frozen. No pending conflicts in the member list of this relation"));
+                tabbedPane.setIconAt(3, mergeComplete);
+            } else {
+                tabbedPane.setTitleAt(3, tr("Members(with conflicts)"));
+                tabbedPane.setToolTipTextAt(3, tr("Pending conflicts in the member list of this relation"));
+                tabbedPane.setIconAt(3, mergeIncomplete);
+            }
+        } else if (evt.getPropertyName().equals(PropertiesMergeModel.RESOLVED_COMPLETELY_PROP)) {
+            boolean resolved = (Boolean)evt.getNewValue();
+            if (resolved) {
+                tabbedPane.setTitleAt(0, tr("Properties"));
+                tabbedPane.setToolTipTextAt(0, tr("No pending property conflicts"));
+                tabbedPane.setIconAt(0, mergeComplete);
+            } else {
+                tabbedPane.setTitleAt(0, tr("Properties(with conflicts)"));
+                tabbedPane.setToolTipTextAt(0, tr("Pending property conflicts to be resolved"));
+                tabbedPane.setIconAt(0, mergeIncomplete);
+            }
+        }
+    }
+
 
     /**
@@ -126,17 +144,24 @@
         this.my = my;
         this.their =  their;
+        propertiesMerger.getModel().populate(my, their);
+        tabbedPane.setEnabledAt(0, true);
         tagMerger.getModel().populate(my, their);
-        tabbedPane.setEnabledAt(0,true);
+        tabbedPane.setEnabledAt(1, true);
+
         if (my instanceof Node) {
-            tabbedPane.setEnabledAt(1,false);
             tabbedPane.setEnabledAt(2,false);
+            tabbedPane.setEnabledAt(3,false);
         } else if (my instanceof Way) {
             nodeListMerger.populate((Way)my, (Way)their);
-            tabbedPane.setEnabledAt(1, true);
-            tabbedPane.setEnabledAt(2, false);
+            tabbedPane.setEnabledAt(2, true);
+            tabbedPane.setEnabledAt(3, false);
+            tabbedPane.setTitleAt(3,tr("Members"));
+            tabbedPane.setIconAt(3, null);
         } else if (my instanceof Relation) {
             relationMemberMerger.populate((Relation)my, (Relation)their);
-            tabbedPane.setEnabledAt(1, false);
-            tabbedPane.setEnabledAt(2, true);
+            tabbedPane.setEnabledAt(2, false);
+            tabbedPane.setTitleAt(2,tr("Nodes"));
+            tabbedPane.setIconAt(2, null);
+            tabbedPane.setEnabledAt(3, true);
         }
     }
@@ -150,6 +175,8 @@
     public Command buildResolveCommand() {
         ArrayList<Command> commands = new ArrayList<Command>();
-        TagConflictResolveCommand cmd = tagMerger.getModel().buildResolveCommand(my, their);
-        commands.add(cmd);
+        if (tagMerger.getModel().getNumResolvedConflicts() > 0) {
+            commands.add(tagMerger.getModel().buildResolveCommand(my, their));
+        }
+        commands.addAll(propertiesMerger.getModel().buildResolveCommand(my, their));
         if (my instanceof Way && nodeListMerger.getModel().isFrozen()) {
             NodeListMergeModel model  =(NodeListMergeModel)nodeListMerger.getModel();
@@ -159,43 +186,19 @@
             commands.add(model.buildResolveCommand((Relation)my, (Relation)their));
         }
+        if (isResolvedCompletely()) {
+            commands.add(
+                    new VersionConflictResolveCommand(my, their)
+            );
+        }
+        return new SequenceCommand(tr("Conflict Resolution"), commands);
+    }
+
+    public boolean isResolvedCompletely() {
         if (my instanceof Node) {
             // resolve the version conflict if this is a node and all tag
             // conflicts have been resolved
             //
-            if (tagMerger.getModel().isResolvedCompletely()) {
-                commands.add(
-                        new VersionConflictResolveCommand(my, their)
-                );
-            }
-        } else if (my instanceof Way) {
-            // resolve the version conflict if this is a way, all tag
-            // conflicts have been resolved, and conflicts in the node list
-            // have been resolved
-            //
-            if (tagMerger.getModel().isResolvedCompletely() && nodeListMerger.getModel().isFrozen()) {
-                commands.add(
-                        new VersionConflictResolveCommand(my, their)
-                );
-            }
-        }  else if (my instanceof Relation) {
-            // resolve the version conflict if this is a relation, all tag
-            // conflicts and all conflicts in the member list
-            // have been resolved
-            //
-            if (tagMerger.getModel().isResolvedCompletely() && relationMemberMerger.getModel().isFrozen()) {
-                commands.add(
-                        new VersionConflictResolveCommand(my, their)
-                );
-            }
-        }
-        return new SequenceCommand(tr("Conflict Resolution"), commands);
-    }
-
-    public boolean isCompletelyResolved() {
-        if (my instanceof Node) {
-            // resolve the version conflict if this is a node and all tag
-            // conflicts have been resolved
-            //
-            if (tagMerger.getModel().isResolvedCompletely())
+            if (tagMerger.getModel().isResolvedCompletely()
+                    && propertiesMerger.getModel().isResolvedCompletely())
                 return true;
         } else if (my instanceof Way) {
@@ -204,5 +207,7 @@
             // have been resolved
             //
-            if (tagMerger.getModel().isResolvedCompletely() && nodeListMerger.getModel().isFrozen())
+            if (tagMerger.getModel().isResolvedCompletely()
+                    &&  propertiesMerger.getModel().isResolvedCompletely()
+                    && nodeListMerger.getModel().isFrozen())
                 return true;
         }  else if (my instanceof Relation) {
@@ -211,5 +216,7 @@
             // have been resolved
             //
-            if (tagMerger.getModel().isResolvedCompletely() && relationMemberMerger.getModel().isFrozen())
+            if (tagMerger.getModel().isResolvedCompletely()
+                    &&  propertiesMerger.getModel().isResolvedCompletely()
+                    && relationMemberMerger.getModel().isFrozen())
                 return true;
         }
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/MergeDecisionType.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/MergeDecisionType.java	(revision 1654)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/MergeDecisionType.java	(revision 1654)
@@ -0,0 +1,8 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict;
+
+public enum MergeDecisionType {
+    KEEP_MINE,
+    KEEP_THEIR,
+    UNDECIDED,
+}
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/nodes/NodeListTableCellRenderer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/nodes/NodeListTableCellRenderer.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/nodes/NodeListTableCellRenderer.java	(revision 1654)
@@ -81,11 +81,23 @@
     }
 
-
+    /**
+     * build the tool tip text for an {@see OsmPrimitive}. It consist of the formatted
+     * key/value pairs for this primitive.
+     * 
+     * @param primitive
+     * @return the tool tip text
+     */
     public String buildToolTipText(OsmPrimitive primitive) {
         StringBuilder sb = new StringBuilder();
+
         sb.append("<html>");
+        // show the id
+        //
         sb.append("<strong>id</strong>=")
         .append(primitive.id)
         .append("<br>");
+
+        // show the key/value-pairs, sorted by key
+        //
         ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
         Collections.sort(keyList);
@@ -99,4 +111,7 @@
             .append("</strong>")
             .append("=");
+            // make sure long values are split into several rows. Otherwise
+            // the tool tip window can become to wide
+            //
             String value = primitive.get(key);
             while(value.length() != 0) {
@@ -121,6 +136,4 @@
         setForeground(Color.BLACK);
     }
-
-
 
     /**
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModel.java	(revision 1654)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModel.java	(revision 1654)
@@ -0,0 +1,220 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.properties;
+
+import static org.openstreetmap.josm.gui.conflict.MergeDecisionType.UNDECIDED;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Observable;
+
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.CoordinateConflictResolveCommand;
+import org.openstreetmap.josm.command.DeletedStateConflictResolveCommand;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
+
+/**
+ * This is the model for resolving conflicts in the values of same properties of
+ * {@see OsmPrimitive}. In particular, it represents conflicts in the coordiates of {@see Node}s and
+ * the deleted state of {@see OsmPrimitive}s.
+ * 
+ * This model is an {@see Observable}. It notifies registered {@see Observer}s whenever the
+ * internal state changes.
+ * 
+ * This model also emits property changes for {@see #RESOLVED_COMPLETELY_PROP}. Property change
+ * listeners may register themselves using {@see #addPropertyChangeListener(PropertyChangeListener)}.
+ * 
+ * @see Node#getCoor()
+ * @see OsmPrimitive#deleted
+ *
+ */
+public class PropertiesMergeModel extends Observable {
+
+    static public final String RESOLVED_COMPLETELY_PROP = PropertiesMergeModel.class.getName() + ".resolvedCompletely";
+
+    private LatLon myCoords;
+    private LatLon theirCoords;
+    private MergeDecisionType coordMergeDecision;
+
+    private boolean myDeletedState;
+    private boolean theirDeletedState;
+    private MergeDecisionType deletedMergeDecision;
+    private final PropertyChangeSupport support;
+    private boolean resolvedCompletely;
+
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        support.addPropertyChangeListener(listener);
+    }
+
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        support.removePropertyChangeListener(listener);
+    }
+
+    public void fireCompletelyResolved() {
+        boolean oldValue = resolvedCompletely;
+        resolvedCompletely = isResolvedCompletely();
+        support.firePropertyChange(RESOLVED_COMPLETELY_PROP, oldValue, resolvedCompletely);
+    }
+
+    public PropertiesMergeModel() {
+        coordMergeDecision = UNDECIDED;
+        deletedMergeDecision = UNDECIDED;
+        support = new PropertyChangeSupport(this);
+        resolvedCompletely = false;
+    }
+
+    /**
+     * replies true if there is a coordiate conflict and if this conflict is
+     * resolved
+     * 
+     * @return true if there is a coordiate conflict and if this conflict is
+     * resolved; false, otherwise
+     */
+    public boolean isDecidedCoord() {
+        return ! coordMergeDecision.equals(UNDECIDED);
+    }
+
+    /**
+     * replies true if there is a  conflict in the deleted state and if this conflict is
+     * resolved
+     * 
+     * @return true if there is a conflict in the deleted state and if this conflict is
+     * resolved; false, otherwise
+     */
+    public boolean isDecidedDeletedState() {
+        return ! deletedMergeDecision.equals(UNDECIDED);
+    }
+
+    /**
+     * replies true if the current decision for the coordiate conflict is <code>decision</code>
+     * 
+     * @return true if the current decision for the coordiate conflict is <code>decision</code>;
+     *  false, otherwise
+     */
+    public boolean isCoordMergeDecision(MergeDecisionType decision) {
+        return coordMergeDecision.equals(decision);
+    }
+
+    /**
+     * replies true if the current decision for the deleted state conflict is <code>decision</code>
+     * 
+     * @return true if the current decision for the deleted state conflict is <code>decision</code>;
+     *  false, otherwise
+     */
+    public boolean isDeletedStateDecision(MergeDecisionType decision) {
+        return deletedMergeDecision.equals(decision);
+    }
+
+    /**
+     * populates the model with the differences between my and their version
+     * 
+     * @param my my version of the primitive
+     * @param their their version of the primitive
+     */
+    public void populate(OsmPrimitive my, OsmPrimitive their) {
+        if (my instanceof Node) {
+            myCoords = ((Node)my).getCoor();
+            theirCoords = ((Node)their).getCoor();
+        } else {
+            myCoords = null;
+            theirCoords = null;
+        }
+
+        myDeletedState = my.deleted;
+        theirDeletedState = their.deleted;
+
+        coordMergeDecision = UNDECIDED;
+        deletedMergeDecision = UNDECIDED;
+        setChanged();
+        notifyObservers();
+        fireCompletelyResolved();
+    }
+
+
+    public LatLon getMyCoords() {
+        return myCoords;
+    }
+
+    public LatLon getTheirCoords() {
+        return theirCoords;
+    }
+
+    public LatLon getMergedCoords() {
+        switch(coordMergeDecision) {
+        case KEEP_MINE: return myCoords;
+        case KEEP_THEIR: return theirCoords;
+        case UNDECIDED: return null;
+        }
+        // should not happen
+        return null;
+    }
+
+    public void decideCoordsConflict(MergeDecisionType decision) {
+        coordMergeDecision = decision;
+        setChanged();
+        notifyObservers();
+        fireCompletelyResolved();
+    }
+
+    public Boolean getMyDeletedState() {
+        return myDeletedState;
+    }
+
+    public  Boolean getTheirDeletedState() {
+        return theirDeletedState;
+    }
+
+    public Boolean getMergedDeletedState() {
+        switch(deletedMergeDecision) {
+        case KEEP_MINE: return myDeletedState;
+        case KEEP_THEIR: return theirDeletedState;
+        case UNDECIDED: return null;
+        }
+        // should not happen
+        return null;
+    }
+
+    public void decideDeletedStateConflict(MergeDecisionType decision) {
+        this.deletedMergeDecision = decision;
+        setChanged();
+        notifyObservers();
+        fireCompletelyResolved();
+    }
+
+    public boolean hasCoordConflict() {
+        if (myCoords == null && theirCoords != null) return true;
+        if (myCoords != null && theirCoords == null) return true;
+        if (myCoords == null && theirCoords == null) return false;
+        return !myCoords.equalsEpsilon(theirCoords);
+    }
+
+    public boolean hasDeletedStateConflict() {
+        return myDeletedState != theirDeletedState;
+    }
+
+    public boolean isResolvedCompletely() {
+        boolean ret = true;
+        if (hasCoordConflict()) {
+            ret = ret && ! coordMergeDecision.equals(UNDECIDED);
+        }
+        if (hasDeletedStateConflict()) {
+            ret = ret && ! deletedMergeDecision.equals(UNDECIDED);
+        }
+        return ret;
+    }
+
+    public List<Command> buildResolveCommand(OsmPrimitive my, OsmPrimitive their) {
+        ArrayList<Command> cmds = new ArrayList<Command>();
+        if (hasCoordConflict() && isDecidedCoord()) {
+            cmds.add(new CoordinateConflictResolveCommand((Node)my, (Node)their, coordMergeDecision));
+        }
+        if (hasDeletedStateConflict() && isDecidedDeletedState()) {
+            cmds.add(new DeletedStateConflictResolveCommand(my, their, coordMergeDecision));
+        }
+        return cmds;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMerger.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMerger.java	(revision 1654)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMerger.java	(revision 1654)
@@ -0,0 +1,421 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.properties;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.text.DecimalFormat;
+import java.util.Observable;
+import java.util.Observer;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * This class represents a UI component for resolving conflicts in some properties
+ * of {@see OsmPrimitive}.
+ * 
+ */
+public class PropertiesMerger extends JPanel implements Observer {
+    private static DecimalFormat COORD_FORMATTER = new DecimalFormat("###0.0000");
+
+    public final static Color BGCOLOR_NO_CONFLICT = new Color(234,234,234);
+    public final static Color BGCOLOR_UNDECIDED = new Color(255,197,197);
+    public final static Color BGCOLOR_DECIDED = new Color(217,255,217);
+
+    private  JLabel lblMyVersion;
+    private  JLabel lblMergedVersion;
+    private  JLabel lblTheirVersion;
+
+    private JLabel lblMyCoordinates;
+    private JLabel lblMergedCoordinates;
+    private JLabel lblTheirCoordinates;
+
+    private JLabel lblMyDeletedState;
+    private JLabel lblMergedDeletedState;
+    private JLabel lblTheirDeletedState;
+
+    private final PropertiesMergeModel model;
+
+    protected JLabel buildValueLabel(String name) {
+        JLabel lbl = new JLabel();
+        lbl.setName(name);
+        lbl.setHorizontalAlignment(JLabel.CENTER);
+        lbl.setOpaque(true);
+        lbl.setBorder(BorderFactory.createLoweredBevelBorder());
+        return lbl;
+    }
+
+    protected void build() {
+        setLayout(new GridBagLayout());
+        GridBagConstraints gc = new GridBagConstraints();
+
+        // ------------------
+        gc.gridx = 1;
+        gc.gridy = 0;
+        gc.gridwidth = 1;
+        gc.gridheight = 1;
+        gc.fill = GridBagConstraints.NONE;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.insets = new Insets(10,0,10,0);
+        lblMyVersion = new JLabel(tr("My version"));
+        lblMyVersion.setToolTipText(tr("Properties in my dataset, i.e. the local dataset"));
+        add(lblMyVersion, gc);
+
+        gc.gridx = 3;
+        gc.gridy = 0;
+        lblMergedVersion = new JLabel(tr("Merged version"));
+        lblMergedVersion.setToolTipText(tr("Properties in the merged element. They will replace properties in my elements when  merge decisions are applied."));
+        add(lblMergedVersion, gc);
+
+        gc.gridx = 5;
+        gc.gridy = 0;
+        lblTheirVersion = new JLabel(tr("Their version"));
+        lblTheirVersion.setToolTipText(tr("Properties in their dataset, i.e. the server dataset"));
+        add(lblTheirVersion, gc);
+
+        // --------------------------------
+        gc.gridx = 0;
+        gc.gridy = 1;
+        gc.gridwidth = 1;
+        gc.gridheight = 1;
+        gc.fill = GridBagConstraints.HORIZONTAL;
+        gc.anchor = GridBagConstraints.LINE_START;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.insets = new Insets(0,5,0,5);
+        add(new JLabel(tr("Coordinates:")), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.33;
+        gc.weighty = 0.0;
+        add(lblMyCoordinates = buildValueLabel("label.mycoordinates"), gc);
+
+        gc.gridx = 2;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.NONE;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        KeepMyCoordinatesAction actKeepMyCoordinates = new KeepMyCoordinatesAction();
+        model.addObserver(actKeepMyCoordinates);
+        JButton btnKeepMyCoordinates = new JButton(actKeepMyCoordinates);
+        btnKeepMyCoordinates.setName("button.keepmycoordinates");
+        add(btnKeepMyCoordinates, gc);
+
+
+        gc.gridx = 3;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.33;
+        gc.weighty = 0.0;
+        add(lblMergedCoordinates = buildValueLabel("label.mergedcoordinates"), gc);
+
+        gc.gridx = 4;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.NONE;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        KeepTheirCoordinatesAction actKeepTheirCoordinates = new KeepTheirCoordinatesAction();
+        model.addObserver(actKeepTheirCoordinates);
+        JButton btnKeepTheirCoordinates = new JButton(actKeepTheirCoordinates);
+        add(btnKeepTheirCoordinates, gc);
+
+        gc.gridx = 5;
+        gc.gridy = 1;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.33;
+        gc.weighty = 0.0;
+        add(lblTheirCoordinates = buildValueLabel("label.theircoordinates"), gc);
+
+        // ---------------------------------------------------
+        gc.gridx = 3;
+        gc.gridy = 2;
+        gc.fill = GridBagConstraints.NONE;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.insets = new Insets(0,5,20,5);
+        UndecideCoordinateConflictAction actUndecideCoordinates = new UndecideCoordinateConflictAction();
+        model.addObserver(actUndecideCoordinates);
+        JButton btnUndecideCoordinates = new JButton(actUndecideCoordinates);
+        add(btnUndecideCoordinates, gc);
+        // ---------------------------------------------------
+
+        gc.gridx = 0;
+        gc.gridy = 3;
+        gc.gridwidth = 1;
+        gc.gridheight = 1;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.LINE_START;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.insets = new Insets(0,5,0,5);
+        add(new JLabel(tr("Deleted State:")), gc);
+
+        gc.gridx = 1;
+        gc.gridy = 3;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.33;
+        gc.weighty = 0.0;
+        add(lblMyDeletedState = buildValueLabel("label.mydeletedstate"), gc);
+
+        gc.gridx = 2;
+        gc.gridy = 3;
+        gc.fill = GridBagConstraints.NONE;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        KeepMyDeletedStateAction actKeepMyDeletedState = new KeepMyDeletedStateAction();
+        model.addObserver(actKeepMyDeletedState);
+        JButton btnKeepMyDeletedState = new JButton(actKeepMyDeletedState);
+        btnKeepMyCoordinates.setName("button.keepmydeletedstate");
+        add(btnKeepMyDeletedState, gc);
+
+
+        gc.gridx = 3;
+        gc.gridy = 3;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.33;
+        gc.weighty = 0.0;
+        add(lblMergedDeletedState = buildValueLabel("label.mergeddeletedstate"), gc);
+
+        gc.gridx = 4;
+        gc.gridy = 3;
+        gc.fill = GridBagConstraints.NONE;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        KeepTheirDeletedStateAction actKeepTheirDeletedState = new KeepTheirDeletedStateAction();
+        model.addObserver(actKeepTheirDeletedState);
+        JButton btnKeepTheirDeletedState = new JButton(actKeepTheirDeletedState);
+        btnKeepMyCoordinates.setName("button.keeptheirdeletedstate");
+        add(btnKeepTheirDeletedState, gc);
+
+        gc.gridx = 5;
+        gc.gridy = 3;
+        gc.fill = GridBagConstraints.BOTH;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.33;
+        gc.weighty = 0.0;
+        add(lblTheirDeletedState = buildValueLabel("label.theirdeletedstate"), gc);
+
+        // ---------------------------------------------------
+        gc.gridx = 3;
+        gc.gridy = 4;
+        gc.fill = GridBagConstraints.NONE;
+        gc.anchor = GridBagConstraints.CENTER;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        UndecideDeletedStateConflictAction actUndecideDeletedState = new UndecideDeletedStateConflictAction();
+        model.addObserver(actUndecideDeletedState);
+        JButton btnUndecideDeletedState = new JButton(actUndecideDeletedState);
+        btnKeepMyCoordinates.setName("button.undecidedeletedstate");
+        add(btnUndecideDeletedState, gc);
+
+    }
+
+    public PropertiesMerger() {
+        model = new PropertiesMergeModel();
+        model.addObserver(this);
+        build();
+    }
+
+    public String coordToString(LatLon coord) {
+        if (coord == null)
+            return tr("(none)");
+        StringBuilder sb = new StringBuilder();
+        sb.append("(")
+        .append(COORD_FORMATTER.format(coord.lat()))
+        .append(",")
+        .append(COORD_FORMATTER.format(coord.lon()))
+        .append(")");
+        return sb.toString();
+    }
+
+    public String deletedStateToString(Boolean deleted) {
+        if (deleted == null)
+            return tr("(none)");
+        if (deleted)
+            return tr("deleted");
+        else
+            return tr("not deleted");
+    }
+
+    protected void updateCoordiates() {
+        lblMyCoordinates.setText(coordToString(model.getMyCoords()));
+        lblMergedCoordinates.setText(coordToString(model.getMergedCoords()));
+        lblTheirCoordinates.setText(coordToString(model.getTheirCoords()));
+        if (! model.hasCoordConflict()) {
+            lblMyCoordinates.setBackground(BGCOLOR_NO_CONFLICT);
+            lblMergedCoordinates.setBackground(BGCOLOR_NO_CONFLICT);
+            lblTheirCoordinates.setBackground(BGCOLOR_NO_CONFLICT);
+        } else {
+            if (!model.isDecidedCoord()) {
+                lblMyCoordinates.setBackground(BGCOLOR_UNDECIDED);
+                lblMergedCoordinates.setBackground(BGCOLOR_NO_CONFLICT);
+                lblTheirCoordinates.setBackground(BGCOLOR_UNDECIDED);
+            } else {
+                lblMyCoordinates.setBackground(
+                        model.isCoordMergeDecision(MergeDecisionType.KEEP_MINE)
+                        ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT
+                );
+                lblMergedCoordinates.setBackground(BGCOLOR_DECIDED);
+                lblTheirCoordinates.setBackground(
+                        model.isCoordMergeDecision(MergeDecisionType.KEEP_THEIR)
+                        ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT
+                );
+            }
+        }
+    }
+
+    protected void updateDeletedState() {
+        lblMyDeletedState.setText(deletedStateToString(model.getMyDeletedState()));
+        lblMergedDeletedState.setText(deletedStateToString(model.getMergedDeletedState()));
+        lblTheirDeletedState.setText(deletedStateToString(model.getTheirDeletedState()));
+
+        if (! model.hasDeletedStateConflict()) {
+            lblMyDeletedState.setBackground(BGCOLOR_NO_CONFLICT);
+            lblMergedDeletedState.setBackground(BGCOLOR_NO_CONFLICT);
+            lblTheirDeletedState.setBackground(BGCOLOR_NO_CONFLICT);
+        } else {
+            if (!model.isDecidedDeletedState()) {
+                lblMyDeletedState.setBackground(BGCOLOR_UNDECIDED);
+                lblMergedDeletedState.setBackground(BGCOLOR_NO_CONFLICT);
+                lblTheirDeletedState.setBackground(BGCOLOR_UNDECIDED);
+            } else {
+                lblMyDeletedState.setBackground(
+                        model.isDeletedStateDecision(MergeDecisionType.KEEP_MINE)
+                        ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT
+                );
+                lblMergedDeletedState.setBackground(BGCOLOR_DECIDED);
+                lblTheirDeletedState.setBackground(
+                        model.isDeletedStateDecision(MergeDecisionType.KEEP_THEIR)
+                        ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT
+                );
+            }
+        }
+    }
+
+    public void update(Observable o, Object arg) {
+        updateCoordiates();
+        updateDeletedState();
+    }
+
+    public PropertiesMergeModel getModel() {
+        return model;
+    }
+
+    class KeepMyCoordinatesAction extends AbstractAction implements Observer {
+        public KeepMyCoordinatesAction() {
+            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine"));
+            putValue(Action.SHORT_DESCRIPTION, tr("Keep my coordiates"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            model.decideCoordsConflict(MergeDecisionType.KEEP_MINE);
+        }
+
+        public void update(Observable o, Object arg) {
+            setEnabled(model.hasCoordConflict() && ! model.isDecidedCoord());
+        }
+    }
+
+    class KeepTheirCoordinatesAction extends AbstractAction implements Observer {
+        public KeepTheirCoordinatesAction() {
+            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir"));
+            putValue(Action.SHORT_DESCRIPTION, tr("Keep their coordiates"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            model.decideCoordsConflict(MergeDecisionType.KEEP_THEIR);
+        }
+
+        public void update(Observable o, Object arg) {
+            setEnabled(model.hasCoordConflict() && ! model.isDecidedCoord());
+        }
+    }
+
+    class UndecideCoordinateConflictAction extends AbstractAction implements Observer {
+        public UndecideCoordinateConflictAction() {
+            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide"));
+            putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between different coordinates"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            model.decideCoordsConflict(MergeDecisionType.UNDECIDED);
+        }
+
+        public void update(Observable o, Object arg) {
+            setEnabled(model.hasCoordConflict() && model.isDecidedCoord());
+        }
+    }
+
+    class KeepMyDeletedStateAction extends AbstractAction implements Observer {
+        public KeepMyDeletedStateAction() {
+            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine"));
+            putValue(Action.SHORT_DESCRIPTION, tr("Keep my deleted state"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            model.decideDeletedStateConflict(MergeDecisionType.KEEP_MINE);
+        }
+
+        public void update(Observable o, Object arg) {
+            setEnabled(model.hasDeletedStateConflict() && ! model.isDecidedDeletedState());
+        }
+    }
+
+    class KeepTheirDeletedStateAction extends AbstractAction implements Observer {
+        public KeepTheirDeletedStateAction() {
+            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir"));
+            putValue(Action.SHORT_DESCRIPTION, tr("Keep their deleted state"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            model.decideDeletedStateConflict(MergeDecisionType.KEEP_THEIR);
+        }
+
+        public void update(Observable o, Object arg) {
+            setEnabled(model.hasDeletedStateConflict() && ! model.isDecidedDeletedState());
+        }
+    }
+
+    class UndecideDeletedStateConflictAction extends AbstractAction implements Observer {
+        public UndecideDeletedStateConflictAction() {
+            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide"));
+            putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between deleted state"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            model.decideDeletedStateConflict(MergeDecisionType.UNDECIDED);
+        }
+
+        public void update(Observable o, Object arg) {
+            setEnabled(model.hasDeletedStateConflict() && model.isDecidedDeletedState());
+        }
+    }
+}
Index: unk/src/org/openstreetmap/josm/gui/conflict/tags/MergeDecisionType.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/MergeDecisionType.java	(revision 1653)
+++ 	(revision )
@@ -1,8 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.conflict.tags;
-
-public enum MergeDecisionType {
-    KEEP_MINE,
-    KEEP_THEIR,
-    UNDECIDED,
-}
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/tags/MergedTableCellRenderer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/MergedTableCellRenderer.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/tags/MergedTableCellRenderer.java	(revision 1654)
@@ -5,4 +5,6 @@
 
 import java.awt.Color;
+
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
 
 public class MergedTableCellRenderer extends TagMergeTableCellRenderer {
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/tags/MineTableCellRenderer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/MineTableCellRenderer.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/tags/MineTableCellRenderer.java	(revision 1654)
@@ -3,4 +3,7 @@
 
 import java.awt.Color;
+
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
+
 import static org.openstreetmap.josm.tools.I18n.tr;
 
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMergeItem.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMergeItem.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMergeItem.java	(revision 1654)
@@ -7,4 +7,5 @@
 
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
 
 /**
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMergeModel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMergeModel.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMergeModel.java	(revision 1654)
@@ -13,4 +13,5 @@
 import org.openstreetmap.josm.command.TagConflictResolveCommand;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
 
 /**
@@ -21,6 +22,6 @@
  * 
  *  {@see #decide(int, MergeDecisionType)} and {@see #decide(int[], MergeDecisionType)} can be used
- *  to remember a merge decision for a specific row in the model. 
- *  
+ *  to remember a merge decision for a specific row in the model.
+ * 
  *  The model notifies {@see PropertyChangeListener}s about updates of the number of
  *  undecided tags (see {@see #PROP_NUM_UNDECIDED_TAGS}).
@@ -31,19 +32,19 @@
 
     static public final String PROP_NUM_UNDECIDED_TAGS = TagMergeModel.class.getName() + ".numUndecidedTags";
-    
+
     /** the list of tag merge items */
-    private ArrayList<TagMergeItem> tagMergeItems;
-    
+    private final ArrayList<TagMergeItem> tagMergeItems;
+
     /** the property change listeners */
-    private ArrayList<PropertyChangeListener> listeners;
-    
+    private final ArrayList<PropertyChangeListener> listeners;
+
     private int numUndecidedTags = 0;
-    
-    
+
+
     public TagMergeModel() {
         tagMergeItems = new ArrayList<TagMergeItem>();
         listeners = new ArrayList<PropertyChangeListener>();
     }
-    
+
     public void addPropertyChangeListener(PropertyChangeListener listener) {
         synchronized(listeners) {
@@ -53,5 +54,5 @@
         }
     }
-    
+
     public void removePropertyChangeListener(PropertyChangeListener listener) {
         synchronized(listeners) {
@@ -61,10 +62,10 @@
         }
     }
-    
+
     /**
      * notifies {@see PropertyChangeListener}s about an update of {@see TagMergeModel#PROP_NUM_UNDECIDED_TAGS}
 
-     * @param oldValue the old value 
-     * @param newValue the new value 
+     * @param oldValue the old value
+     * @param newValue the new value
      */
     protected void fireNumUndecidedTagsChanged(int oldValue, int newValue) {
@@ -76,5 +77,5 @@
         }
     }
-    
+
     /**
      * refreshes the number of undecided tag conflicts after an update in the list of
@@ -89,17 +90,16 @@
             }
         }
-        if (newValue != numUndecidedTags) {
-            int oldValue = numUndecidedTags;
-            numUndecidedTags = newValue;
-            fireNumUndecidedTagsChanged(oldValue, numUndecidedTags);
-        }
-    }
-    
-    /**
-     * Populate the model with conflicts between the tag sets of the two 
+        int oldValue = numUndecidedTags;
+        numUndecidedTags = newValue;
+        fireNumUndecidedTagsChanged(oldValue, numUndecidedTags);
+
+    }
+
+    /**
+     * Populate the model with conflicts between the tag sets of the two
      * {@see OsmPrimitive} <code>my</code> and <code>their</code>.
      * 
      * @param my  my primitive (i.e. the primitive from the local dataset)
-     * @param their their primitive (i.e. the primitive from the server dataset) 
+     * @param their their primitive (i.e. the primitive from the server dataset)
      * 
      */
@@ -114,5 +114,5 @@
             if (myValue == null || theirValue == null || ! myValue.equals(theirValue)) {
                 tagMergeItems.add(
-                  new TagMergeItem(key, my, their)      
+                        new TagMergeItem(key, my, their)
                 );
             }
@@ -121,9 +121,9 @@
         refreshNumUndecidedTags();
     }
-    
+
     /**
      * add a {@see TagMergeItem} to the model
      * 
-     * @param item the item 
+     * @param item the item
      */
     public void addItem(TagMergeItem item) {
@@ -134,5 +134,5 @@
         }
     }
-    
+
     protected void rememberDecision(int row, MergeDecisionType decision) {
         TagMergeItem item = tagMergeItems.get(row);
@@ -144,6 +144,6 @@
      * to <code>decision</code>.
      * 
-     * @param row  the row 
-     * @param decision the decision 
+     * @param row  the row
+     * @param decision the decision
      */
     public void decide(int row, MergeDecisionType decision) {
@@ -157,12 +157,11 @@
      * to <code>decision</code>.
      * 
-     * @param row  the array of row indices  
-     * @param decision the decision 
+     * @param row  the array of row indices
+     * @param decision the decision
      */
 
     public void decide(int [] rows, MergeDecisionType decision) {
-        if (rows == null || rows.length == 0) {
+        if (rows == null || rows.length == 0)
             return;
-        }
         for (int row : rows) {
             rememberDecision(row, decision);
@@ -182,5 +181,5 @@
         // return the tagMergeItem for both columns. The cell
         // renderer will dispatch on the column index and get
-        // the key or the value from the TagMergeItem 
+        // the key or the value from the TagMergeItem
         //
         return tagMergeItems.get(row);
@@ -191,17 +190,26 @@
         return false;
     }
-    
-    
+
     public TagConflictResolveCommand buildResolveCommand(OsmPrimitive my, OsmPrimitive their) {
         return new TagConflictResolveCommand(my,  their, tagMergeItems);
     }
-    
+
     public boolean isResolvedCompletely() {
         for (TagMergeItem item: tagMergeItems) {
-            if (item.getMergeDecision().equals(MergeDecisionType.UNDECIDED)) {
-                return false; 
-            }
-        }
-        return true; 
+            if (item.getMergeDecision().equals(MergeDecisionType.UNDECIDED))
+                return false;
+        }
+        return true;
+    }
+
+    public int getNumResolvedConflicts() {
+        int n = 0;
+        for (TagMergeItem item: tagMergeItems) {
+            if (!item.getMergeDecision().equals(MergeDecisionType.UNDECIDED)) {
+                n++;
+            }
+        }
+        return n;
+
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMerger.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMerger.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagMerger.java	(revision 1654)
@@ -26,4 +26,5 @@
 import javax.swing.event.ListSelectionListener;
 
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
 import org.openstreetmap.josm.tools.ImageProvider;
 /**
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TheirTableCellRenderer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TheirTableCellRenderer.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/tags/TheirTableCellRenderer.java	(revision 1654)
@@ -5,4 +5,6 @@
 
 import java.awt.Color;
+
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
 
 public class TheirTableCellRenderer extends TagMergeTableCellRenderer {
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictResolutionDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictResolutionDialog.java	(revision 1653)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictResolutionDialog.java	(revision 1654)
@@ -169,5 +169,5 @@
 
         public void actionPerformed(ActionEvent arg0) {
-            if (! resolver.isCompletelyResolved()) {
+            if (! resolver.isResolvedCompletely()) {
                 Object[] options = {
                         tr("Apply partial resolutions"),
Index: /trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeItemTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeItemTest.java	(revision 1653)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeItemTest.java	(revision 1654)
@@ -4,5 +4,5 @@
 import org.junit.*;
 import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.gui.conflict.tags.MergeDecisionType;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
 import org.openstreetmap.josm.gui.conflict.tags.TagMergeItem;
 
Index: /trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeModelTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeModelTest.java	(revision 1653)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeModelTest.java	(revision 1654)
@@ -12,4 +12,5 @@
 import org.junit.Test;
 import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
 
 public class TagMergeModelTest {
