Index: /trunk/src/org/openstreetmap/josm/command/ChangePropertyCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/ChangePropertyCommand.java	(revision 4772)
+++ /trunk/src/org/openstreetmap/josm/command/ChangePropertyCommand.java	(revision 4773)
@@ -5,10 +5,13 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import javax.swing.JLabel;
@@ -32,37 +35,75 @@
     private final List<OsmPrimitive> objects;
     /**
-     * The key that is subject to change.
-     */
-    private final String key;
-    /**
-     * The key value. If it is <code>null</code>, delete all key references with the given
+     * Key and value pairs. If value is <code>null</code>, delete all key references with the given
      * key. Otherwise, change the properties of all objects to the given value or create keys of
      * those objects that do not have the key yet.
      */
-    private final String value;
-
-    public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, String key, String value) {
+    private final AbstractMap<String, String> tags;
+
+    /**
+     * Creates a command to change multiple properties of multiple objects
+     *
+     * @param objects the objects to modify
+     * @param tags the properties to set
+     */
+    public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, AbstractMap<String, String> tags) {
         super();
         this.objects = new LinkedList<OsmPrimitive>();
-        this.key = key;
-        this.value = (value == null || value.isEmpty()) ? null : value;
-        if (this.value == null) {
-            for (OsmPrimitive osm : objects) {
-                if(osm.get(key) != null) {
-                    this.objects.add(osm);
-                }
-            }
-        } else {
-            for (OsmPrimitive osm : objects) {
-                String val = osm.get(key);
-                if (val == null || !this.value.equals(val)) {
-                    this.objects.add(osm);
-                }
-            }
-        }
-    }
-
+        this.tags = tags;
+        init(objects);
+    }
+
+    /**
+     * Creates a command to change one property of multiple objects
+     *
+     * @param objects the objects to modify
+     * @param key the key of the property to set
+     * @param value the value of the key to set
+     */
+    public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, String key, String value) {
+        this.objects = new LinkedList<OsmPrimitive>();
+        this.tags = new HashMap<String, String>(1);
+        this.tags.put(key, value);
+        init(objects);
+    }
+
+    /**
+     * Creates a command to change on property of one object
+     *
+     * @param object the object to modify
+     * @param key the key of the property to set
+     * @param value the value of the key to set
+     */
     public ChangePropertyCommand(OsmPrimitive object, String key, String value) {
         this(Arrays.asList(object), key, value);
+    }
+
+    /**
+     * Initialize the instance by finding what objects will be modified
+     *
+     * @param objects the objects to (possibly) modify
+     */
+    private void init(Collection<? extends OsmPrimitive> objects) {
+        // determine what objects will be modified
+        for (OsmPrimitive osm : objects) {
+            boolean modified = false;
+
+            // loop over all tags
+            for (Map.Entry<String, String> tag : this.tags.entrySet()) {
+                String oldVal = osm.get(tag.getKey());
+                String newVal = tag.getValue();
+
+                if (newVal == null || newVal.isEmpty()) {
+                    if (oldVal != null)
+                        // new value is null and tag exists (will delete tag)
+                        modified = true;
+                }
+                else if (oldVal == null || !newVal.equals(oldVal))
+                    // new value is not null and is different from current value
+                    modified = true;
+            }
+            if (modified)
+                this.objects.add(osm);
+        }
     }
 
@@ -71,14 +112,25 @@
         try {
             super.executeCommand(); // save old
-            if (value == null) {
-                for (OsmPrimitive osm : objects) {
+
+            for (OsmPrimitive osm : objects) {
+                boolean modified = false;
+
+                 // loop over all tags
+                for (Map.Entry<String, String> tag : this.tags.entrySet()) {
+                    String oldVal = osm.get(tag.getKey());
+                    String newVal = tag.getValue();
+
+                    if (newVal == null || newVal.isEmpty()) {
+                        if (oldVal != null) {
+                            osm.remove(tag.getKey());
+                            modified = true;
+                        }
+                    }
+                    else if (oldVal == null || !newVal.equals(oldVal))
+                        osm.put(tag.getKey(), newVal);
+                        modified = true;
+                }
+                if (modified)
                     osm.setModified(true);
-                    osm.remove(key);
-                }
-            } else {
-                for (OsmPrimitive osm : objects) {
-                    osm.setModified(true);
-                    osm.put(key, value);
-                }
             }
             return true;
@@ -95,8 +147,9 @@
     @Override public JLabel getDescription() {
         String text;
-        if (objects.size() == 1) {
+        if (objects.size() == 1 && tags.size() == 1) {
             OsmPrimitive primitive = objects.iterator().next();
             String msg = "";
-            if (value == null) {
+            Map.Entry<String, String> entry = tags.entrySet().iterator().next();
+            if (entry.getValue() == null) {
                 switch(OsmPrimitiveType.from(primitive)) {
                 case NODE: msg = marktr("Remove \"{0}\" for node ''{1}''"); break;
@@ -104,5 +157,5 @@
                 case RELATION: msg = marktr("Remove \"{0}\" for relation ''{1}''"); break;
                 }
-                text = tr(msg, key, primitive.getDisplayName(DefaultNameFormatter.getInstance()));
+                text = tr(msg, entry.getKey(), primitive.getDisplayName(DefaultNameFormatter.getInstance()));
             } else {
                 switch(OsmPrimitiveType.from(primitive)) {
@@ -111,10 +164,26 @@
                 case RELATION: msg = marktr("Set {0}={1} for relation ''{2}''"); break;
                 }
-                text = tr(msg, key, value, primitive.getDisplayName(DefaultNameFormatter.getInstance()));
-            }
-        } else {
-            text = value == null
-            ? tr("Remove \"{0}\" for {1} objects", key, objects.size())
-                    : tr("Set {0}={1} for {2} objects", key, value, objects.size());
+                text = tr(msg, entry.getKey(), entry.getValue(), primitive.getDisplayName(DefaultNameFormatter.getInstance()));
+            }
+        } else if (objects.size() > 1 && tags.size() == 1) {
+            Map.Entry<String, String> entry = tags.entrySet().iterator().next();
+            if (entry.getValue() == null)
+                text = tr("Remove \"{0}\" for {1} objects", entry.getKey(), objects.size());
+            else
+                text = tr("Set {0}={1} for {2} objects", entry.getKey(), entry.getValue(), objects.size());
+        }
+        else {
+            boolean allnull = true;
+            for (Map.Entry<String, String> tag : this.tags.entrySet()) {
+                if (tag.getValue() != null) {
+                    allnull = false;
+                    break;
+                }
+            }
+            
+            if (allnull) {
+                text = tr("Deleted {0} properties for {1} objects", tags.size(), objects.size());
+            } else
+                text = tr("Set {0} properties for {1} objects", tags.size(), objects.size());
         }
         return new JLabel(text, ImageProvider.get("data", "key"), JLabel.HORIZONTAL);
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 4772)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 4773)
@@ -1045,15 +1045,30 @@
         }
 
-        protected void deleteProperty(int row){
-            String key = propertyData.getValueAt(row, 0).toString();
-
+        protected void deleteProperties(int[] rows){
+            // convert list of rows to HashMap (and find gap for nextKey)
+            HashMap<String, String> tags = new HashMap<String, String>(rows.length);
+            int nextKeyIndex = rows[0];
+            for (int row : rows) {
+                String key = propertyData.getValueAt(row, 0).toString();
+                if (row == nextKeyIndex + 1)
+                    nextKeyIndex = row; // no gap yet
+                tags.put(key, null);
+            }
+
+            // find key to select after deleting other properties
             String nextKey = null;
             int rowCount = propertyData.getRowCount();
-            if (rowCount > 1) {
-                nextKey = (String)propertyData.getValueAt((row + 1 < rowCount ? row + 1 : row - 1), 0);
+            if (rowCount > rows.length) {
+                if (nextKeyIndex == rows[rows.length-1])
+                    // no gap found, pick next or previous key in list
+                    nextKeyIndex = (nextKeyIndex + 1 < rowCount ? nextKeyIndex + 1 : rows[0] - 1);
+                else
+                    // gap found
+                    nextKeyIndex++;
+                nextKey = (String)propertyData.getValueAt(nextKeyIndex, 0);
             }
 
             Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
-            Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, null));
+            Main.main.undoRedo.add(new ChangePropertyCommand(sel, tags));
 
             membershipTable.clearSelection();
@@ -1100,7 +1115,5 @@
             if (propertyTable.getSelectedRowCount() > 0) {
                 int[] rows = propertyTable.getSelectedRows();
-                for (int i = rows.length - 1; i >= 0; i--) {
-                    deleteProperty(rows[i]);
-                }
+                deleteProperties(rows);
             } else if (membershipTable.getSelectedRowCount() > 0) {
                 int row = membershipTable.getSelectedRow();
