Index: trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PresetListPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PresetListPanel.java	(revision 3518)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PresetListPanel.java	(revision 3518)
@@ -0,0 +1,150 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.properties;
+
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagLayout;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Tag;
+import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.Check;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.Combo;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.PresetType;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.Text;
+import org.openstreetmap.josm.tools.GBC;
+
+public class PresetListPanel extends JPanel {
+
+    public PresetListPanel() {
+        super(new GridBagLayout());
+    }
+
+    public interface PresetHandler {
+        Collection<OsmPrimitive> getSelection();
+        void updateTags(List<Tag> tags);
+    }
+
+    /**
+     * Small helper class that manages the highlighting of the label on hover as well as opening
+     * the corresponding preset when clicked
+     */
+    private static class PresetLabelML implements MouseListener {
+        final JLabel label;
+        final Font bold;
+        final Font normal;
+        final TaggingPreset tag;
+        final PresetHandler presetHandler;
+
+        PresetLabelML(JLabel lbl, TaggingPreset t, PresetHandler presetHandler) {
+            super();
+            label = lbl;
+            lbl.setCursor(new Cursor(Cursor.HAND_CURSOR));
+            normal = label.getFont();
+            bold = normal.deriveFont(normal.getStyle() ^ Font.BOLD);
+            tag = t;
+            this.presetHandler = presetHandler;
+        }
+        public void mouseClicked(MouseEvent arg0) {
+            Collection<OsmPrimitive> selection = presetHandler.getSelection();
+            if (selection == null || selection.isEmpty())
+                return;
+            int answer = tag.showDialog(selection, false);
+
+            if (answer == TaggingPreset.DIALOG_ANSWER_APPLY) {
+                presetHandler.updateTags(tag.getChangedTags());
+            }
+
+        }
+        public void mouseEntered(MouseEvent arg0) {
+            label.setFont(bold);
+        }
+        public void mouseExited(MouseEvent arg0) {
+            label.setFont(normal);
+        }
+        public void mousePressed(MouseEvent arg0) {}
+        public void mouseReleased(MouseEvent arg0) {}
+    }
+
+    public void updatePresets(int nodes, int ways, int relations, int closedways, Map<String, Map<String, Integer>> valueCount, PresetHandler presetHandler)  {
+
+        removeAll();
+        int total = nodes+ways+relations+closedways;
+        if(total == 0) {
+            setVisible(false);
+            return;
+        }
+
+        for(TaggingPreset t : TaggingPresetPreference.taggingPresets) {
+            if(
+                    (       t.types == null
+                            || (relations > 0 && t.types.contains(PresetType.RELATION))
+                            || (nodes > 0 && t.types.contains(PresetType.NODE))
+                            || (ways+closedways > 0 && t.types.contains(PresetType.WAY))
+                            || (closedways > 0 && t.types.contains(PresetType.CLOSEDWAY))
+                    )
+                    && t.isShowable())
+            {
+                int found = 0;
+                for(TaggingPreset.Item i : t.data) {
+                    if(i instanceof TaggingPreset.Key) {
+                        String val = ((TaggingPreset.Key)i).value;
+                        String key = ((TaggingPreset.Key)i).key;
+                        // we subtract 100 if not found and add 1 if found
+                        found -= 100;
+                        if(key == null || !valueCount.containsKey(key)) {
+                            continue;
+                        }
+
+                        Map<String, Integer> v = valueCount.get(key);
+                        if(v.size() == 1 && val != null && v.containsKey(val) && v.get(val) == total) {
+                            found += 101;
+                        }
+                    } else {
+                        String key = null;
+                        if ((i instanceof Text) && ((Text)i).required) {
+                            key = ((Text)i).key;
+                        } else if ((i instanceof Combo) && ((Combo)i).required) {
+                            key = ((Combo)i).key;
+                        } else if ((i instanceof Check) && ((Check)i).required) {
+                            key = ((Check)i).key;
+                        }
+                        if (key != null && valueCount.get(key) == null) {
+                            found -= 100;
+                        }
+                    }
+                }
+
+                if(found <= 0) {
+                    continue;
+                }
+
+                JLabel lbl = new JLabel(t.getName());
+                lbl.addMouseListener(new PresetLabelML(lbl, t, presetHandler));
+                add(lbl, GBC.eol().fill(GBC.HORIZONTAL));
+            }
+        }
+
+        if(getComponentCount() > 0) {
+            setVisible(true);
+            // This ensures the presets are exactly as high as needed.
+            int height = getComponentCount() * getComponent(0).getHeight();
+            Dimension size = new Dimension(getWidth(), height);
+            setMaximumSize(size);
+            setMinimumSize(size);
+        } else {
+            setVisible(false);
+        }
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 3515)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 3518)
@@ -7,10 +7,8 @@
 import java.awt.BorderLayout;
 import java.awt.Component;
-import java.awt.Cursor;
-import java.awt.Dialog.ModalityType;
-import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.GridBagLayout;
 import java.awt.Point;
+import java.awt.Dialog.ModalityType;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -20,5 +18,4 @@
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -66,4 +63,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
@@ -78,7 +76,7 @@
 import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel.PresetHandler;
 import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
 import org.openstreetmap.josm.gui.tagging.TaggingPreset;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingComboBox;
@@ -480,5 +478,5 @@
     private final SideButton btnEdit;
     private final SideButton btnDel;
-    private final JPanel presets = new JPanel(new GridBagLayout());
+    private final PresetListPanel presets = new PresetListPanel();
 
     private final JLabel selectSth = new JLabel("<html><p>"
@@ -716,90 +714,4 @@
     }
 
-    private void checkPresets(int nodes, int ways, int relations, int closedways)
-    {
-        /**
-         * Small helper class that manages the highlighting of the label on hover as well as opening
-         * the corresponding preset when clicked
-         */
-        class PresetLabelML implements MouseListener {
-            JLabel label;
-            Font bold;
-            Font normal;
-            TaggingPreset tag;
-            PresetLabelML(JLabel lbl, TaggingPreset t) {
-                super();
-                label = lbl;
-                lbl.setCursor(new Cursor(Cursor.HAND_CURSOR));
-                normal = label.getFont();
-                bold = normal.deriveFont(normal.getStyle() ^ Font.BOLD);
-                tag = t;
-            }
-            public void mouseClicked(MouseEvent arg0) {
-                tag.actionPerformed(null);
-            }
-            public void mouseEntered(MouseEvent arg0) {
-                label.setFont(bold);
-            }
-            public void mouseExited(MouseEvent arg0) {
-                label.setFont(normal);
-            }
-            public void mousePressed(MouseEvent arg0) {}
-            public void mouseReleased(MouseEvent arg0) {}
-        }
-
-        presets.removeAll();
-        int total = nodes+ways+relations+closedways;
-        if(total == 0) {
-            presets.setVisible(false);
-            return;
-        }
-
-        for(TaggingPreset t : TaggingPresetPreference.taggingPresets) {
-            if((t.types == null || !((relations > 0 && !t.types.contains("relation")) &&
-                    (nodes > 0 && !t.types.contains("node")) &&
-                    (ways+closedways > 0 && !t.types.contains("way")) &&
-                    (closedways > 0 && !t.types.contains("closedway")))) && t.isShowable())
-            {
-                int found = 0;
-                for(TaggingPreset.Item i : t.data) {
-                    if(!(i instanceof TaggingPreset.Key)) {
-                        continue;
-                    }
-                    String val = ((TaggingPreset.Key)i).value;
-                    String key = ((TaggingPreset.Key)i).key;
-                    // we subtract 100 if not found and add 1 if found
-                    found -= 100;
-                    if(key == null || !valueCount.containsKey(key)) {
-                        continue;
-                    }
-
-                    Map<String, Integer> v = valueCount.get(key);
-                    if(v.size() == 1 && val != null && v.containsKey(val) && v.get(val) == total) {
-                        found += 101;
-                    }
-                }
-
-                if(found <= 0) {
-                    continue;
-                }
-
-                JLabel lbl = new JLabel(t.getName());
-                lbl.addMouseListener(new PresetLabelML(lbl, t));
-                presets.add(lbl, GBC.eol().fill(GBC.HORIZONTAL));
-            }
-        }
-
-        if(presets.getComponentCount() > 0) {
-            presets.setVisible(true);
-            // This ensures the presets are exactly as high as needed.
-            int height = presets.getComponentCount() * presets.getComponent(0).getHeight();
-            Dimension size = new Dimension(presets.getWidth(), height);
-            presets.setMaximumSize(size);
-            presets.setMinimumSize(size);
-        } else {
-            presets.setVisible(false);
-        }
-    }
-
     private int findRow(TableModel model, Object value) {
         for (int i=0; i<model.getRowCount(); i++) {
@@ -809,4 +721,23 @@
         return -1;
     }
+
+    private PresetHandler presetHandler = new PresetHandler() {
+
+        @Override
+        public void updateTags(List<Tag> tags) {
+            Command command = TaggingPreset.createCommand(getSelection(), tags);
+            if (command != null) {
+                Main.main.undoRedo.add(command);
+            }
+        }
+
+        @Override
+        public Collection<OsmPrimitive> getSelection() {
+            if (Main.main == null) return null;
+            if (Main.main.getCurrentDataSet() == null) return null;
+
+            return Main.main.getCurrentDataSet().getSelected();
+        }
+    };
 
     public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
@@ -870,8 +801,4 @@
             propertyData.addRow(new Object[]{e.getKey(), e.getValue()});
         }
-
-        // re-load membership data
-        // this is rather expensive since we have to walk through all members of all existing relationships.
-        // could use back references here for speed if necessary.
 
         membershipData.setRowCount(0);
@@ -913,5 +840,5 @@
         }
 
-        checkPresets(nodes, ways, relations, closedways);
+        presets.updatePresets(nodes, ways, relations, closedways, valueCount, presetHandler);
 
         membershipTable.getTableHeader().setVisible(membershipData.getRowCount() > 0);
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 3515)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 3518)
@@ -24,7 +24,9 @@
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.logging.Logger;
@@ -61,4 +63,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
 import org.openstreetmap.josm.gui.DefaultNameFormatter;
@@ -66,8 +69,10 @@
 import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
+import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel.PresetHandler;
 import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
+import org.openstreetmap.josm.gui.tagging.TagModel;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
@@ -123,5 +128,39 @@
         referrerModel = new ReferringRelationsBrowserModel(relation);
 
-        tagEditorPanel = new TagEditorPanel();
+        tagEditorPanel = new TagEditorPanel(new PresetHandler() {
+
+            @Override
+            public void updateTags(List<Tag> tags) {
+                Map<String, TagModel> modelTags = new HashMap<String, TagModel>();
+                for (int i=0; i<tagEditorPanel.getModel().getRowCount(); i++) {
+                    TagModel tagModel = tagEditorPanel.getModel().get(i);
+                    modelTags.put(tagModel.getName(), tagModel);
+                }
+                for (Tag tag: tags) {
+                    TagModel existing = modelTags.get(tag.getKey());
+
+                    if (tag.getValue().isEmpty()) {
+                        if (existing != null) {
+                            tagEditorPanel.getModel().delete(tag.getKey());
+                        }
+                    } else {
+                        if (existing != null) {
+                            tagEditorPanel.getModel().updateTagValue(existing, tag.getValue());
+                        } else {
+                            tagEditorPanel.getModel().add(tag.getKey(), tag.getValue());
+                        }
+                    }
+
+                }
+            }
+
+            @Override
+            public Collection<OsmPrimitive> getSelection() {
+                Relation relation = new Relation();
+                tagEditorPanel.getModel().applyToPrimitive(relation);
+                return Collections.<OsmPrimitive>singletonList(relation);
+            }
+        });
+
         // populate the models
         //
Index: trunk/src/org/openstreetmap/josm/gui/io/TagSettingsPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/TagSettingsPanel.java	(revision 3515)
+++ trunk/src/org/openstreetmap/josm/gui/io/TagSettingsPanel.java	(revision 3518)
@@ -27,10 +27,10 @@
     protected void build() {
         setLayout(new BorderLayout());
-        add(pnlTagEditor = new TagEditorPanel(), BorderLayout.CENTER);
+        add(pnlTagEditor = new TagEditorPanel(null), BorderLayout.CENTER);
     }
 
     /**
      * Creates a new panel
-     * 
+     *
      * @param changesetCommentModel the changeset comment model. Must not be null.
      * @throws IllegalArgumentException thrown if {@code changesetCommentModel} is null
Index: trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java	(revision 3515)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java	(revision 3518)
@@ -50,5 +50,5 @@
      * Creates a new tag editor model. Internally allocates two selection models
      * for row selection and column selection.
-     * 
+     *
      * To create a {@see JTable} with this model:
      * <pre>
@@ -56,5 +56,5 @@
      *    TagTable tbl  = new TagTabel(model);
      * </pre>
-     * 
+     *
      * @see #getRowSelectionModel()
      * @see #getColumnSelectionModel()
@@ -66,5 +66,5 @@
     /**
      * Creates a new tag editor model.
-     * 
+     *
      * @param rowSelectionModel the row selection model. Must not be null.
      * @param colSelectionModel the column selection model. Must not be null.
@@ -85,5 +85,5 @@
     /**
      * Replies the row selection model used by this tag editor model
-     * 
+     *
      * @return the row selection model used by this tag editor model
      */
@@ -94,5 +94,5 @@
     /**
      * Replies the column selection model used by this tag editor model
-     * 
+     *
      * @return the column selection model used by this tag editor model
      */
@@ -355,4 +355,5 @@
         tags.add(tag);
         setDirty(false);
+        fireTableDataChanged();
     }
 
@@ -441,5 +442,5 @@
     /**
      * Replies the the tags in this tag editor model as {@see TagCollection}.
-     * 
+     *
      * @return the the tags in this tag editor model as {@see TagCollection}
      */
Index: trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorPanel.java	(revision 3515)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorPanel.java	(revision 3518)
@@ -6,4 +6,7 @@
 import java.awt.GridBagLayout;
 import java.awt.Insets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.logging.Logger;
 
@@ -12,5 +15,9 @@
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
 
+import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel;
+import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel.PresetHandler;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
@@ -33,4 +40,7 @@
     private TagTable tagTable;
 
+    private PresetListPanel presetListPanel;
+    private final PresetHandler presetHandler;
+
     private AutoCompletionManager autocomplete;
     private AutoCompletionList acList;
@@ -46,4 +56,8 @@
         pnl.setLayout(new BorderLayout());
         pnl.add(new JScrollPane(tagTable), BorderLayout.CENTER);
+        if (presetHandler != null) {
+            presetListPanel = new PresetListPanel();
+            pnl.add(presetListPanel, BorderLayout.NORTH);
+        }
         return pnl;
     }
@@ -98,4 +112,13 @@
         gc.anchor = GridBagConstraints.CENTER;
         add(tablePanel,gc);
+
+        if (presetHandler != null) {
+            model.addTableModelListener(new TableModelListener() {
+                @Override
+                public void tableChanged(TableModelEvent e) {
+                    updatePresets();
+                }
+            });
+        }
     }
 
@@ -104,6 +127,6 @@
      * internally and can be retrieved with {@see #getModel()}.
      */
-    public TagEditorPanel() {
-        this(null);
+    public TagEditorPanel(PresetHandler presetHandler) {
+        this(null, presetHandler);
     }
 
@@ -111,9 +134,10 @@
      * Creates a new tag editor panel with a supplied model. If
      * {@code model} is null, a new model is created.
-     * 
+     *
      * @param model the tag editor model
      */
-    public TagEditorPanel(TagEditorModel model) {
+    public TagEditorPanel(TagEditorModel model, PresetHandler presetHandler) {
         this.model = model;
+        this.presetHandler = presetHandler;
         if (this.model == null) {
             this.model = new TagEditorModel();
@@ -135,5 +159,5 @@
      * tag editor panel. {@code layer} is the data layer from whose data set
      * tag values are proposed as auto completion items.
-     * 
+     *
      * @param layer the data layer. Must not be null.
      * @throws IllegalArgumentException thrown if {@code layer} is null
@@ -158,3 +182,14 @@
         super.setEnabled(enabled);
     }
+
+    private void updatePresets() {
+        Map<String, Map<String, Integer>> valuesCount = new HashMap<String, Map<String,Integer>>();
+        for (Entry<String, String> entry: model.getTags().entrySet()) {
+            Map<String, Integer> values = new HashMap<String, Integer>();
+            values.put(entry.getValue(), 1);
+            valuesCount.put(entry.getKey(), values);
+        }
+        presetListPanel.updatePresets(0, 0, 1, 0, valuesCount, presetHandler);
+        validate();
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java	(revision 3515)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java	(revision 3518)
@@ -19,6 +19,8 @@
 import java.io.Reader;
 import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -37,7 +39,9 @@
 import javax.swing.JPanel;
 import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
 import javax.xml.transform.stream.StreamSource;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
 import org.openstreetmap.josm.command.ChangePropertyCommand;
 import org.openstreetmap.josm.command.Command;
@@ -47,4 +51,5 @@
 import org.openstreetmap.josm.data.osm.OsmUtils;
 import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.ExtendedDialog;
@@ -88,6 +93,9 @@
             return name().toLowerCase();
         }
-
-    }
+    }
+
+    public static final int DIALOG_ANSWER_APPLY = 1;
+    public static final int DIALOG_ANSWER_NEW_RELATION = 2;
+    public static final int DIALOG_ANSWER_CANCEL = 3;
 
     public TaggingPresetMenu group = null;
@@ -109,5 +117,5 @@
         public boolean focus = false;
         abstract boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel);
-        abstract void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds);
+        abstract void addCommands(List<Tag> changedTags);
         boolean requestFocusInWindow() {return false;}
     }
@@ -175,4 +183,5 @@
         public boolean use_last_as_default = false;
         public boolean delete_if_empty = false;
+        public boolean required = false;
 
         private JComponent value;
@@ -220,5 +229,5 @@
         }
 
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
+        @Override public void addCommands(List<Tag> changedTags) {
 
             // return if unchanged
@@ -235,5 +244,5 @@
                         v = null;
                     }
-                    cmds.add(new ChangePropertyCommand(sel, key, v));
+                    changedTags.add(new Tag(key, v));
         }
         @Override boolean requestFocusInWindow() {return value.requestFocusInWindow();}
@@ -250,4 +259,5 @@
         public boolean default_ = false; // only used for tagless objects
         public boolean use_last_as_default = false;
+        public boolean required = false;
 
         private QuadStateCheckBox check;
@@ -312,10 +322,10 @@
         }
 
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
+        @Override public void addCommands(List<Tag> changedTags) {
             // if the user hasn't changed anything, don't create a command.
             if (check.getState() == initialState && !def) return;
 
             // otherwise change things according to the selected value.
-            cmds.add(new ChangePropertyCommand(sel, key,
+            changedTags.add(new Tag(key,
                     check.getState() == QuadStateCheckBox.State.SELECTED ? value_on :
                         check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? value_off :
@@ -339,4 +349,5 @@
         public boolean editable = true;
         public boolean use_last_as_default = false;
+        public boolean required = false;
 
         private JComboBox combo;
@@ -432,5 +443,5 @@
             return true;
         }
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
+        @Override public void addCommands(List<Tag> changedTags) {
             Object obj = combo.getSelectedItem();
             String display = (obj == null) ? null : obj.toString();
@@ -464,5 +475,5 @@
                 lastValue.put(key, value);
             }
-            cmds.add(new ChangePropertyCommand(sel, key, value));
+            changedTags.add(new Tag(key, value));
         }
         @Override boolean requestFocusInWindow() {return combo.requestFocusInWindow();}
@@ -485,5 +496,5 @@
             return false;
         }
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
+        @Override public void addCommands(List<Tag> changedTags) {}
     }
 
@@ -514,5 +525,5 @@
             return false;
         }
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
+        @Override public void addCommands(List<Tag> changedTags) {}
     }
 
@@ -603,5 +614,5 @@
             return false;
         }
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
+        @Override public void addCommands(List<Tag> changedTags) {}
     }
 
@@ -614,5 +625,5 @@
             return false;
         }
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
+        @Override public void addCommands(List<Tag> changedTags) {}
     }
 
@@ -622,5 +633,5 @@
             return false;
         }
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
+        @Override public void addCommands(List<Tag> changedTags) {}
     }
 
@@ -630,6 +641,6 @@
 
         @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { return false; }
-        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
-            cmds.add(new ChangePropertyCommand(sel, key, value != null && !value.equals("") ? value : null));
+        @Override public void addCommands(List<Tag> changedTags) {
+            changedTags.add(new Tag(key, value != null && !value.equals("") ? value : null));
         }
     }
@@ -885,5 +896,5 @@
         }
         p.add(items, GBC.eol().fill());
-        if (selected.size() == 0) {
+        if (selected.size() == 0 && !supportsRelation()) {
             setEnabledRec(items, false);
         }
@@ -924,8 +935,39 @@
         if (Main.main == null) return;
         if (Main.main.getCurrentDataSet() == null) return;
-        Collection<OsmPrimitive> sel = createSelection(Main.main.getCurrentDataSet().getSelected());
+
+        Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
+        int answer = showDialog(sel, supportsRelation());
+
+        if (sel.size() != 0 && answer == DIALOG_ANSWER_APPLY) {
+            Command cmd = createCommand(sel, getChangedTags());
+            if (cmd != null) {
+                Main.main.undoRedo.add(cmd);
+            }
+        } else if (answer == DIALOG_ANSWER_NEW_RELATION) {
+            List<Command> cmds = new ArrayList<Command>(2);
+            final Relation r = new Relation();
+            cmds.add(new AddCommand(r));
+            Command cmd = createCommand(Collections.<OsmPrimitive>singletonList(r), getChangedTags());
+            if (cmd != null) {
+                cmds.add(cmd);
+            }
+            Main.main.undoRedo.add(new SequenceCommand(tr("Add relation"), cmds));
+            SwingUtilities.invokeLater(new Runnable() {
+                @Override
+                public void run() {
+                    // Relation list dialog has to be updated first for selectRelation to work
+                    Main.map.relationListDialog.selectRelation(r);
+                }
+            });
+        }
+        Main.main.getCurrentDataSet().setSelected(Main.main.getCurrentDataSet().getSelected()); // force update
+
+    }
+
+    public int showDialog(Collection<OsmPrimitive> selection, final boolean showNewRelation) {
+        Collection<OsmPrimitive> sel = createSelection(selection);
         PresetPanel p = createPanel(sel);
         if (p == null)
-            return;
+            return DIALOG_ANSWER_CANCEL;
 
         int answer = 1;
@@ -944,8 +986,14 @@
                     super(Main.parent,
                             title,
-                            new String[] { tr("Apply Preset"), tr("Cancel") },
-                            true);
+                            showNewRelation?
+                                    new String[] { tr("Apply Preset"), tr("New relation"), tr("Cancel") }:
+                                        new String[] { tr("Apply Preset"), tr("Cancel") },
+                                        true);
                     contentInsets = new Insets(10,5,0,5);
-                    setButtonIcons(new String[] {"ok.png", "cancel.png" });
+                    if (showNewRelation) {
+                        setButtonIcons(new String[] {"ok.png", "dialogs/addrelation.png", "cancel.png" });
+                    } else {
+                        setButtonIcons(new String[] {"ok.png", "cancel.png" });
+                    }
                     setContent(content);
                     setDefaultButton(1);
@@ -959,11 +1007,8 @@
             answer = new PresetDialog(p, title, (sel.size() == 0)).getValue();
         }
-        if (sel.size() != 0 && answer == 1) {
-            Command cmd = createCommand(sel);
-            if (cmd != null) {
-                Main.main.undoRedo.add(cmd);
-            }
-        }
-        Main.main.getCurrentDataSet().setSelected(Main.main.getCurrentDataSet().getSelected()); // force update
+        if (!showNewRelation && answer == 2)
+            return DIALOG_ANSWER_CANCEL;
+        else
+            return answer;
     }
 
@@ -1010,9 +1055,20 @@
     }
 
-    private Command createCommand(Collection<OsmPrimitive> sel) {
-        List<Command> cmds = new LinkedList<Command>();
-        for (Item i : data) {
-            i.addCommands(sel, cmds);
-        }
+    public List<Tag> getChangedTags() {
+        List<Tag> result = new ArrayList<Tag>();
+        for (Item i: data) {
+            i.addCommands(result);
+        }
+        return result;
+    }
+
+    public static Command createCommand(Collection<OsmPrimitive> sel, List<Tag> changedTags) {
+        List<Command> cmds = new ArrayList<Command>();
+        for (Tag tag: changedTags) {
+            if (!tag.getValue().isEmpty()) {
+                cmds.add(new ChangePropertyCommand(sel, tag.getKey(), tag.getValue()));
+            }
+        }
+
         if (cmds.size() == 0)
             return null;
@@ -1023,4 +1079,8 @@
     }
 
+    private boolean supportsRelation() {
+        return types == null || types.contains(PresetType.RELATION);
+    }
+
     protected void updateEnabledState() {
         setEnabled(Main.main != null && Main.main.getCurrentDataSet() != null);
@@ -1038,3 +1098,8 @@
         updateEnabledState();
     }
+
+    @Override
+    public String toString() {
+        return (types == null?"":types) + " " + name;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/tagging/tagging-preset.xsd
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/tagging-preset.xsd	(revision 3515)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/tagging-preset.xsd	(revision 3518)
@@ -105,4 +105,5 @@
 		<attribute name="delete_if_empty" type="boolean" />
 		<attribute name="use_last_as_default" type="boolean" />
+		<attribute name="required" type="boolean"/>
 
 		<attribute name="type" use="prohibited"/>
@@ -121,4 +122,5 @@
 		<attribute name="delete_if_empty" type="boolean" />
 		<attribute name="display_values" type="string"/>
+		<attribute name="required" type="boolean"/>
 
 		<attribute name="type" use="prohibited"/>
@@ -136,4 +138,5 @@
 		<attribute name="delete_if_empty" type="boolean" />
 		<attribute name="use_last_as_default" type="boolean" />
+		<attribute name="required" type="boolean"/>
 
 		<attribute name="name" use="prohibited"/>
