Index: /trunk/src/org/openstreetmap/josm/actions/HelpAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/HelpAction.java	(revision 2249)
+++ /trunk/src/org/openstreetmap/josm/actions/HelpAction.java	(revision 2250)
@@ -123,6 +123,11 @@
         } else if (e.getActionCommand() == null) {
             String topic = null;
-            Point mouse = Main.parent.getMousePosition();
-            if (mouse != null) {
+            if (e.getSource() instanceof Component) {
+                Component c = SwingUtilities.getRoot((Component)e.getSource());
+                Point mouse = c.getMousePosition();
+                c = SwingUtilities.getDeepestComponentAt(c, mouse.x, mouse.y);
+                topic = contextSensitiveHelp(c);
+            } else {
+                Point mouse = Main.parent.getMousePosition();
                 topic = contextSensitiveHelp(SwingUtilities.getDeepestComponentAt(Main.parent, mouse.x, mouse.y));
             }
@@ -143,4 +148,6 @@
      */
     private String contextSensitiveHelp(Object c) {
+        if (c == null)
+            return null;
         if (c instanceof Helpful)
             return ((Helpful)c).helpTopic();
@@ -159,4 +166,6 @@
         if (c instanceof Action)
             return (String)((Action)c).getValue("help");
+        if (c instanceof JComponent && ((JComponent)c).getClientProperty("help") != null)
+            return (String)((JComponent)c).getClientProperty("help");
         if (c instanceof Component)
             return contextSensitiveHelp(((Component)c).getParent());
Index: /trunk/src/org/openstreetmap/josm/actions/UploadAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 2249)
+++ /trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 2250)
@@ -152,4 +152,26 @@
     }
 
+    public void uploadData(OsmDataLayer layer, APIDataSet apiData) {
+        if (apiData.isEmpty()) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("No changes to upload."),
+                    tr("Warning"),
+                    JOptionPane.INFORMATION_MESSAGE
+            );
+            return;
+        }
+        if (!checkPreUploadConditions(layer, apiData))
+            return;
+        Main.worker.execute(
+                createUploadTask(
+                        layer,
+                        apiData.getPrimitives(),
+                        UploadDialog.getUploadDialog().getChangeset(),
+                        UploadDialog.getUploadDialog().isDoCloseAfterUpload()
+                )
+        );
+    }
+
     public void actionPerformed(ActionEvent e) {
         if (!isEnabled())
@@ -164,25 +186,6 @@
             return;
         }
-
         APIDataSet apiData = new APIDataSet(Main.main.getCurrentDataSet());
-        if (apiData.isEmpty()) {
-            JOptionPane.showMessageDialog(
-                    Main.parent,
-                    tr("No changes to upload."),
-                    tr("Warning"),
-                    JOptionPane.INFORMATION_MESSAGE
-            );
-            return;
-        }
-        if (!checkPreUploadConditions(Main.map.mapView.getEditLayer(), apiData))
-            return;
-        Main.worker.execute(
-                createUploadTask(
-                        Main.map.mapView.getEditLayer(),
-                        apiData.getPrimitives(),
-                        UploadDialog.getUploadDialog().getChangeset(),
-                        UploadDialog.getUploadDialog().isDoCloseAfterUpload()
-                )
-        );
+        uploadData(Main.map.mapView.getEditLayer(), apiData);
     }
 
Index: /trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java	(revision 2250)
+++ /trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java	(revision 2250)
@@ -0,0 +1,344 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
+import java.util.logging.Logger;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.APIDataSet;
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.io.UploadSelectionDialog;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.ExceptionUtil;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Uploads the current selection to the server.
+ *
+ */
+public class UploadSelectionAction extends JosmAction{
+    static private Logger logger = Logger.getLogger(UploadSelectionAction.class.getName());
+
+    public UploadSelectionAction() {
+        super(
+                tr("Upload selection..."),
+                "uploadselection",
+                tr("Upload the current selection to the OSM server."),
+                null, /* no shortcut */
+                true);
+    }
+
+    /**
+     * Refreshes the enabled state
+     *
+     */
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(getEditLayer() != null);
+    }
+
+    protected Set<OsmPrimitive> getDeletedPrimitives(DataSet ds) {
+        HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
+        for (OsmPrimitive p : ds.nodes) {
+            if (p.isDeleted() && p.getId() > 0 && p.isVisible() && p.isModified()) {
+                ret.add(p);
+            }
+        }
+        for (OsmPrimitive p : ds.ways) {
+            if (p.isDeleted() && p.getId() > 0 && p.isVisible() && p.isModified()) {
+                ret.add(p);
+            }
+        }
+        for (OsmPrimitive p : ds.relations) {
+            if (p.isDeleted() && p.getId() > 0 && p.isVisible() && p.isModified()) {
+                ret.add(p);
+            }
+        }
+        return ret;
+    }
+
+    protected Set<OsmPrimitive> getModifiedPrimitives(Collection<OsmPrimitive> primitives) {
+        HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
+        for (OsmPrimitive p: primitives) {
+            if (p.getId() == 0) {
+                ret.add(p);
+            } else if (p.isVisible() && p.isModified() && !p.incomplete) {
+                ret.add(p);
+            }
+        }
+        return ret;
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        if (!isEnabled())
+            return;
+        UploadHullBuilder builder = new UploadHullBuilder();
+        UploadSelectionDialog dialog = new UploadSelectionDialog();
+        Collection<OsmPrimitive> modifiedCandidates = getModifiedPrimitives(getEditLayer().data.getSelected());
+        Collection<OsmPrimitive> deletedCandiates = getDeletedPrimitives(getEditLayer().data);
+        if (modifiedCandidates.isEmpty() && deletedCandiates.isEmpty()) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("No changes to upload."),
+                    tr("Warning"),
+                    JOptionPane.INFORMATION_MESSAGE
+            );
+            return;
+        }
+        dialog.populate(
+                modifiedCandidates,
+                deletedCandiates
+        );
+        dialog.setVisible(true);
+        if (dialog.isCanceled())
+            return;
+        Collection<OsmPrimitive> toUpload = builder.build(dialog.getSelectedPrimitives());
+        if (toUpload.isEmpty()) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("No changes to upload."),
+                    tr("Warning"),
+                    JOptionPane.INFORMATION_MESSAGE
+            );
+            return;
+        }
+        uploadPrimitives(getEditLayer(), toUpload);
+    }
+
+    protected boolean hasPrimitivesToDelete(Collection<OsmPrimitive> primitives) {
+        for (OsmPrimitive p: primitives)
+            if (p.isDeleted() && p.isModified() && p.getId() > 0)
+                return true;
+        return false;
+    }
+    /**
+     * Uploads the primitives in <code>toUpload</code> to the server. Only
+     * uploads primitives which are either new, modified or deleted.
+     * 
+     * Also checks whether <code>toUpload</code> has to be extended with
+     * deleted parents in order to avoid precondition violations on the server.
+     * 
+     * @param layer the data layer from which we upload a subset of primitives
+     * @param toUpload the primitives to upload. If null or empty returns immediatelly
+     */
+    public void uploadPrimitives(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
+        if (toUpload == null || toUpload.isEmpty()) return;
+        UploadHullBuilder builder = new UploadHullBuilder();
+        toUpload = builder.build(toUpload);
+        if (hasPrimitivesToDelete(toUpload)) {
+            // runs the check for deleted parents and then invokes
+            // processPostParentChecker()
+            //
+            Main.worker.submit(new DeletedParentsChecker(layer, toUpload));
+        } else {
+            processPostParentChecker(layer, toUpload);
+        }
+    }
+
+    protected void processPostParentChecker(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
+        APIDataSet ds = new APIDataSet(toUpload);
+        UploadAction action = new UploadAction();
+        action.uploadData(layer, ds);
+    }
+
+    /**
+     * Computes the collection of primitives to upload, given a collection of candidate
+     * primitives.
+     * Some of the candidates are excluded, i.e. if they aren't modified or if they
+     * aren't visible.
+     * Other primitives are added. A typical case is a primitive which is new and and
+     * which is referred by a modified relation. In order to upload the relation the
+     * new primitive has to be uploaded as well, even if it isn't included in the
+     * list of candidate primitives.
+     * 
+     */
+    static class UploadHullBuilder implements Visitor {
+        private Set<OsmPrimitive> hull;
+
+        public UploadHullBuilder(){
+            hull = new HashSet<OsmPrimitive>();
+        }
+
+        public void visit(Node n) {
+            if (n.getId() == 0 || ((n.isModified() || n.isDeleted()) && n.isVisible())) {
+                // upload new nodes as well as modified and deleted ones
+                hull.add(n);
+            }
+        }
+
+        public void visit(Way w) {
+            if (w.getId() == 0 || ((w.isModified() || w.isDeleted()) && w.isVisible())) {
+                // upload new ways as well as modified and deleted ones
+                hull.add(w);
+                for (Node n: w.getNodes()) {
+                    // we upload modified nodes even if they aren't in the current
+                    // selection.
+                    n.visit(this);
+                }
+            }
+        }
+
+        public void visit(Relation r) {
+            if (r.getId() == 0 || ((r.isModified() || r.isDeleted()) && r.isVisible())) {
+                hull.add(r);
+                for (OsmPrimitive p : r.getMemberPrimitives()) {
+                    // add new relation members. Don't include modified
+                    // relation members. r shouldn't refer to deleted primitives,
+                    // so wont check here for deleted primitives here
+                    //
+                    if (p.getId() == 0) {
+                        p.visit(this);
+                    }
+                }
+            }
+        }
+
+        public void visit(Changeset cs) {
+            // do nothing
+        }
+
+        /**
+         * Builds the "hull" of primitives to be uploaded given a base collection
+         * of osm primitives.
+         * 
+         * @param base the base collection. Must not be null.
+         * @return the "hull"
+         * @throws IllegalArgumentException thrown if base is null
+         */
+        public Set<OsmPrimitive> build(Collection<OsmPrimitive> base) throws IllegalArgumentException{
+            if (base == null)
+                throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "base"));
+            hull = new HashSet<OsmPrimitive>();
+            for (OsmPrimitive p: base) {
+                p.visit(this);
+            }
+            return hull;
+        }
+    }
+
+    class DeletedParentsChecker extends PleaseWaitRunnable {
+        private boolean canceled;
+        private Exception lastException;
+        private Collection<OsmPrimitive> toUpload;
+        private OsmDataLayer layer;
+        private OsmServerBackreferenceReader reader;
+
+        /**
+         * 
+         * @param layer the data layer for which a collection of selected primitives is uploaded
+         * @param toUpload the collection of primitives to upload
+         */
+        public DeletedParentsChecker(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
+            super(tr("Checking parents for deleted objects"));
+            this.toUpload = toUpload;
+            this.layer = layer;
+        }
+
+        @Override
+        protected void cancel() {
+            this.canceled = true;
+            synchronized (this) {
+                if (reader != null) {
+                    reader.cancel();
+                }
+            }
+        }
+
+        @Override
+        protected void finish() {
+            if (canceled)
+                return;
+            if (lastException != null) {
+                ExceptionUtil.explainException(lastException);
+                return;
+            }
+            Runnable r = new Runnable() {
+                public void run() {
+                    processPostParentChecker(layer, toUpload);
+                }
+            };
+            SwingUtilities.invokeLater(r);
+        }
+
+        /**
+         * Replies the collection of deleted OSM primitives for which we have to check whether
+         * there are dangling references on the server.
+         * 
+         * @return
+         */
+        protected Set<OsmPrimitive> getPrimitivesToCheckForParents() {
+            HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
+            for (OsmPrimitive p: toUpload) {
+                if (p.isDeleted() && p.getId() >0) {
+                    ret.add(p);
+                }
+            }
+            return ret;
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            try {
+                Stack<OsmPrimitive> toCheck = new Stack<OsmPrimitive>();
+                toCheck.addAll(getPrimitivesToCheckForParents());
+                Set<OsmPrimitive> checked = new HashSet<OsmPrimitive>();
+                while(!toCheck.isEmpty()) {
+                    if (canceled) return;
+                    OsmPrimitive current = toCheck.pop();
+                    synchronized(this) {
+                        reader = new OsmServerBackreferenceReader(current);
+                    }
+                    getProgressMonitor().subTask(tr("Reading parents of ''{0}''", current.getDisplayName(DefaultNameFormatter.getInstance())));
+                    DataSet ds = reader.parseOsm(getProgressMonitor().createSubTaskMonitor(1, false));
+                    synchronized(this) {
+                        reader = null;
+                    }
+                    checked.add(current);
+                    getProgressMonitor().subTask(tr("Checking for deleted parents in the local dataset"));
+                    for (OsmPrimitive p: ds.allPrimitives()) {
+                        if (canceled) return;
+                        OsmPrimitive myDeletedParent = layer.data.getPrimitiveById(p.getId(), OsmPrimitiveType.from(p));
+                        // our local dataset includes a deleted parent of a primitive we want
+                        // to delete. Include this parent in the collection of uploaded primitives
+                        //
+                        if (myDeletedParent != null && myDeletedParent.isDeleted()) {
+                            if (!toUpload.contains(myDeletedParent)) {
+                                toUpload.add(myDeletedParent);
+                            }
+                            if (!checked.contains(myDeletedParent)) {
+                                toCheck.push(myDeletedParent);
+                            }
+                        }
+                    }
+                }
+            } catch(Exception e) {
+                if (canceled)
+                    // ignore exception
+                    return;
+                lastException = e;
+            }
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/data/APIDataSet.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/APIDataSet.java	(revision 2249)
+++ /trunk/src/org/openstreetmap/josm/data/APIDataSet.java	(revision 2250)
@@ -66,6 +66,36 @@
             }
         }
-    }
-
+        sortDeleted();
+    }
+
+    /**
+     * Ensures that primitives are deleted in the following order: Relations, then Ways,
+     * then Nodes.
+     * 
+     */
+    protected void sortDeleted() {
+        Collections.sort(
+                toDelete,
+                new Comparator<OsmPrimitive>() {
+                    public int compare(OsmPrimitive o1, OsmPrimitive o2) {
+                        if (o1 instanceof Node && o2 instanceof Node)
+                            return 0;
+                        else if (o1 instanceof Node)
+                            return 1;
+                        else if (o2 instanceof Node)
+                            return -1;
+
+                        if (o1 instanceof Way && o2 instanceof Way)
+                            return 0;
+                        else if (o1 instanceof Way && o2 instanceof Relation)
+                            return 1;
+                        else if (o2 instanceof Way && o1 instanceof Relation)
+                            return -1;
+
+                        return 0;
+                    }
+                }
+        );
+    }
     /**
      * initializes the API data set with the modified primitives in <code>ds</code>
@@ -76,4 +106,26 @@
         this();
         init(ds);
+    }
+
+    /**
+     * initializes the API data set with the primitives in <code>primitives</code>
+     * 
+     * @param primitives the collection of primitives
+     */
+    public APIDataSet(Collection<OsmPrimitive> primitives) {
+        this();
+        toAdd.clear();
+        toUpdate.clear();
+        toDelete.clear();
+        for (OsmPrimitive osm: primitives) {
+            if (osm.getId() == 0 && !osm.isDeleted()) {
+                toAdd.addLast(osm);
+            } else if (osm.isModified() && !osm.isDeleted()) {
+                toUpdate.addLast(osm);
+            } else if (osm.isDeleted() && osm.getId() != 0 && osm.isModified()) {
+                toDelete.addFirst(osm);
+            }
+        }
+        sortDeleted();
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 2249)
+++ /trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 2250)
@@ -66,4 +66,5 @@
 import org.openstreetmap.josm.actions.UpdateSelectionAction;
 import org.openstreetmap.josm.actions.UploadAction;
+import org.openstreetmap.josm.actions.UploadSelectionAction;
 import org.openstreetmap.josm.actions.ZoomInAction;
 import org.openstreetmap.josm.actions.ZoomOutAction;
@@ -104,4 +105,5 @@
     public final JosmAction updateSelection = new UpdateSelectionAction();
     public final JosmAction upload = new UploadAction();
+    public final JosmAction uploadSelection = new UploadSelectionAction();
     public final JosmAction exit = new ExitAction();
 
@@ -206,4 +208,5 @@
         add(fileMenu, downloadReferrers);
         add(fileMenu, upload);
+        add(fileMenu, uploadSelection);
         add(fileMenu, update);
         add(fileMenu, updateSelection);
Index: /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowser.java	(revision 2249)
+++ /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowser.java	(revision 2250)
@@ -96,5 +96,5 @@
 
         pane.setOneTouchExpandable(true);
-        pane.setDividerLocation(150);
+        pane.setDividerLocation(200);
 
         Dimension minimumSize = new Dimension(100, 50);
Index: /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java	(revision 2249)
+++ /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java	(revision 2250)
@@ -7,4 +7,6 @@
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.text.SimpleDateFormat;
 import java.util.Observable;
@@ -108,9 +110,18 @@
         String url = AbstractInfoAction.getBaseBrowseUrl() + "/changeset/" + getPrimitive().getChangesetId();
         lblChangeset.setUrl(url);
-        lblChangeset.setDescription(tr("{0}", getPrimitive().getChangesetId()));
+        lblChangeset.setDescription(Long.toString(getPrimitive().getChangesetId()));
 
-        url = AbstractInfoAction.getBaseUserUrl() + "/" + getPrimitive().getUser();
-        lblUser.setUrl(url);
-        lblUser.setDescription(tr("{0}", getPrimitive().getUser()));
+        try {
+            if (getPrimitive().getUid() != -1) {
+                url = AbstractInfoAction.getBaseUserUrl() + "/" +  URLEncoder.encode(getPrimitive().getUser(), "UTF-8").replaceAll("\\+", "%20");
+                lblUser.setUrl(url);
+            } else {
+                lblUser.setUrl(null);
+            }
+        } catch(UnsupportedEncodingException e) {
+            e.printStackTrace();
+            lblUser.setUrl(null);
+        }
+        lblUser.setDescription(getPrimitive().getUser());
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/history/VersionTableCellRenderer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/VersionTableCellRenderer.java	(revision 2249)
+++ /trunk/src/org/openstreetmap/josm/gui/history/VersionTableCellRenderer.java	(revision 2250)
@@ -59,5 +59,11 @@
             sb.append("");
         } else {
-            sb.append(tr("Version {0}", Long.toString(primitive.getVersion())));
+            String msg = tr(
+                    "Version {0}, {1} (by {2})",
+                    Long.toString(primitive.getVersion()),
+                    new SimpleDateFormat().format(primitive.getTimestamp()),
+                    primitive.getUser()
+            );
+            sb.append(msg);
         }
         setText(sb.toString());
Index: /trunk/src/org/openstreetmap/josm/gui/io/UploadSelectionDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/UploadSelectionDialog.java	(revision 2250)
+++ /trunk/src/org/openstreetmap/josm/gui/io/UploadSelectionDialog.java	(revision 2250)
@@ -0,0 +1,300 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractListModel;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BorderFactory;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.WindowGeometry;
+
+/**
+ * This dialog can be used to select individual object for uploading.
+ * 
+ *
+ */
+public class UploadSelectionDialog extends JDialog {
+
+    private OsmPrimitiveList lstSelectedPrimitives;
+    private OsmPrimitiveList lstDeletedPrimitives;
+    private JSplitPane spLists;
+    private boolean canceled;
+    private SideButton btnContinue;
+
+    protected JPanel buildSelectedPrimitivesPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new BorderLayout());
+        JLabel lbl = new JLabel(tr("<html>Mark modified objects <strong>from the current selection</strong> to be uploaded to the server.</html>"));
+        lbl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+        pnl.add(lbl, BorderLayout.NORTH);
+        pnl.add(new JScrollPane(lstSelectedPrimitives = new OsmPrimitiveList()), BorderLayout.CENTER);
+        return pnl;
+    }
+
+    protected JPanel buildDeletedPrimitivesPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new BorderLayout());
+        JLabel lbl = new JLabel(tr("<html>Mark <strong>locally deleted objects</strong> to be deleted on the server.</html>"));
+        lbl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+        pnl.add(lbl, BorderLayout.NORTH);
+        pnl.add(new JScrollPane(lstDeletedPrimitives = new OsmPrimitiveList()), BorderLayout.CENTER);
+        return pnl;
+    }
+
+    protected JPanel buildButtonPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new FlowLayout());
+        pnl.add(new SideButton(new CancelAction()));
+        ContinueAction continueAction = new ContinueAction();
+        pnl.add(btnContinue = new SideButton(continueAction));
+        btnContinue.setFocusable(true);
+        lstDeletedPrimitives.getSelectionModel().addListSelectionListener(continueAction);
+        lstSelectedPrimitives.getSelectionModel().addListSelectionListener(continueAction);
+        return pnl;
+    }
+
+    protected void build() {
+        setLayout(new BorderLayout());
+        spLists = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+        spLists.setTopComponent(buildSelectedPrimitivesPanel());
+        spLists.setBottomComponent(buildDeletedPrimitivesPanel());
+        add(spLists, BorderLayout.CENTER);
+        add(buildButtonPanel(), BorderLayout.SOUTH);
+        addWindowListener(
+                new WindowAdapter() {
+                    @Override
+                    public void windowOpened(WindowEvent e) {
+                        spLists.setDividerLocation(0.5);
+                        btnContinue.requestFocusInWindow();
+                    }
+
+                    @Override
+                    public void windowClosing(WindowEvent e) {
+                        setCanceled(true);
+                    }
+                }
+        );
+        setTitle(tr("Select objects to upload"));
+        ActionMap am = getRootPane().getActionMap();
+        InputMap im = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), "help");
+        am.put("help", Main.main.menu.help);
+        getRootPane().putClientProperty("help", "Dialog/UploadSelection");
+    }
+
+    public UploadSelectionDialog() {
+        super(JOptionPane.getFrameForComponent(Main.parent), true /* modal */);
+        build();
+    }
+
+    public void populate(Collection<OsmPrimitive> selected, Collection<OsmPrimitive> deleted) {
+        if (selected != null) {
+            lstSelectedPrimitives.getOsmPrimitiveListModel().setPrimitives(new ArrayList<OsmPrimitive>(selected));
+            if (!selected.isEmpty()) {
+                lstSelectedPrimitives.getSelectionModel().setSelectionInterval(0, selected.size()-1);
+            } else {
+                lstSelectedPrimitives.getSelectionModel().clearSelection();
+            }
+        } else {
+            lstSelectedPrimitives.getOsmPrimitiveListModel().setPrimitives(null);
+            lstSelectedPrimitives.getSelectionModel().clearSelection();
+        }
+
+        if (deleted != null) {
+            lstDeletedPrimitives.getOsmPrimitiveListModel().setPrimitives(new ArrayList<OsmPrimitive>(deleted));
+        } else {
+            lstDeletedPrimitives.getOsmPrimitiveListModel().setPrimitives(null);
+        }
+    }
+
+    public boolean isCanceled() {
+        return canceled;
+    }
+
+    protected void setCanceled(boolean canceled) {
+        this.canceled = canceled;
+    }
+
+    public List<OsmPrimitive> getSelectedPrimitives() {
+        ArrayList<OsmPrimitive> ret  = new ArrayList<OsmPrimitive>();
+        System.out.println("selected length:" +lstSelectedPrimitives.getSelectedIndices().length);
+        for (int i=0; i< lstSelectedPrimitives.getSelectedIndices().length;i++) {
+            System.out.println("selected:" +lstSelectedPrimitives.getSelectedIndices()[i]);
+        }
+        ret.addAll(lstSelectedPrimitives.getOsmPrimitiveListModel().getPrimitives(lstSelectedPrimitives.getSelectedIndices()));
+        ret.addAll(lstDeletedPrimitives.getOsmPrimitiveListModel().getPrimitives(lstDeletedPrimitives.getSelectedIndices()));
+        return ret;
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        if (visible) {
+            new WindowGeometry(
+                    getClass().getName() + ".geometry",
+                    WindowGeometry.centerInWindow(
+                            Main.parent,
+                            new Dimension(200,400)
+                    )
+            ).apply(this);
+        } else if (!visible && isShowing()){
+            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
+        }
+        super.setVisible(visible);
+    }
+
+    static class OsmPrimitiveList extends JList {
+        protected void init() {
+            setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+            setCellRenderer(new OsmPrimitivRenderer());
+        }
+
+        public OsmPrimitiveList() {
+            super(new OsmPrimitiveListModel());
+            init();
+        }
+
+        public OsmPrimitiveList(OsmPrimitiveListModel model) {
+            super(model);
+            init();
+        }
+
+        public OsmPrimitiveListModel getOsmPrimitiveListModel() {
+            return (OsmPrimitiveListModel)getModel();
+        }
+    }
+
+    static class OsmPrimitiveListModel extends AbstractListModel {
+        private List<OsmPrimitive> data;
+
+        public OsmPrimitiveListModel() {
+        }
+
+        protected void sort() {
+            if (data == null)
+                return;
+            Collections.sort(
+                    data,
+                    new Comparator<OsmPrimitive>() {
+                        private DefaultNameFormatter formatter = DefaultNameFormatter.getInstance();
+                        public int compare(OsmPrimitive o1, OsmPrimitive o2) {
+                            int ret = OsmPrimitiveType.from(o1).compareTo(OsmPrimitiveType.from(o2));
+                            if (ret != 0) return ret;
+                            return o1.getDisplayName(formatter).compareTo(o1.getDisplayName(formatter));
+                        }
+                    }
+            );
+        }
+
+        public OsmPrimitiveListModel(List<OsmPrimitive> data) {
+            setPrimitives(data);
+        }
+
+        public void setPrimitives(List<OsmPrimitive> data) {
+            this.data = data;
+            sort();
+            if (data != null) {
+                fireContentsChanged(this,0, data.size());
+            } else {
+                fireContentsChanged(this, 0,0);
+            }
+        }
+
+        public Object getElementAt(int index) {
+            if (data == null)
+                return null;
+            return data.get(index);
+        }
+
+        public int getSize() {
+            if (data == null)
+                return 0;
+            return data.size();
+        }
+
+        public List<OsmPrimitive> getPrimitives(int [] indices) {
+            if (indices == null || indices.length == 0)
+                return Collections.emptyList();
+            ArrayList<OsmPrimitive> ret = new ArrayList<OsmPrimitive>(indices.length);
+            for (int i: indices) {
+                if (i < 0) {
+                    continue;
+                }
+                ret.add(data.get(i));
+            }
+            return ret;
+        }
+    }
+
+    class CancelAction extends AbstractAction {
+        public CancelAction() {
+            putValue(Action.SHORT_DESCRIPTION, tr("Cancel uploading"));
+            putValue(Action.NAME, tr("Cancel"));
+            putValue(Action.SMALL_ICON, ImageProvider.get("", "cancel"));
+            setEnabled(true);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            setCanceled(true);
+            setVisible(false);
+        }
+    }
+
+    class ContinueAction extends AbstractAction implements ListSelectionListener {
+        public ContinueAction() {
+            putValue(Action.SHORT_DESCRIPTION, tr("Continue uploading"));
+            putValue(Action.NAME, tr("Continue"));
+            putValue(Action.SMALL_ICON, ImageProvider.get("", "upload"));
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            setCanceled(false);
+            setVisible(false);
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(lstSelectedPrimitives.getSelectedIndex() >=0
+                    || lstDeletedPrimitives.getSelectedIndex() >= 0);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            updateEnabledState();
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/tools/UrlLabel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/UrlLabel.java	(revision 2249)
+++ /trunk/src/org/openstreetmap/josm/tools/UrlLabel.java	(revision 2250)
@@ -34,5 +34,9 @@
     protected void refresh() {
         setContentType("text/html");
-        setText("<html><a href=\""+url+"\">"+description+"</a></html>");
+        if (url != null) {
+            setText("<html><a href=\""+url+"\">"+description+"</a></html>");
+        } else {
+            setText("<html>" + description + "</html>");
+        }
         setToolTipText(url);
     }
