Index: src/org/openstreetmap/josm/Main.java
===================================================================
--- src/org/openstreetmap/josm/Main.java	(revision 23)
+++ src/org/openstreetmap/josm/Main.java	(revision 24)
@@ -19,4 +19,5 @@
 import org.openstreetmap.josm.actions.OpenOsmServerAction;
 import org.openstreetmap.josm.actions.PreferencesAction;
+import org.openstreetmap.josm.actions.SaveAction;
 import org.openstreetmap.josm.actions.SaveGpxAction;
 import org.openstreetmap.josm.data.Preferences;
@@ -76,4 +77,5 @@
 		OpenOsmServerAction openServerAction = new OpenOsmServerAction();
 		OpenGpxAction openGpxAction = new OpenGpxAction();
+		SaveAction saveAction = new SaveAction();
 		SaveGpxAction saveGpxAction = new SaveGpxAction();
 		ExitAction exitAction = new ExitAction();
@@ -88,4 +90,5 @@
 		fileMenu.setMnemonic('F');
 		fileMenu.add(openGpxAction);
+		fileMenu.add(saveAction);
 		fileMenu.add(saveGpxAction);
 		fileMenu.addSeparator();
@@ -114,4 +117,5 @@
 		toolBar.add(openServerAction);
 		toolBar.add(openGpxAction);
+		toolBar.add(saveAction);
 		toolBar.add(saveGpxAction);
 		toolBar.addSeparator();
Index: src/org/openstreetmap/josm/actions/AboutAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/AboutAction.java	(revision 23)
+++ src/org/openstreetmap/josm/actions/AboutAction.java	(revision 24)
@@ -5,4 +5,5 @@
 import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.io.BufferedReader;
@@ -21,4 +22,5 @@
 import javax.swing.JTabbedPane;
 import javax.swing.JTextArea;
+import javax.swing.KeyStroke;
 import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkListener;
Index: src/org/openstreetmap/josm/actions/OpenGpxAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/OpenGpxAction.java	(revision 23)
+++ src/org/openstreetmap/josm/actions/OpenGpxAction.java	(revision 24)
@@ -3,4 +3,5 @@
 import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.io.File;
@@ -16,4 +17,5 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.KeyStroke;
 import javax.swing.filechooser.FileFilter;
 
@@ -45,4 +47,6 @@
 	public OpenGpxAction() {
 		super("Open GPX", ImageProvider.get("opengpx"));
+		putValue(ACCELERATOR_KEY, KeyStroke.getAWTKeyStroke(KeyEvent.VK_O, 
+				InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
 		putValue(MNEMONIC_KEY, KeyEvent.VK_O);
 		putValue(SHORT_DESCRIPTION, "Open a file in GPX format.");
Index: src/org/openstreetmap/josm/actions/OpenOsmServerAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/OpenOsmServerAction.java	(revision 23)
+++ src/org/openstreetmap/josm/actions/OpenOsmServerAction.java	(revision 24)
@@ -35,5 +35,5 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.layer.RawGpsDataLayer;
-import org.openstreetmap.josm.io.OsmReader;
+import org.openstreetmap.josm.io.OsmServerReader;
 
 /**
@@ -147,5 +147,5 @@
 			return;
 		}
-		OsmReader osmReader = new OsmReader(Main.pref.osmDataServer,
+		OsmServerReader osmReader = new OsmServerReader(Main.pref.osmDataServer,
 				b.latlon[0], b.latlon[1], b.latlon[2], b.latlon[3]);
 		try {
Index: src/org/openstreetmap/josm/actions/PreferencesAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/PreferencesAction.java	(revision 23)
+++ src/org/openstreetmap/josm/actions/PreferencesAction.java	(revision 24)
@@ -2,7 +2,9 @@
 
 import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 
 import javax.swing.AbstractAction;
+import javax.swing.KeyStroke;
 
 import org.openstreetmap.josm.gui.ImageProvider;
@@ -21,4 +23,5 @@
 	public PreferencesAction() {
 		super("Preferences", ImageProvider.get("preference"));
+		putValue(ACCELERATOR_KEY, KeyStroke.getAWTKeyStroke(KeyEvent.VK_P, InputEvent.CTRL_DOWN_MASK));
 		putValue(MNEMONIC_KEY, KeyEvent.VK_P);
 		putValue(SHORT_DESCRIPTION, "Open a preferences page for global settings.");
Index: src/org/openstreetmap/josm/actions/SaveAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/SaveAction.java	(revision 24)
+++ src/org/openstreetmap/josm/actions/SaveAction.java	(revision 24)
@@ -0,0 +1,71 @@
+package org.openstreetmap.josm.actions;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.KeyStroke;
+import javax.swing.filechooser.FileFilter;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.ImageProvider;
+import org.openstreetmap.josm.io.OsmWriter;
+
+/**
+ * Export the data  as OSM intern xml file.
+ * 
+ * TODO: This is very redundant with SaveGpxAction. Merge both actions into one!
+ *  
+ * @author imi
+ */
+public class SaveAction extends AbstractAction {
+
+	/**
+	 * Construct the action with "Save" as label.
+	 */
+	public SaveAction() {
+		super("Save", ImageProvider.get("save"));
+		putValue(ACCELERATOR_KEY, KeyStroke.getAWTKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
+		putValue(SHORT_DESCRIPTION, "Save the current data.");
+	}
+	
+	public void actionPerformed(ActionEvent event) {
+		if (Main.main.getMapFrame() == null) {
+			JOptionPane.showMessageDialog(Main.main, "No document open so nothing to save.");
+			return;
+		}
+		JFileChooser fc = new JFileChooser("data");
+		fc.setFileFilter(new FileFilter(){
+			@Override
+			public boolean accept(File f) {
+				String name = f.getName().toLowerCase();
+				return f.isDirectory() || name.endsWith(".xml");
+			}
+			@Override
+			public String getDescription() {
+				return "XML Files";
+			}
+		});
+		fc.showSaveDialog(Main.main);
+		File file = fc.getSelectedFile();
+		if (file == null)
+			return;
+		
+		try {
+			FileWriter fileWriter = new FileWriter(file);
+			OsmWriter out = new OsmWriter(fileWriter, Main.main.ds);
+			out.output();
+			fileWriter.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+			JOptionPane.showMessageDialog(Main.main, "An error occoured while saving.\n"+e.getMessage());
+		}
+	}
+
+}
Index: src/org/openstreetmap/josm/actions/SaveGpxAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/SaveGpxAction.java	(revision 23)
+++ src/org/openstreetmap/josm/actions/SaveGpxAction.java	(revision 24)
@@ -2,4 +2,5 @@
 
 import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.io.File;
@@ -10,4 +11,5 @@
 import javax.swing.JFileChooser;
 import javax.swing.JOptionPane;
+import javax.swing.KeyStroke;
 
 import org.openstreetmap.josm.Main;
@@ -29,9 +31,10 @@
 	public SaveGpxAction() {
 		super("Save GPX", ImageProvider.get("savegpx"));
+		putValue(ACCELERATOR_KEY, KeyStroke.getAWTKeyStroke(KeyEvent.VK_S,
+				InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK));
 		putValue(MNEMONIC_KEY, KeyEvent.VK_S);
-		putValue(SHORT_DESCRIPTION, "Save the current active layer as GPX file.");
+		putValue(SHORT_DESCRIPTION, "Export the current data as GPX file.");
 	}
 	
-	@SuppressWarnings("unchecked")
 	public void actionPerformed(ActionEvent event) {
 		if (Main.main.getMapFrame() == null) {
Index: src/org/openstreetmap/josm/actions/mapmode/AddLineSegmentAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/AddLineSegmentAction.java	(revision 23)
+++ src/org/openstreetmap/josm/actions/mapmode/AddLineSegmentAction.java	(revision 24)
@@ -54,5 +54,5 @@
 	 */
 	public AddLineSegmentAction(MapFrame mapFrame) {
-		super("Add Line Segment", "addlinesegment", "Add a line segment between two nodes.", KeyEvent.VK_L, mapFrame);
+		super("Add Line Segment", "addlinesegment", "Add a line segment between two nodes.", KeyEvent.VK_G, mapFrame);
 	}
 
Index: src/org/openstreetmap/josm/command/ChangeKeyValueCommand.java
===================================================================
--- src/org/openstreetmap/josm/command/ChangeKeyValueCommand.java	(revision 24)
+++ src/org/openstreetmap/josm/command/ChangeKeyValueCommand.java	(revision 24)
@@ -0,0 +1,70 @@
+package org.openstreetmap.josm.command;
+
+import java.awt.Component;
+import java.util.Collection;
+import java.util.HashMap;
+
+import javax.swing.JLabel;
+
+import org.openstreetmap.josm.data.osm.Key;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Command that manipulate the key/value structure of several objects. Manages deletion,
+ * adding and modify of values and keys.
+ * 
+ * @author imi
+ */
+public class ChangeKeyValueCommand implements Command {
+
+	/**
+	 * All primitives, that are affected with this command.
+	 */
+	private final Collection<OsmPrimitive> objects;
+	/**
+	 * The key that is subject to change.
+	 */
+	private final Key key;
+	/**
+	 * The key value. If it is <code>null</code>, delete all key references with the given
+	 * key. Else, 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 ChangeKeyValueCommand(Collection<OsmPrimitive> objects, Key key, String value) {
+		this.objects = objects;
+		this.key = key;
+		this.value = value;
+	}
+	
+	public void executeCommand() {
+		if (value == null) {
+			for (OsmPrimitive osm : objects) {
+				if (osm.keys != null) {
+					osm.keys.remove(key);
+					if (osm.keys.isEmpty())
+						osm.keys = null;
+				}
+			}
+		} else {
+			for (OsmPrimitive osm : objects) {
+				if (osm.keys == null)
+					osm.keys = new HashMap<Key, String>();
+				osm.keys.put(key, value);
+			}
+		}
+	}
+
+	public Component commandDescription() {
+		String objStr = objects.size()+" object" + (objects.size()==1?"":"s");
+		if (value == null)
+			return new JLabel("Delete the key '"+key+"' of "+objStr);
+		return new JLabel("Change the key '"+key+"' of "+objStr+" to '"+value+"'");
+	}
+
+	public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
+		modified.addAll(objects);
+	}
+
+}
Index: src/org/openstreetmap/josm/data/osm/Key.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/Key.java	(revision 23)
+++ src/org/openstreetmap/josm/data/osm/Key.java	(revision 24)
@@ -22,5 +22,5 @@
 	 * All keys are stored here.
 	 */
-	private static Map<String, Key> allKeys = new HashMap<String, Key>();
+	public static final Map<String, Key> allKeys = new HashMap<String, Key>();
 	
 	/**
@@ -51,3 +51,8 @@
 		visitor.visit(this);
 	}
+
+	@Override
+	public String toString() {
+		return name;
+	}
 }
Index: src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapView.java	(revision 23)
+++ src/org/openstreetmap/josm/gui/MapView.java	(revision 24)
@@ -406,6 +406,9 @@
 				if (bounds == null)
 					bounds = l.getBoundsXY();
-				else
-					bounds = bounds.mergeXY(l.getBoundsXY());
+				else {
+					Bounds lb = l.getBoundsXY();
+					if (lb != null)
+						bounds = bounds.mergeXY(lb);
+				}
 			}
 
Index: src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java	(revision 23)
+++ src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java	(revision 24)
@@ -1,40 +1,311 @@
 package org.openstreetmap.josm.gui.dialogs;
 
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
-
-import javax.swing.BorderFactory;
-import javax.swing.Box;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowFocusListener;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+import java.util.Map.Entry;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
 import javax.swing.JLabel;
-import javax.swing.border.Border;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableModel;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangeKeyValueCommand;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.Key;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.ImageProvider;
 import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.MapView;
 
 /**
- * Open a Property dialog for the current visible map. When saving to own josm-
- * data format, the properties are saved along.
+ * This dialog displays the properties of the current selected primitives.
+ * 
+ * If no object is selected, the dialog list is empty. 
+ * If only one is selected, all properties of this object are selected.
+ * If more than one object are selected, the sum of all properties are displayed. If the
+ * different objects share the same property, the shared value is displayed. If they have
+ * different values, all of them are put in a combo box and the string "&lt;different&gt;"
+ * is displayed in italic.
+ * 
+ * Below the list, the user can click on an add, modify and delete property button to 
+ * edit the table selection value.
+ * 
+ * The command is applied to all selected entries.
  * 
  * @author imi
  */
-public class PropertiesDialog extends ToggleDialog {
-
+public class PropertiesDialog extends ToggleDialog implements SelectionChangedListener {
+
+	/**
+	 * Watches for double clicks and start editing or new property, depending on the
+	 * location, the click was.
+	 * @author imi
+	 */
+	public class DblClickWatch extends MouseAdapter {
+		@Override
+		public void mouseClicked(MouseEvent e) {
+			if (e.getClickCount() < 2)
+				return;
+			if (e.getSource() instanceof JScrollPane)
+				add();
+			else {
+				int row = propertyTable.rowAtPoint(e.getPoint());
+				edit(row);
+			}
+		}
+	}
+	
+	/**
+	 * Edit the value in the table row
+	 * @param row 	The row of the table, from which the value is edited. 
+	 */
+	void edit(int row) {
+		String key = data.getValueAt(row, 0).toString();
+		Collection<OsmPrimitive> sel = Main.main.ds.getSelected();
+		String msg = "<html>This will change "+sel.size()+" object"+(sel.size()==1?"":"s")+".<br><br>"+
+		"Please select a new value for '"+key+"'.<br>(Empty string deletes the key.)";
+		final JComboBox combo = (JComboBox)data.getValueAt(row, 1);
+		JPanel p = new JPanel(new BorderLayout());
+		p.add(new JLabel(msg+"</html>"), BorderLayout.NORTH);
+		p.add(combo, BorderLayout.CENTER);
+
+		final JOptionPane optionPane = new JOptionPane(p, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
+		final JDialog dlg = optionPane.createDialog(PropertiesDialog.this, "Change values?");
+		dlg.addWindowFocusListener(new WindowFocusListener(){
+			public void windowGainedFocus(WindowEvent e) {
+				combo.requestFocusInWindow();
+			}
+			public void windowLostFocus(WindowEvent e) {
+			}
+		});
+		combo.getEditor().addActionListener(new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				optionPane.setValue(JOptionPane.OK_OPTION);
+				dlg.setVisible(false);
+			}
+		});
+		dlg.setVisible(true);
+
+		Object answer = optionPane.getValue();
+		if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
+				(answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION))
+			return;
+
+		String value = combo.getEditor().getItem().toString();
+		if (value.equals("<different>"))
+			return;
+		if (value.equals(""))
+			value = null; // delete the key
+		mv.editLayer().add(new ChangeKeyValueCommand(sel, Key.get(key), value));
+
+		if (value == null)
+			selectionChanged(sel); // update whole table
+		else
+			PropertiesDialog.this.repaint(); // repaint is enough 
+	}
+	
+	/**
+	 * Open the add selection dialog and add a new key/value to the table (and to the
+	 * dataset, of course).
+	 */
+	void add() {
+		Collection<OsmPrimitive> sel = Main.main.ds.getSelected();
+		
+		JPanel p = new JPanel(new BorderLayout());
+		p.add(new JLabel("<html>This will change "+sel.size()+" object"+(sel.size()==1?"":"s")+".<br><br>"+
+		"Please select a key"), BorderLayout.NORTH);
+		Vector<String> allKeys = new Vector<String>(Key.allKeys.keySet());
+		for (Iterator<String> it = allKeys.iterator(); it.hasNext();) {
+			String s = it.next();
+			for (int i = 0; i < data.getRowCount(); ++i) {
+				if (s.equals(data.getValueAt(i, 0))) {
+					it.remove();
+					break;
+				}
+			}
+		}
+		JComboBox keys = new JComboBox(allKeys);
+		keys.setEditable(true);
+		p.add(keys, BorderLayout.CENTER);
+		
+		JPanel p2 = new JPanel(new BorderLayout());
+		p.add(p2, BorderLayout.SOUTH);
+		p2.add(new JLabel("Please select a value"), BorderLayout.NORTH);
+		JTextField values = new JTextField();
+		p2.add(values, BorderLayout.CENTER);
+		int answer = JOptionPane.showConfirmDialog(PropertiesDialog.this, p, 
+				"Change values?", JOptionPane.OK_CANCEL_OPTION); 
+		if (answer != JOptionPane.OK_OPTION)
+			return;
+		String key = keys.getEditor().getItem().toString();
+		String value = values.getText();
+		if (value.equals(""))
+			return;
+		mv.editLayer().add(new ChangeKeyValueCommand(sel, Key.get(key), value));
+		selectionChanged(sel); // update table
+	}
+
+	/**
+	 * Delete the keys from the given row.
+	 * @param row	The row, which key gets deleted from the dataset.
+	 */
+	private void delete(int row) {
+		String key = data.getValueAt(row, 0).toString();
+		Collection<OsmPrimitive> sel = Main.main.ds.getSelected();
+		mv.editLayer().add(new ChangeKeyValueCommand(sel, Key.get(key), null));
+		selectionChanged(sel); // update table
+	}
+	
+	/**
+	 * The property data.
+	 */
+	private final DefaultTableModel data = new DefaultTableModel(){
+		@Override
+		public boolean isCellEditable(int row, int column) {
+			return false;
+		}
+		@Override
+		public Class<?> getColumnClass(int columnIndex) {
+			return columnIndex == 1 ? JComboBox.class : String.class;
+		}
+	};
+	/**
+	 * The properties list.
+	 */
+	private final JTable propertyTable = new JTable(data);
+	/**
+	 * The map view this dialog operates on.
+	 */
+	private final MapView mv;
+	
 	/**
 	 * Create a new PropertiesDialog
 	 */
 	public PropertiesDialog(MapFrame mapFrame) {
-		super(mapFrame, "Properties of "+Main.main.getNameOfLoadedMapFrame(), "Properties Dialog", "properties", KeyEvent.VK_P, "Property page for this map.");
-		putValue(MNEMONIC_KEY, KeyEvent.VK_P);
-
-		final Border panelBorder = BorderFactory.createEmptyBorder(5,0,0,0);
-		Box panel = Box.createVerticalBox();
+		super(mapFrame, "Properties", "Properties Dialog", "properties", KeyEvent.VK_P, "Property for selected objects.");
+		mv = mapFrame.mapView;
+
+		setLayout(new BorderLayout());
+		setSize(350,450);
 		
-		JLabel todo = new JLabel("Nothing implemented yet.");
-		todo.setBorder(panelBorder);
-		panel.add(todo);
+		data.setColumnIdentifiers(new String[]{"Key", "Value"});
+		propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+		propertyTable.setDefaultRenderer(JComboBox.class, new DefaultTableCellRenderer(){
+			@Override
+			public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+				Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
+				if (c instanceof JLabel) {
+					String str = ((JComboBox)value).getEditor().getItem().toString();
+					((JLabel)c).setText(str);
+					if (str.equals("<different>"))
+						c.setFont(c.getFont().deriveFont(Font.ITALIC));
+				}
+				return c;
+			}
+		});
+		propertyTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer(){
+			@Override
+			public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+				return super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
+			}
+		});
+		propertyTable.addMouseListener(new DblClickWatch());
+
+		JScrollPane scrollPane = new JScrollPane(propertyTable);
+		scrollPane.addMouseListener(new DblClickWatch());
+		getContentPane().add(scrollPane, BorderLayout.CENTER);
 		
-		panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
-		setContentPane(panel);
-		pack();
-		setResizable(false);
+		JPanel buttonPanel = new JPanel(new GridLayout(1,3));
+		ActionListener buttonAction = new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				int sel = propertyTable.getSelectedRow();
+				if (e.getActionCommand().equals("Add"))
+					add();
+				else if (e.getActionCommand().equals("Edit")) {
+					if (sel == -1)
+						JOptionPane.showMessageDialog(PropertiesDialog.this, "Please select the row to edit.");
+					else
+						edit(sel);
+				} else if (e.getActionCommand().equals("Delete")) {
+					if (sel == -1)
+						JOptionPane.showMessageDialog(PropertiesDialog.this, "Please select the row to delete.");
+					else
+						delete(sel);
+				}
+			}
+		};
+		buttonPanel.add(createButton("Add", "Add a new key/value pair to all objects", KeyEvent.VK_A, buttonAction));
+		buttonPanel.add(createButton("Edit", "Edit the value of the selected key for all objects", KeyEvent.VK_E, buttonAction));
+		buttonPanel.add(createButton("Delete", "Delete the selected key in all objects", KeyEvent.VK_DELETE, buttonAction));
+		getContentPane().add(buttonPanel, BorderLayout.SOUTH);
+	}
+	
+	private JButton createButton(String name, String tooltip, int mnemonic, ActionListener actionListener) {
+		JButton b = new JButton(name, ImageProvider.get("dialogs", name.toLowerCase()));
+		b.setActionCommand(name);
+		b.addActionListener(actionListener);
+		b.setToolTipText(tooltip);
+		b.setMnemonic(mnemonic);
+		return b;
+	}
+
+	@Override
+	public void setVisible(boolean b) {
+		if (b) {
+			Main.main.ds.addSelectionChangedListener(this);
+			selectionChanged(Main.main.ds.getSelected());
+		} else {
+			Main.main.ds.removeSelectionChangedListener(this);
+		}
+		super.setVisible(b);
+	}
+
+	public void selectionChanged(Collection<OsmPrimitive> newSelection) {
+		if (propertyTable.getCellEditor() != null)
+			propertyTable.getCellEditor().cancelCellEditing();
+		data.setRowCount(0);
+		TreeMap<String, Collection<String>> props = new TreeMap<String, Collection<String>>();
+		for (OsmPrimitive osm : newSelection) {
+			if (osm.keys != null) {
+				for (Entry<Key, String> e : osm.keys.entrySet()) {
+					Collection<String> value = props.get(e.getKey().name);
+					if (value == null) {
+						value = new TreeSet<String>();
+						props.put(e.getKey().name, value);
+					}
+					value.add(e.getValue());
+				}
+			}
+		}
+		for (Entry<String, Collection<String>> e : props.entrySet()) {
+			JComboBox value = new JComboBox(e.getValue().toArray());
+			value.setEditable(true);
+			if (e.getValue().size() > 1)
+				value.getEditor().setItem("<different>");
+			data.addRow(new Object[]{e.getKey(), value});
+		}
 	}
 }
Index: src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- src/org/openstreetmap/josm/io/GpxReader.java	(revision 23)
+++ src/org/openstreetmap/josm/io/GpxReader.java	(revision 24)
@@ -40,5 +40,5 @@
 	 */
 	public Reader source;
-	
+
 	/**
 	 * Construct a parser from a specific data source.
@@ -132,7 +132,5 @@
 					}
 				}
-			}
-			
-			if (child.getName().equals("extensions"))
+			} else if (child.getName().equals("extensions"))
 				parseKeyValueExtensions(track, child);
 			else if (child.getName().equals("link"))
@@ -157,5 +155,5 @@
 	 * @return Either the parameter node or the old node found in the dataset. 
 	 */
-	private Node addNode (DataSet data, Node node) {
+	private Node addNode(DataSet data, Node node) {
 		if (Main.pref.mergeNodes)
 			for (Node n : data.nodes)
Index: src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmReader.java	(revision 23)
+++ 	(revision )
@@ -1,105 +1,0 @@
-package org.openstreetmap.josm.io;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Collection;
-import java.util.LinkedList;
-
-import org.jdom.JDOMException;
-import org.openstreetmap.josm.data.GeoPoint;
-import org.openstreetmap.josm.data.osm.DataSet;
-
-/**
- * This DataReader read directly from the REST API of the osm server.
- * 
- * @author imi
- */
-public class OsmReader extends OsmConnection {
-
-	/**
-	 * The url string of the desired map data.
-	 */
-	private String urlStr;
-	private final double lat1;
-	private final double lon1;
-	private final double lat2;
-	private final double lon2;
-	
-	/**
-	 * Construct the reader and store the information for attaching
-	 */
-	public OsmReader(String server, 
-			double lat1, double lon1, double lat2, double lon2) {
-		this.lon2 = lon2;
-		this.lat2 = lat2;
-		this.lon1 = lon1;
-		this.lat1 = lat1;
-		urlStr = server.endsWith("/") ? server : server+"/";
-	}
-
-
-	/**
-	 * Retrieve raw gps trackpoints from the server API.
-	 * @return A list of all primitives retrieved. Currently, the list of lists
-	 * 		contain only one list, since the server cannot distinguish between
-	 * 		tracks.
-	 */
-	public Collection<Collection<GeoPoint>> parseRawGps() throws IOException, JDOMException {
-		String url = urlStr+"trackpoints?bbox="+lat1+","+lon1+","+lat2+","+lon2+"&page=";
-		Collection<Collection<GeoPoint>> data = new LinkedList<Collection<GeoPoint>>();
-		Collection<GeoPoint> list = new LinkedList<GeoPoint>();
-		
-		for (int i = 0;;++i) {
-			Reader r = getReader(url+i);
-			if (r == null)
-				break;
-			RawGpsReader gpsReader = new RawGpsReader(r);
-			Collection<Collection<GeoPoint>> allTracks = gpsReader.parse();
-			boolean foundSomething = false;
-			for (Collection<GeoPoint> t : allTracks) {
-				if (!t.isEmpty()) {
-					foundSomething = true;
-					list.addAll(t);
-				}
-			}
-			if (!foundSomething)
-				break;
-		}
-
-		data.add(list);
-		return data;
-	}
-
-
-	/**
-	 * Read the data from the osm server address.
-	 * @return A data set containing all data retrieved from that url
-	 */
-	public DataSet parseOsm() throws JDOMException, IOException {
-		Reader r = getReader(urlStr+"map?bbox="+lon1+","+lat1+","+lon2+","+lat2);
-		if (r == null)
-			return null;
-		return new GpxReader(r).parse();
-	}
-
-
-	/**
-	 * Open a connection to the given url and return a reader on the input stream
-	 * from that connection. In case of user cancel, return <code>null</code>.
-	 * @param url The exact url to connect to.
-	 * @return An reader reading the input stream (servers answer) or <code>null</code>.
-	 */
-	private Reader getReader(String urlStr) throws IOException {
-		System.out.println(urlStr);
-		initAuthentication();
-		URL url = new URL(urlStr);
-		HttpURLConnection con = (HttpURLConnection)url.openConnection();
-		con.setConnectTimeout(20000);
-		if (con.getResponseCode() == 401 && isCancelled())
-			return null;
-		return new InputStreamReader(con.getInputStream());
-	}
-}
Index: src/org/openstreetmap/josm/io/OsmServerReader.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 24)
+++ src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 24)
@@ -0,0 +1,105 @@
+package org.openstreetmap.josm.io;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.jdom.JDOMException;
+import org.openstreetmap.josm.data.GeoPoint;
+import org.openstreetmap.josm.data.osm.DataSet;
+
+/**
+ * This DataReader read directly from the REST API of the osm server.
+ * 
+ * @author imi
+ */
+public class OsmServerReader extends OsmConnection {
+
+	/**
+	 * The url string of the desired map data.
+	 */
+	private String urlStr;
+	private final double lat1;
+	private final double lon1;
+	private final double lat2;
+	private final double lon2;
+	
+	/**
+	 * Construct the reader and store the information for attaching
+	 */
+	public OsmServerReader(String server, 
+			double lat1, double lon1, double lat2, double lon2) {
+		this.lon2 = lon2;
+		this.lat2 = lat2;
+		this.lon1 = lon1;
+		this.lat1 = lat1;
+		urlStr = server.endsWith("/") ? server : server+"/";
+	}
+
+
+	/**
+	 * Retrieve raw gps trackpoints from the server API.
+	 * @return A list of all primitives retrieved. Currently, the list of lists
+	 * 		contain only one list, since the server cannot distinguish between
+	 * 		tracks.
+	 */
+	public Collection<Collection<GeoPoint>> parseRawGps() throws IOException, JDOMException {
+		String url = urlStr+"trackpoints?bbox="+lat1+","+lon1+","+lat2+","+lon2+"&page=";
+		Collection<Collection<GeoPoint>> data = new LinkedList<Collection<GeoPoint>>();
+		Collection<GeoPoint> list = new LinkedList<GeoPoint>();
+		
+		for (int i = 0;;++i) {
+			Reader r = getReader(url+i);
+			if (r == null)
+				break;
+			RawGpsReader gpsReader = new RawGpsReader(r);
+			Collection<Collection<GeoPoint>> allTracks = gpsReader.parse();
+			boolean foundSomething = false;
+			for (Collection<GeoPoint> t : allTracks) {
+				if (!t.isEmpty()) {
+					foundSomething = true;
+					list.addAll(t);
+				}
+			}
+			if (!foundSomething)
+				break;
+		}
+
+		data.add(list);
+		return data;
+	}
+
+
+	/**
+	 * Read the data from the osm server address.
+	 * @return A data set containing all data retrieved from that url
+	 */
+	public DataSet parseOsm() throws JDOMException, IOException {
+		Reader r = getReader(urlStr+"map?bbox="+lon1+","+lat1+","+lon2+","+lat2);
+		if (r == null)
+			return null;
+		return new GpxReader(r).parse();
+	}
+
+
+	/**
+	 * Open a connection to the given url and return a reader on the input stream
+	 * from that connection. In case of user cancel, return <code>null</code>.
+	 * @param url The exact url to connect to.
+	 * @return An reader reading the input stream (servers answer) or <code>null</code>.
+	 */
+	private Reader getReader(String urlStr) throws IOException {
+		System.out.println(urlStr);
+		initAuthentication();
+		URL url = new URL(urlStr);
+		HttpURLConnection con = (HttpURLConnection)url.openConnection();
+		con.setConnectTimeout(20000);
+		if (con.getResponseCode() == 401 && isCancelled())
+			return null;
+		return new InputStreamReader(con.getInputStream());
+	}
+}
Index: src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmWriter.java	(revision 23)
+++ src/org/openstreetmap/josm/io/OsmWriter.java	(revision 24)
@@ -2,16 +2,26 @@
 
 import java.io.IOException;
+import java.io.Writer;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.Map.Entry;
 
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.command.Command;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Key;
+import org.openstreetmap.josm.data.osm.LineSegment;
+import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Track;
 
 
 /**
- * Send back the modified data to the osm server.
+ * Save the dataset into a stream as osm intern xml format.
  * @author imi
  */
@@ -19,31 +29,156 @@
 
 	/**
-	 * The server base url to handle the osm requests.
+	 * The output writer to write the xml stream to.
 	 */
-	private final String server;
+	private final Writer out;
 	/**
 	 * The commands that should be uploaded on the server.
 	 */
-	private final Collection<Command> commands;
+	private DataSet ds;
+	/**
+	 * ID generator start for all nodes with id==0
+	 */
+	private long id = 0;
+	/**
+	 * A collection of all ids used so far to look up and jump over used ids.
+	 */
+	private Set<Long> ids;
+	
+	public OsmWriter(Writer out, DataSet dataSet) {
+		this.out = out;
+		ds = dataSet;
+	}
 
-	public OsmWriter(String server, Collection<Command> commands) {
-		this.server = server;
-		this.commands = commands;
+	/**
+	 * Output the data to the stream
+	 * @throws IOException In case of stream IO errors.
+	 */
+	@SuppressWarnings("unchecked")
+	public void output() throws IOException {
+		ids = allUsedIds();
+		Element root = new Element("osm");
+		List<Element> list = root.getChildren();
+		Collection<Element> properties = new LinkedList<Element>();
+		for (Node n : ds.nodes)
+			list.add(parseNode(n, properties));
+		for (LineSegment ls : ds.pendingLineSegments)
+			list.add(parseLineSegment(ls, properties));
+		// all other line segments
+		Collection<LineSegment> lineSegments = new HashSet<LineSegment>();
+		for (Track t : ds.tracks)
+			lineSegments.addAll(t.segments);
+		for (LineSegment ls : lineSegments)
+			list.add(parseLineSegment(ls, properties));
+		for (Track t : ds.tracks)
+			list.add(parseTrack(t, properties));
+		list.addAll(properties);
+
+		Document d = new Document(root);
+		XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat());
+		xmlOut.output(d, out);
+	}
+
+	/**
+	 * Create an track element. Add all properties of the node to the properties-list.
+	 */
+	@SuppressWarnings("unchecked")
+	private Element parseTrack(Track t, Collection<Element> properties) {
+		Element e = new Element("track");
+		addProperties(e, t, properties);
+		for (LineSegment ls : t.segments)
+			e.getChildren().add(new Element("segment").setAttribute("id", ""+ls.id));
+		return e;
+	}
+
+	/**
+	 * Create an node element. Add all properties of the node to the properties-list.
+	 */
+	private Element parseNode(Node n, Collection<Element> properties) {
+		Element e = new Element("node");
+		addProperties(e, n, properties);
+		e.setAttribute("lat", ""+n.coor.lat);
+		e.setAttribute("lon", ""+n.coor.lon);
+		return e;
+	}
+
+	
+
+	/**
+	 * Create an line segment element. Add all properties of the node to the properties-list.
+	 */
+	private Element parseLineSegment(LineSegment ls, Collection<Element> properties) {
+		Element e = new Element("segment");
+		addProperties(e, ls, properties);
+		e.setAttribute("start", ""+ls.start.id);
+		e.setAttribute("end", ""+ls.end.id);
+		return e;
 	}
 	
 	/**
-	 * Upload the commands to the server.
-	 * @throws IOException
+	 * Create a properties element.
 	 */
-	public void output() throws IOException {
-		Collection<OsmPrimitive> added = new LinkedList<OsmPrimitive>();
-		Collection<OsmPrimitive> modified = new LinkedList<OsmPrimitive>();
-		Collection<OsmPrimitive> deleted = new LinkedList<OsmPrimitive>();
-		for (Command c : commands)
-			c.fillModifiedData(modified, deleted, added);
-		int answer = JOptionPane.showConfirmDialog(Main.main, "Send "+added.size()+" new, "
-				+modified.size()+" modified and "+deleted.size()+" deleted objects to server?");
-		if (answer != JOptionPane.YES_OPTION)
-			return;
+	private Element parseProperty(OsmPrimitive osm, Entry<Key, String> entry) {
+		Element e = new Element("property");
+		Key key = entry.getKey();
+		if (key.id == 0)
+			key.id = generateId();
+		e.setAttribute("id", ""+key.id);
+		e.setAttribute("object", ""+osm.id);
+		e.setAttribute("key", key.name);
+		e.setAttribute("value", entry.getValue());
+		return e;
+	}
+	
+	/**
+	 * Add the id attribute to the element and the properties to the collection.
+	 */
+	private void addProperties(Element e, OsmPrimitive osm, Collection<Element> properties) {
+		if (osm.id == 0)
+			osm.id = generateId();
+		e.setAttribute("id", ""+osm.id);
+		if (osm.keys != null)
+			for (Entry<Key, String> entry : osm.keys.entrySet())
+				properties.add(parseProperty(osm, entry));
+	}
+
+	/**
+	 * Generate an new unused id.
+	 */
+	private long generateId() {
+		while (ids.contains(Long.valueOf(id)))
+			id++;
+		ids.add(id);
+		return id;
+	}
+
+	/**
+	 * Return all used ids in a set. 
+	 */
+	private Set<Long> allUsedIds() {
+		HashSet<Long> ids = new HashSet<Long>();
+		for (OsmPrimitive osm : ds.nodes)
+			addIdAndKeyIds(osm, ids);
+		for (OsmPrimitive osm : ds.pendingLineSegments)
+			addIdAndKeyIds(osm, ids);
+		for (Track t : ds.tracks) {
+			addIdAndKeyIds(t, ids);
+			for (LineSegment ls : t.segments) {
+				addIdAndKeyIds(ls, ids);
+				addIdAndKeyIds(ls.start, ids);
+				addIdAndKeyIds(ls.end, ids);
+			}
+		}
+		return ids;
+	}
+
+	/**
+	 * Return all used ids in the given osm primitive.
+	 */
+	private void addIdAndKeyIds(OsmPrimitive osm, Collection<Long> ids) {
+		ids.add(osm.id);
+		if (osm.keys != null)
+			for (Key key : osm.keys.keySet())
+				ids.add(key.id);
 	}
 }
+
