Index: /trunk/src/org/openstreetmap/josm/actions/CopyAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/CopyAction.java	(revision 557)
+++ /trunk/src/org/openstreetmap/josm/actions/CopyAction.java	(revision 558)
@@ -10,4 +10,5 @@
 import java.util.HashMap;
 import java.util.List;
+import java.util.LinkedList;
 import java.util.Collection;
 
@@ -26,4 +27,6 @@
 public final class CopyAction extends JosmAction implements SelectionChangedListener {
 
+	private LinkedList<JosmAction> listeners;
+	
 	public CopyAction() {
 		super(tr("Copy"), "copy",
@@ -32,6 +35,11 @@
 		setEnabled(false);
 		DataSet.selListeners.add(this);
+		listeners = new LinkedList<JosmAction>();
 	}
 
+	@Override public void addListener(JosmAction a) {
+		listeners.add(a);
+	}
+	
 	public void actionPerformed(ActionEvent e) {
 		Collection<OsmPrimitive> sel = Main.ds.getSelected();
@@ -99,4 +107,8 @@
 		Main.pasteBuffer = pasteBuffer;
 		Main.main.menu.paste.setEnabled(true); /* now we have a paste buffer we can make paste available */
+		
+		for(JosmAction a : listeners) {
+			a.pasteBufferChanged(Main.pasteBuffer);
+		}
 	}
 
Index: /trunk/src/org/openstreetmap/josm/actions/JosmAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/JosmAction.java	(revision 557)
+++ /trunk/src/org/openstreetmap/josm/actions/JosmAction.java	(revision 558)
@@ -8,4 +8,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -51,5 +52,18 @@
 	}
 
-
+	/**
+	 * needs to be overridden to be useful
+	 */
+	public void pasteBufferChanged(DataSet newPasteBuffer) {
+		return;
+	}
+	
+	/**
+	 * needs to be overridden to be useful
+	 */
+	public void addListener(JosmAction a) {
+		return;
+	}
+	
 	private void setHelpId() {
 		String helpId = "Action/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
Index: /trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java	(revision 558)
+++ /trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java	(revision 558)
@@ -0,0 +1,107 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+// Author: David Earl
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+public final class PasteTagsAction extends JosmAction implements SelectionChangedListener {
+
+    public PasteTagsAction(JosmAction copyAction) {
+    	super(tr("Paste Tags"), "pastetags",
+			tr("Apply tags of contents of paste buffer to all selected items."),
+			KeyEvent.VK_V, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK, true);
+		DataSet.selListeners.add(this);
+		copyAction.addListener(this);
+		setEnabled(false);
+    }
+
+    private void pasteKeys(Collection<Command> clist, Collection<? extends OsmPrimitive> pasteBufferSubset, Collection<OsmPrimitive> selectionSubset) {
+		/* scan the paste buffer, and add tags to each of the selected objects. 
+		 * If a tag already exists, it is overwritten */
+		if (! selectionSubset.isEmpty()) {
+			for (Iterator<? extends OsmPrimitive> it = pasteBufferSubset.iterator(); it.hasNext();) {
+				OsmPrimitive osm = it.next();
+				for (String key : osm.keys.keySet()) {
+					if (! key.equals("created_by"))
+						clist.add(new ChangePropertyCommand(selectionSubset, key, osm.keys.get(key)));
+				}
+			}
+		}    	
+    }
+    
+    public void actionPerformed(ActionEvent e) {
+		Collection<Command> clist = new LinkedList<Command>();		
+		pasteKeys(clist, Main.pasteBuffer.nodes, Main.ds.getSelectedNodes());
+		pasteKeys(clist, Main.pasteBuffer.ways, Main.ds.getSelectedWays());
+		pasteKeys(clist, Main.pasteBuffer.relations, Main.ds.getSelectedRelations());
+		Main.main.undoRedo.add(new SequenceCommand(tr("Paste Tags"), clist));
+		Main.ds.setSelected(Main.ds.getSelected()); // to force selection listeners, in particular the tag panel, to update
+		Main.map.mapView.repaint();
+    }
+    
+	private boolean containsSameKeysWithDifferentValues(Collection<? extends OsmPrimitive> osms) {
+		Map<String,String> kvSeen = new HashMap<String,String>();
+		for (Iterator<? extends OsmPrimitive> it = osms.iterator(); it.hasNext();) {
+			OsmPrimitive osm = it.next();
+			if (osm.keys.isEmpty())
+				continue;
+			for (String key : osm.keys.keySet()) {
+				if (key.equals("created_by")) // we ignore created_by
+					continue;
+				String value = osm.keys.get(key);
+				if (! kvSeen.containsKey(key))
+					kvSeen.put(key, value);
+				else if (! kvSeen.get(key).equals(value))
+					return true;
+			}
+		}		
+		return false;
+	}
+	
+	/**
+	 * determines whether to enable the widget depending on the contents of the paste 
+	 * buffer and current selection 
+	 * @param pasteBuffer
+	 */
+	private void possiblyEnable(Collection<? extends OsmPrimitive> selection, DataSet pasteBuffer) {
+		/* only enable if there is something selected to paste into and 
+			if we don't have conflicting keys in the pastebuffer */
+		setEnabled(! selection.isEmpty() &&
+				! pasteBuffer.allPrimitives().isEmpty() && 
+				(Main.ds.getSelectedNodes().isEmpty() ||
+					! containsSameKeysWithDifferentValues(pasteBuffer.nodes)) && 
+				(Main.ds.getSelectedWays().isEmpty() ||
+					! containsSameKeysWithDifferentValues(pasteBuffer.ways)) && 
+				(Main.ds.getSelectedRelations().isEmpty() ||
+					! containsSameKeysWithDifferentValues(pasteBuffer.relations)));
+	}
+	
+	@Override public void pasteBufferChanged(DataSet newPasteBuffer) {
+		possiblyEnable(Main.ds.getSelected(), newPasteBuffer);
+	}
+	
+	public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+		possiblyEnable(newSelection, Main.pasteBuffer);
+	}
+}
Index: /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 557)
+++ /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 558)
@@ -111,4 +111,25 @@
 		sel.addAll(getSelected(relations));
 		return sel;
+	}
+
+	/**
+	 * Return selected nodes.
+	 */
+	public Collection<OsmPrimitive> getSelectedNodes() {
+		return getSelected(nodes);
+	}
+
+	/**
+	 * Return selected ways.
+	 */
+	public Collection<OsmPrimitive> getSelectedWays() {
+		return getSelected(ways);
+	}
+
+	/**
+	 * Return selected relations.
+	 */
+	public Collection<OsmPrimitive> getSelectedRelations() {
+		return getSelected(relations);
 	}
 
Index: /trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 557)
+++ /trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 558)
@@ -31,4 +31,5 @@
 import org.openstreetmap.josm.actions.OpenAction;
 import org.openstreetmap.josm.actions.PasteAction;
+import org.openstreetmap.josm.actions.PasteTagsAction;
 import org.openstreetmap.josm.actions.PreferencesAction;
 import org.openstreetmap.josm.actions.RedoAction;
@@ -74,4 +75,5 @@
 	public final JosmAction copy = new CopyAction();
 	public final JosmAction paste = new PasteAction();
+	public final JosmAction pasteTags = new PasteTagsAction(copy); 
 	public final JosmAction duplicate = new DuplicateAction(); 
 	public final JosmAction selectAll = new SelectAllAction();
@@ -147,4 +149,6 @@
 		current = editMenu.add(paste);
 		current.setAccelerator(paste.shortCut);
+		current = editMenu.add(pasteTags);
+		current.setAccelerator(pasteTags.shortCut);
 		current = editMenu.add(duplicate);
 		current.setAccelerator(duplicate.shortCut);
