Index: /trunk/src/org/openstreetmap/josm/actions/AddNodeAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/AddNodeAction.java	(revision 3265)
+++ /trunk/src/org/openstreetmap/josm/actions/AddNodeAction.java	(revision 3266)
@@ -5,44 +5,13 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.FlowLayout;
-import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
 import java.awt.event.KeyEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.text.NumberFormat;
-import java.text.ParsePosition;
-import java.util.Locale;
-
-import javax.swing.AbstractAction;
-import javax.swing.BorderFactory;
-import javax.swing.JComponent;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
-import javax.swing.KeyStroke;
-import javax.swing.UIManager;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.command.AddCommand;
-import org.openstreetmap.josm.data.coor.CoordinateFormat;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.gui.SideButton;
-import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
-import org.openstreetmap.josm.gui.help.HelpUtil;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.gui.dialogs.LatLonDialog;
 import org.openstreetmap.josm.tools.Shortcut;
-import org.openstreetmap.josm.tools.WindowGeometry;
 
 /**
@@ -84,252 +53,3 @@
         setEnabled(getEditLayer() != null);
     }
-
-    static private class LatLonDialog extends JDialog {
-        private static final Color BG_COLOR_ERROR = new Color(255,224,224);
-
-        private JTextField tfLat;
-        private JTextField tfLon;
-        private boolean canceled = false;
-        private LatLon coordinates;
-        private OKAction actOK;
-        private CancelAction actCancel;
-
-        protected JPanel buildInputForm() {
-            JPanel pnl = new JPanel(new GridBagLayout());
-            pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
-            pnl.add(new JLabel("<html>"+
-                    tr("Enter the coordinates for the new node.") +
-                    "<br>" + tr("Use decimal degrees.") +
-                    "<br>" + tr("Negative values denote Western/Southern hemisphere.")),
-                    GBC.eol());
-
-            pnl.add(new JLabel(tr("Latitude")), GBC.std().insets(0,10,5,0));
-            tfLat = new JTextField(12);
-            pnl.add(tfLat, GBC.eol().insets(0,10,0,0));
-            pnl.add(new JLabel(tr("Longitude")), GBC.std().insets(0,0,5,10));
-            tfLon = new JTextField(12);
-            pnl.add(tfLon, GBC.eol().insets(0,0,0,10));
-
-            // parse and verify input on the fly
-            //
-            LatLonInputVerifier inputVerifier = new LatLonInputVerifier();
-            tfLat.getDocument().addDocumentListener(inputVerifier);
-            tfLon.getDocument().addDocumentListener(inputVerifier);
-
-            // select the text in the field on focus
-            //
-            TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
-            tfLat.addFocusListener(focusHandler);
-            tfLon.addFocusListener(focusHandler);
-            return pnl;
-        }
-
-        protected JPanel buildButtonRow() {
-            JPanel pnl = new JPanel(new FlowLayout());
-
-            SideButton btn;
-            pnl.add(btn = new SideButton(actOK = new OKAction()));
-            makeButtonRespondToEnter(btn);
-            pnl.add(btn = new SideButton(actCancel = new CancelAction()));
-            makeButtonRespondToEnter(btn);
-            pnl.add(new SideButton(new ContextSensitiveHelpAction(ht("/Action/AddNode"))));
-            return pnl;
-        }
-
-        protected void makeButtonRespondToEnter(SideButton btn) {
-            btn.setFocusable(true);
-            btn.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
-            btn.getActionMap().put("enter", btn.getAction());
-        }
-
-        protected void build() {
-            getContentPane().setLayout(new BorderLayout());
-            getContentPane().add(buildInputForm(), BorderLayout.CENTER);
-            getContentPane().add(buildButtonRow(), BorderLayout.SOUTH);
-            pack();
-
-            // make dialog respond to ESCAPE
-            //
-            getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "escape");
-            getRootPane().getActionMap().put("escape", actCancel);
-
-            // make dialog respond to F1
-            //
-            HelpUtil.setHelpContext(getRootPane(), ht("/Action/AddNode"));
-        }
-
-        public LatLonDialog(Component parent) {
-            super(JOptionPane.getFrameForComponent(parent), true /* modal */);
-            setTitle(tr("Add Node..."));
-            build();
-            addWindowListener(new WindowEventHandler());
-            setCoordinates(null);
-        }
-
-        public void setCoordinates(LatLon coordinates) {
-            if (coordinates == null) {
-                coordinates = new LatLon(0,0);
-            }
-            this.coordinates = coordinates;
-            tfLat.setText(coordinates.latToString(CoordinateFormat.DECIMAL_DEGREES));
-            tfLon.setText(coordinates.lonToString(CoordinateFormat.DECIMAL_DEGREES));
-            actOK.setEnabled(true);
-        }
-
-        public LatLon getCoordinates() {
-            return coordinates;
-        }
-
-        protected void setErrorFeedback(JTextField tf, String message) {
-            tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
-            tf.setToolTipText(message);
-            tf.setBackground(BG_COLOR_ERROR);
-        }
-
-        protected void clearErrorFeedback(JTextField tf, String message) {
-            tf.setBorder(UIManager.getBorder("TextField.border"));
-            tf.setToolTipText(message);
-            tf.setBackground(UIManager.getColor("TextField.background"));
-        }
-
-        protected Double parseDoubleFromUserInput(String input) {
-            if (input == null) return null;
-            // remove white space and an optional degree symbol
-            //
-            input = input.trim();
-            input = input.replaceAll("\u00B0", ""); // the degree symbol
-
-            // try to parse using the current locale
-            //
-            NumberFormat f = NumberFormat.getNumberInstance();
-            Number n=null;
-            ParsePosition pp = new ParsePosition(0);
-            n = f.parse(input,pp);
-            if (pp.getErrorIndex() >= 0 || pp.getIndex()<input.length()) {
-                // fall back - try to parse with the english locale
-                //
-                pp = new ParsePosition(0);
-                f = NumberFormat.getNumberInstance(Locale.ENGLISH);
-                n = f.parse(input, pp);
-                if (pp.getErrorIndex() >= 0 || pp.getIndex()<input.length())
-                    return null;
-            }
-            return n== null ? null : n.doubleValue();
-        }
-
-        protected Double parseLatFromUserInput() {
-            Double d = parseDoubleFromUserInput(tfLat.getText());
-            if (d == null || ! LatLon.isValidLat(d)) {
-                setErrorFeedback(tfLat, tr("Please enter a valid latitude in the range -90..90"));
-                return null;
-            } else {
-                clearErrorFeedback(tfLat,tr("Please enter a latitude in the range -90..90"));
-            }
-            return d;
-        }
-
-        protected Double parseLonFromUserInput() {
-            Double d = parseDoubleFromUserInput(tfLon.getText());
-            if (d == null || ! LatLon.isValidLon(d)) {
-                setErrorFeedback(tfLon, tr("Please enter a valid longitude in the range -180..180"));
-                return null;
-            } else {
-                clearErrorFeedback(tfLon,tr("Please enter a longitude in the range -180..180"));
-            }
-            return d;
-        }
-
-        protected void parseUserInput() {
-            Double lat = parseLatFromUserInput();
-            Double lon = parseLonFromUserInput();
-            if (lat == null || lon == null) {
-                coordinates = null;
-                actOK.setEnabled(false);
-            } else {
-                coordinates = new LatLon(lat,lon);
-                actOK.setEnabled(true);
-            }
-        }
-
-        public boolean isCanceled() {
-            return canceled;
-        }
-
-        protected void setCanceled(boolean canceled) {
-            this.canceled = canceled;
-        }
-
-        @Override
-        public void setVisible(boolean visible) {
-            if (visible) {
-                setCanceled(false);
-                WindowGeometry.centerInWindow(Main.parent, getSize()).applySafe(this);
-            }
-            super.setVisible(visible);
-        }
-
-        class OKAction extends AbstractAction {
-            public OKAction() {
-                putValue(NAME, tr("OK"));
-                putValue(SHORT_DESCRIPTION, tr("Close the dialog and create a new node"));
-                putValue(SMALL_ICON, ImageProvider.get("ok"));
-            }
-
-            public void actionPerformed(ActionEvent e) {
-                setCanceled(false);
-                setVisible(false);
-            }
-        }
-
-        class CancelAction extends AbstractAction {
-            public CancelAction() {
-                putValue(NAME, tr("Cancel"));
-                putValue(SHORT_DESCRIPTION, tr("Close the dialog, do not create a new node"));
-                putValue(SMALL_ICON, ImageProvider.get("cancel"));
-            }
-
-            public void actionPerformed(ActionEvent e) {
-                setCanceled(true);
-                setVisible(false);
-            }
-        }
-
-        class LatLonInputVerifier implements DocumentListener {
-            public void changedUpdate(DocumentEvent e) {
-                parseUserInput();
-            }
-
-            public void insertUpdate(DocumentEvent e) {
-                parseUserInput();
-            }
-
-            public void removeUpdate(DocumentEvent e) {
-                parseUserInput();
-            }
-        }
-
-        static class TextFieldFocusHandler implements FocusListener {
-            public void focusGained(FocusEvent e) {
-                Component c = e.getComponent();
-                if (c instanceof JTextField) {
-                    JTextField tf = (JTextField)c;
-                    tf.selectAll();
-                }
-            }
-            public void focusLost(FocusEvent e) {}
-        }
-
-        class WindowEventHandler extends WindowAdapter {
-            @Override
-            public void windowClosing(WindowEvent e) {
-                setCanceled(true);
-                setVisible(false);
-            }
-
-            @Override
-            public void windowOpened(WindowEvent e) {
-                tfLat.requestFocusInWindow();
-            }
-        }
-    }
 }
Index: /trunk/src/org/openstreetmap/josm/actions/MoveNodeAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/MoveNodeAction.java	(revision 3266)
+++ /trunk/src/org/openstreetmap/josm/actions/MoveNodeAction.java	(revision 3266)
@@ -0,0 +1,71 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.util.Collection;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.MoveCommand;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.dialogs.LatLonDialog;
+
+/**
+ * This action displays a dialog with the coordinates of a node where the user can change them,
+ * and when ok is pressed, the node is relocated to the specified position.
+ */
+public final class MoveNodeAction extends JosmAction {
+
+    public MoveNodeAction() {
+        super(tr("Move Node..."), "movenode", tr("Edit latitude and longitude of a node."),
+                null, /* no shortcut */
+                true);
+        putValue("help", ht("/Action/MoveNode"));
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        if (!isEnabled() || (getCurrentDataSet().getSelectedNodes().size() != 1))
+            return;
+
+        LatLonDialog dialog = new LatLonDialog(Main.parent);
+        Node n = (Node) getCurrentDataSet().getSelectedNodes().toArray()[0];
+        dialog.setCoordinates(n.getCoor());
+        dialog.setVisible(true);
+        if (dialog.isCanceled())
+            return;
+
+        LatLon coordinates = dialog.getCoordinates();
+        if (coordinates == null)
+            return;
+
+        // move the node
+        Main.main.undoRedo.add(new MoveCommand(n, coordinates));
+        Main.map.mapView.repaint();
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        if (getCurrentDataSet() == null) {
+            setEnabled(false);
+        } else {
+            updateEnabledState(getCurrentDataSet().getSelected());
+        }
+    }
+
+    @Override
+    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
+        if (selection == null || selection.isEmpty()) {
+            setEnabled(false);
+            return;
+        }
+        if ((selection.size()) == 1 && (selection.toArray()[0] instanceof Node) ) {
+            setEnabled(true);
+        } else {
+            setEnabled(false);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/command/MoveCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/MoveCommand.java	(revision 3265)
+++ /trunk/src/org/openstreetmap/josm/command/MoveCommand.java	(revision 3266)
@@ -12,4 +12,6 @@
 import javax.swing.JLabel;
 
+import org.openstreetmap.josm.data.coor.CachedLatLon;
+import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.Node;
@@ -55,4 +57,13 @@
         this(Collections.singleton(osm), x, y);
     }
+
+    public MoveCommand(Node node, LatLon position) {
+        this(Collections.singleton((OsmPrimitive) node), node.getEastNorth().sub(new CachedLatLon(position).getEastNorth()));
+    }
+
+    public MoveCommand(Collection<OsmPrimitive> objects, EastNorth offset) {
+        this(objects, offset.getX(), offset.getY());
+    }
+
     /**
      * Create a MoveCommand and assign the initial object set and movement vector.
Index: /trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 3265)
+++ /trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 3266)
@@ -45,4 +45,5 @@
 import org.openstreetmap.josm.actions.MergeSelectionAction;
 import org.openstreetmap.josm.actions.MirrorAction;
+import org.openstreetmap.josm.actions.MoveNodeAction;
 import org.openstreetmap.josm.actions.NewAction;
 import org.openstreetmap.josm.actions.OpenFileAction;
@@ -141,4 +142,5 @@
     public final JosmAction mirror = new MirrorAction();
     public final AddNodeAction addnode = new AddNodeAction();
+    public final MoveNodeAction movenode = new MoveNodeAction();
     public final JosmAction createCircle = new CreateCircleAction();
     public final JosmAction mergeNodes = new MergeNodesAction();
@@ -291,4 +293,5 @@
         toolsMenu.addSeparator();
         add(toolsMenu, addnode);
+        add(toolsMenu, movenode);
         add(toolsMenu, createCircle);
         toolsMenu.addSeparator();
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java	(revision 3266)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java	(revision 3266)
@@ -0,0 +1,293 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.UIManager;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.CoordinateFormat;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
+import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.WindowGeometry;
+
+public class LatLonDialog extends JDialog {
+    private static final Color BG_COLOR_ERROR = new Color(255,224,224);
+
+    private JTextField tfLat;
+    private JTextField tfLon;
+    private boolean canceled = false;
+    private LatLon coordinates;
+    private OKAction actOK;
+    private CancelAction actCancel;
+
+    protected JPanel buildInputForm() {
+        JPanel pnl = new JPanel(new GridBagLayout());
+        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+        pnl.add(new JLabel("<html>"+
+                tr("Enter the coordinates for the new node.") +
+                "<br>" + tr("Use decimal degrees.") +
+                "<br>" + tr("Negative values denote Western/Southern hemisphere.")),
+                GBC.eol());
+
+        pnl.add(new JLabel(tr("Latitude")), GBC.std().insets(0,10,5,0));
+        tfLat = new JTextField(12);
+        pnl.add(tfLat, GBC.eol().insets(0,10,0,0));
+        pnl.add(new JLabel(tr("Longitude")), GBC.std().insets(0,0,5,10));
+        tfLon = new JTextField(12);
+        pnl.add(tfLon, GBC.eol().insets(0,0,0,10));
+
+        // parse and verify input on the fly
+        //
+        LatLonInputVerifier inputVerifier = new LatLonInputVerifier();
+        tfLat.getDocument().addDocumentListener(inputVerifier);
+        tfLon.getDocument().addDocumentListener(inputVerifier);
+
+        // select the text in the field on focus
+        //
+        TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
+        tfLat.addFocusListener(focusHandler);
+        tfLon.addFocusListener(focusHandler);
+        return pnl;
+    }
+
+    protected JPanel buildButtonRow() {
+        JPanel pnl = new JPanel(new FlowLayout());
+
+        SideButton btn;
+        pnl.add(btn = new SideButton(actOK = new OKAction()));
+        makeButtonRespondToEnter(btn);
+        pnl.add(btn = new SideButton(actCancel = new CancelAction()));
+        makeButtonRespondToEnter(btn);
+        pnl.add(new SideButton(new ContextSensitiveHelpAction(ht("/Action/AddNode"))));
+        return pnl;
+    }
+
+    protected void makeButtonRespondToEnter(SideButton btn) {
+        btn.setFocusable(true);
+        btn.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
+        btn.getActionMap().put("enter", btn.getAction());
+    }
+
+    protected void build() {
+        getContentPane().setLayout(new BorderLayout());
+        getContentPane().add(buildInputForm(), BorderLayout.CENTER);
+        getContentPane().add(buildButtonRow(), BorderLayout.SOUTH);
+        pack();
+
+        // make dialog respond to ESCAPE
+        //
+        getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "escape");
+        getRootPane().getActionMap().put("escape", actCancel);
+
+        // make dialog respond to F1
+        //
+        HelpUtil.setHelpContext(getRootPane(), ht("/Action/AddNode"));
+    }
+
+    public LatLonDialog(Component parent) {
+        super(JOptionPane.getFrameForComponent(parent), true /* modal */);
+        setTitle(tr("Add Node..."));
+        build();
+        addWindowListener(new WindowEventHandler());
+        setCoordinates(null);
+    }
+
+    public void setCoordinates(LatLon coordinates) {
+        if (coordinates == null) {
+            coordinates = new LatLon(0,0);
+        }
+        this.coordinates = coordinates;
+        tfLat.setText(coordinates.latToString(CoordinateFormat.DECIMAL_DEGREES));
+        tfLon.setText(coordinates.lonToString(CoordinateFormat.DECIMAL_DEGREES));
+        actOK.setEnabled(true);
+    }
+
+    public LatLon getCoordinates() {
+        return coordinates;
+    }
+
+    protected void setErrorFeedback(JTextField tf, String message) {
+        tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
+        tf.setToolTipText(message);
+        tf.setBackground(BG_COLOR_ERROR);
+    }
+
+    protected void clearErrorFeedback(JTextField tf, String message) {
+        tf.setBorder(UIManager.getBorder("TextField.border"));
+        tf.setToolTipText(message);
+        tf.setBackground(UIManager.getColor("TextField.background"));
+    }
+
+    protected Double parseDoubleFromUserInput(String input) {
+        if (input == null) return null;
+        // remove white space and an optional degree symbol
+        //
+        input = input.trim();
+        input = input.replaceAll("\u00B0", ""); // the degree symbol
+
+        // try to parse using the current locale
+        //
+        NumberFormat f = NumberFormat.getNumberInstance();
+        Number n=null;
+        ParsePosition pp = new ParsePosition(0);
+        n = f.parse(input,pp);
+        if (pp.getErrorIndex() >= 0 || pp.getIndex()<input.length()) {
+            // fall back - try to parse with the english locale
+            //
+            pp = new ParsePosition(0);
+            f = NumberFormat.getNumberInstance(Locale.ENGLISH);
+            n = f.parse(input, pp);
+            if (pp.getErrorIndex() >= 0 || pp.getIndex()<input.length())
+                return null;
+        }
+        return n== null ? null : n.doubleValue();
+    }
+
+    protected Double parseLatFromUserInput() {
+        Double d = parseDoubleFromUserInput(tfLat.getText());
+        if (d == null || ! LatLon.isValidLat(d)) {
+            setErrorFeedback(tfLat, tr("Please enter a valid latitude in the range -90..90"));
+            return null;
+        } else {
+            clearErrorFeedback(tfLat,tr("Please enter a latitude in the range -90..90"));
+        }
+        return d;
+    }
+
+    protected Double parseLonFromUserInput() {
+        Double d = parseDoubleFromUserInput(tfLon.getText());
+        if (d == null || ! LatLon.isValidLon(d)) {
+            setErrorFeedback(tfLon, tr("Please enter a valid longitude in the range -180..180"));
+            return null;
+        } else {
+            clearErrorFeedback(tfLon,tr("Please enter a longitude in the range -180..180"));
+        }
+        return d;
+    }
+
+    protected void parseUserInput() {
+        Double lat = parseLatFromUserInput();
+        Double lon = parseLonFromUserInput();
+        if (lat == null || lon == null) {
+            coordinates = null;
+            actOK.setEnabled(false);
+        } else {
+            coordinates = new LatLon(lat,lon);
+            actOK.setEnabled(true);
+        }
+    }
+
+    public boolean isCanceled() {
+        return canceled;
+    }
+
+    protected void setCanceled(boolean canceled) {
+        this.canceled = canceled;
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        if (visible) {
+            setCanceled(false);
+            WindowGeometry.centerInWindow(Main.parent, getSize()).applySafe(this);
+        }
+        super.setVisible(visible);
+    }
+
+    class OKAction extends AbstractAction {
+        public OKAction() {
+            putValue(NAME, tr("OK"));
+            putValue(SHORT_DESCRIPTION, tr("Close the dialog and create a new node"));
+            putValue(SMALL_ICON, ImageProvider.get("ok"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            setCanceled(false);
+            setVisible(false);
+        }
+    }
+
+    class CancelAction extends AbstractAction {
+        public CancelAction() {
+            putValue(NAME, tr("Cancel"));
+            putValue(SHORT_DESCRIPTION, tr("Close the dialog, do not create a new node"));
+            putValue(SMALL_ICON, ImageProvider.get("cancel"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            setCanceled(true);
+            setVisible(false);
+        }
+    }
+
+    class LatLonInputVerifier implements DocumentListener {
+        public void changedUpdate(DocumentEvent e) {
+            parseUserInput();
+        }
+
+        public void insertUpdate(DocumentEvent e) {
+            parseUserInput();
+        }
+
+        public void removeUpdate(DocumentEvent e) {
+            parseUserInput();
+        }
+    }
+
+    static class TextFieldFocusHandler implements FocusListener {
+        public void focusGained(FocusEvent e) {
+            Component c = e.getComponent();
+            if (c instanceof JTextField) {
+                JTextField tf = (JTextField)c;
+                tf.selectAll();
+            }
+        }
+        public void focusLost(FocusEvent e) {}
+    }
+
+    class WindowEventHandler extends WindowAdapter {
+        @Override
+        public void windowClosing(WindowEvent e) {
+            setCanceled(true);
+            setVisible(false);
+        }
+
+        @Override
+        public void windowOpened(WindowEvent e) {
+            tfLat.requestFocusInWindow();
+        }
+    }
+
+}
