diff --git a/images/cursor/hand.png b/images/cursor/hand.png
new file mode 100644
index 0000000..ecbdb5a
Binary files /dev/null and b/images/cursor/hand.png differ
diff --git a/images/cursor/modifier/add_node.png b/images/cursor/modifier/add_node.png
new file mode 100644
index 0000000..2d0dd3f
Binary files /dev/null and b/images/cursor/modifier/add_node.png differ
diff --git a/images/cursor/modifier/add_node_lock.png b/images/cursor/modifier/add_node_lock.png
new file mode 100644
index 0000000..807f8df
Binary files /dev/null and b/images/cursor/modifier/add_node_lock.png differ
diff --git a/images/cursor/modifier/lock.png b/images/cursor/modifier/lock.png
new file mode 100644
index 0000000..ab88b88
Binary files /dev/null and b/images/cursor/modifier/lock.png differ
diff --git a/images/cursor/modifier/mode.png b/images/cursor/modifier/mode.png
new file mode 100644
index 0000000..b018124
Binary files /dev/null and b/images/cursor/modifier/mode.png differ
diff --git a/images/mapmode/button.png b/images/mapmode/button.png
new file mode 100644
index 0000000..8169968
Binary files /dev/null and b/images/mapmode/button.png differ
diff --git a/src/org/openstreetmap/josm/actions/mapmode/IWAGeometry.java b/src/org/openstreetmap/josm/actions/mapmode/IWAGeometry.java
new file mode 100644
index 0000000..82fa0d7
--- /dev/null
+++ b/src/org/openstreetmap/josm/actions/mapmode/IWAGeometry.java
@@ -0,0 +1,79 @@
+// License: GPL. See LICENSE file for details.
+package org.openstreetmap.josm.actions.mapmode;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.tools.Geometry;
+
+/**
+ * This static class contains geometry functions used by the plugin.
+ *
+ * @author Alexander Kachkaev <alexander@kachkaev.ru>, 2011
+ */
+class IWAGeometry {
+
+    /**
+     * Returns angle of a segment defined with 2 point coordinates.
+     *
+     * @param p1
+     * @param p2
+     * @return Angle in radians (-pi, pi]
+     */
+    public static double getSegmentAngle(EastNorth p1, EastNorth p2) {
+        return Math.atan2(p2.north() - p1.north(), p2.east() - p1.east());
+    }
+
+    /**
+     * Returns angle of a corner defined with 3 point coordinates.
+     *
+     * @param p1
+     * @param p2 Common endpoint
+     * @param p3
+     * @return Angle in radians (-pi, pi]
+     */
+    public static double getCornerAngle(EastNorth p1, EastNorth p2, EastNorth p3) {
+        Double result = getSegmentAngle(p2, p1) - getSegmentAngle(p2, p3);
+        if (result <= -Math.PI) {
+            result += 2 * Math.PI;
+        }
+
+        if (result > Math.PI) {
+            result -= 2 * Math.PI;
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the coordinate of intersection of segment sp1-sp2 and an altitude
+     * to it starting at point ap. If the line defined with sp1-sp2 intersects
+     * its altitude out of sp1-sp2, null is returned.
+     *
+     * @param sp1
+     * @param sp2
+     * @param ap
+     * @return Intersection coordinate or null
+     */
+    public static EastNorth getSegmentAltituteIntersection(EastNorth sp1,
+            EastNorth sp2, EastNorth ap) {
+        Double segmentLenght = sp1.distance(sp2);
+        Double altitudeAngle = getSegmentAngle(sp1, sp2) + Math.PI / 2;
+
+        // Taking a random point on the altitude line (angle is known).
+        EastNorth ap2 = new EastNorth(ap.east() + 1000
+                * Math.cos(altitudeAngle), ap.north() + 1000
+                * Math.sin(altitudeAngle));
+
+        // Finding the intersection of two lines
+        EastNorth resultCandidate = Geometry.getLineLineIntersection(sp1, sp2,
+                ap, ap2);
+
+        // Filtering result
+        if (resultCandidate != null
+                && resultCandidate.distance(sp1) * .999 < segmentLenght
+                && resultCandidate.distance(sp2) * .999 < segmentLenght) {
+            return resultCandidate;
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/src/org/openstreetmap/josm/actions/mapmode/IWATargetWayHelper.java b/src/org/openstreetmap/josm/actions/mapmode/IWATargetWayHelper.java
new file mode 100644
index 0000000..981663d
--- /dev/null
+++ b/src/org/openstreetmap/josm/actions/mapmode/IWATargetWayHelper.java
@@ -0,0 +1,176 @@
+// License: GPL. See LICENSE file for details.
+package org.openstreetmap.josm.actions.mapmode;
+
+import java.awt.Point;
+import java.util.Collection;
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.WaySegment;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.tools.Geometry;
+import org.openstreetmap.josm.tools.Pair;
+
+/**
+ * This static class contains functions used to find target way, node to move or
+ * segment to divide.
+ *
+ * @author Alexander Kachkaev <alexander@kachkaev.ru>, 2011
+ */
+class IWATargetWayHelper {
+
+    /**
+     * Finds the way to work on. If the mouse is on the node, extracts one of
+     * the ways containing it. If the mouse is on the way, simply returns it.
+     *
+     * @param mv
+     * @param p
+     * @return Way or null in case there is nothing under the cursor.
+     */
+    public static Way findWay(MapView mv, Point p) {
+        if (mv == null || p == null) {
+            return null;
+        }
+
+        Node node = mv.getNearestNode(p, OsmPrimitive.isSelectablePredicate);
+        Way candidate = null;
+
+        if (node != null) {
+            final Collection<OsmPrimitive> candidates = node.getReferrers();
+            for (OsmPrimitive refferer : candidates) {
+                if (refferer instanceof Way) {
+                    candidate = (Way) refferer;
+                    break;
+                }
+            }
+            if (candidate != null) {
+                return candidate;
+            }
+        }
+
+        candidate = Main.map.mapView.getNearestWay(p,
+                OsmPrimitive.isSelectablePredicate);
+
+        return candidate;
+    }
+
+    /**
+     * Returns the nearest node to cursor. All nodes that are “behind” segments
+     * are neglected. This is to avoid way self-intersection after moving the
+     * candidateNode to a new place.
+     *
+     * @param mv
+     * @param w
+     * @param p
+     * @return
+     */
+    public static Node findCandidateNode(MapView mv, Way w, Point p) {
+        if (mv == null || w == null || p == null) {
+            return null;
+        }
+
+        EastNorth pEN = mv.getEastNorth(p.x, p.y);
+
+        Double bestDistance = Double.MAX_VALUE;
+        Double currentDistance;
+        List<Pair<Node, Node>> wpps = w.getNodePairs(false);
+
+        Node result = null;
+
+        mainLoop:
+        for (Node n : w.getNodes()) {
+            EastNorth nEN = n.getEastNorth();
+            currentDistance = pEN.distance(nEN);
+
+            if (currentDistance < bestDistance) {
+                // Making sure this candidate is not behind any segment.
+                for (Pair<Node, Node> wpp : wpps) {
+                    if (!wpp.a.equals(n)
+                            && !wpp.b.equals(n)
+                            && Geometry.getSegmentSegmentIntersection(
+                            wpp.a.getEastNorth(), wpp.b.getEastNorth(),
+                            pEN, nEN) != null) {
+                        continue mainLoop;
+                    }
+                }
+                result = n;
+                bestDistance = currentDistance;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the nearest way segment to cursor. The distance to segment ab is
+     * the length of altitude from p to ab (say, c) or the minimum distance from
+     * p to a or b if c is out of ab.
+     *
+     * The priority is given to segments where c is in ab. Otherwise, a segment
+     * with the largest angle apb is chosen.
+     *
+     * @param mv
+     * @param w
+     * @param p
+     * @return
+     */
+    public static WaySegment findCandidateSegment(MapView mv, Way w, Point p) {
+        if (mv == null || w == null || p == null) {
+            return null;
+        }
+
+        EastNorth pEN = mv.getEastNorth(p.x, p.y);
+
+        Double currentDistance;
+        Double currentAngle;
+        Double bestDistance = Double.MAX_VALUE;
+        Double bestAngle = 0.0;
+
+        int candidate = -1;
+
+        List<Pair<Node, Node>> wpps = w.getNodePairs(true);
+
+        int i = -1;
+        for (Pair<Node, Node> wpp : wpps) {
+            ++i;
+
+            // Finding intersection of the segment with its altitude from p (c)
+            EastNorth altitudeIntersection = IWAGeometry.getSegmentAltituteIntersection(wpp.a.getEastNorth(),
+                    wpp.b.getEastNorth(), pEN);
+
+            if (altitudeIntersection != null) {
+                // If the segment intersects with the altitude from p
+                currentDistance = pEN.distance(altitudeIntersection);
+
+                // Making an angle too big to let this candidate win any others
+                // having the same distance.
+                currentAngle = Double.MAX_VALUE;
+
+            } else {
+                // Otherwise: Distance is equal to the shortest distance from p
+                // to a or b
+                currentDistance = Math.min(pEN.distance(wpp.a.getEastNorth()),
+                        pEN.distance(wpp.b.getEastNorth()));
+
+                // Measuring the angle
+                currentAngle = Math.abs(IWAGeometry.getCornerAngle(
+                        wpp.a.getEastNorth(), pEN, wpp.b.getEastNorth()));
+            }
+
+            if (currentDistance < bestDistance
+                    || (currentAngle > bestAngle && currentDistance < bestDistance * 1.0001 /*
+                     * equality
+                     */)) {
+                candidate = i;
+                bestAngle = currentAngle;
+                bestDistance = currentDistance;
+            }
+
+        }
+        return candidate != -1 ? new WaySegment(w, candidate) : null;
+    }
+}
diff --git a/src/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyAction.java b/src/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyAction.java
new file mode 100644
index 0000000..0b1b823
--- /dev/null
+++ b/src/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyAction.java
@@ -0,0 +1,661 @@
+// License: GPL. See LICENSE file for details.
+package org.openstreetmap.josm.actions.mapmode;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.AWTEvent;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.event.AWTEventListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.geom.GeneralPath;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.command.MoveCommand;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.coor.EastNorth;
+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.Way;
+import org.openstreetmap.josm.data.osm.WaySegment;
+import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Pair;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * @author Alexander Kachkaev <alexander@kachkaev.ru>, 2011
+ */
+public class ImproveWayAccuracyAction extends MapMode implements MapViewPaintable,
+        SelectionChangedListener, AWTEventListener {
+
+    enum State {
+        selecting, improving
+    }
+
+    private State state;
+
+    private MapView mv;
+
+    private static final long serialVersionUID = 42L;
+
+    private Way targetWay;
+    private Node candidateNode = null;
+    private WaySegment candidateSegment = null;
+
+    private Point mousePos = null;
+    private boolean dragging = false;
+
+    final private Cursor cursorSelect;
+    final private Cursor cursorSelectHover;
+    final private Cursor cursorImprove;
+    final private Cursor cursorImproveAdd;
+    final private Cursor cursorImproveDelete;
+    final private Cursor cursorImproveAddLock;
+    final private Cursor cursorImproveLock;
+
+    private boolean shift = false;
+    private boolean ctrl = false;
+    private boolean alt = false;
+
+    private final Color guideColor;
+    private final BasicStroke selectTargetWayStroke;
+    private final BasicStroke moveNodeStroke;
+    private final BasicStroke addNodeStroke;
+    private final BasicStroke deleteNodeStroke;
+
+    private boolean selectionChangedBlocked = false;
+
+    protected String oldModeHelpText;
+
+    public ImproveWayAccuracyAction(MapFrame mapFrame) {
+        super(tr("Improve Way Accuracy"), "button.png",
+                tr("Improve Way Accuracy mode"), Shortcut.registerShortcut(
+                "mapmode:ImproveWayAccuracy",
+                tr("Mode: {0}", tr("Improve Way Accuracy")),
+                KeyEvent.VK_K, Shortcut.GROUP_EDIT), mapFrame, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+
+        cursorSelect = ImageProvider.getCursor("normal", "mode");
+        cursorSelectHover = ImageProvider.getCursor("hand", "mode");
+        cursorImprove = ImageProvider.getCursor("crosshair", null);
+        cursorImproveAdd = ImageProvider.getCursor("crosshair", "addnode");
+        cursorImproveDelete = ImageProvider.getCursor("crosshair", "delete_node");
+        cursorImproveAddLock = ImageProvider.getCursor("crosshair",
+                "add_node_lock");
+        cursorImproveLock = ImageProvider.getCursor("crosshair", "lock");
+
+        guideColor = PaintColors.HIGHLIGHT.get();
+        selectTargetWayStroke = new BasicStroke(2, BasicStroke.CAP_ROUND,
+                BasicStroke.JOIN_ROUND);
+        float dash1[] = {4.0f};
+        moveNodeStroke = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,
+                BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
+        addNodeStroke = new BasicStroke(1, BasicStroke.CAP_BUTT,
+                BasicStroke.JOIN_MITER);
+        deleteNodeStroke = new BasicStroke(1, BasicStroke.CAP_BUTT,
+                BasicStroke.JOIN_MITER);
+    }
+
+    // -------------------------------------------------------------------------
+    // Mode methods
+    // -------------------------------------------------------------------------
+    @Override
+    public void enterMode() {
+        if (!isEnabled()) {
+            return;
+        }
+        super.enterMode();
+
+        mv = Main.map.mapView;
+        mousePos = null;
+        oldModeHelpText = "";
+
+        if (getCurrentDataSet() == null) {
+            return;
+        }
+
+        updateStateByCurrentSelection();
+
+        Main.map.mapView.addMouseListener(this);
+        Main.map.mapView.addMouseMotionListener(this);
+        Main.map.mapView.addTemporaryLayer(this);
+        DataSet.addSelectionListener(this);
+
+        try {
+            Toolkit.getDefaultToolkit().addAWTEventListener(this,
+                    AWTEvent.KEY_EVENT_MASK);
+        } catch (SecurityException ex) {
+        }
+    }
+
+    @Override
+    public void exitMode() {
+        super.exitMode();
+
+        Main.map.mapView.removeMouseListener(this);
+        Main.map.mapView.removeMouseMotionListener(this);
+        Main.map.mapView.removeTemporaryLayer(this);
+        DataSet.removeSelectionListener(this);
+
+        try {
+            Toolkit.getDefaultToolkit().removeAWTEventListener(this);
+        } catch (SecurityException ex) {
+        }
+
+        Main.map.mapView.repaint();
+    }
+
+    @Override
+    protected void updateStatusLine() {
+        String newModeHelpText = getModeHelpText();
+        if (!newModeHelpText.equals(oldModeHelpText)) {
+            oldModeHelpText = newModeHelpText;
+            Main.map.statusLine.setHelpText(newModeHelpText);
+            Main.map.statusLine.repaint();
+        }
+    }
+
+    @Override
+    public String getModeHelpText() {
+        if (state == State.selecting) {
+            if (targetWay != null) {
+                return tr("Click on the way to start improving its shape.");
+            } else {
+                return tr("Select a way that you want to make more accurate.");
+            }
+        } else {
+            if (ctrl) {
+                return tr("Click to add a new node. Release Ctrl to move existing nodes or hold Alt to delete.");
+            } else if (alt) {
+                return tr("Click to delete the highlighted node. Release Alt to move existing nodes or hold Ctrl to add new nodes.");
+            } else {
+                return tr("Click to move the highlighted node. Hold Ctrl to add new nodes, or Alt to delete.");
+            }
+        }
+    }
+
+    @Override
+    public boolean layerIsSupported(Layer l) {
+        return l instanceof OsmDataLayer;
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(getEditLayer() != null);
+        // setEnabled(Main.main.getActiveLayer() instanceof OsmDataLayer);
+    }
+
+    // -------------------------------------------------------------------------
+    // MapViewPaintable methods
+    // -------------------------------------------------------------------------
+    /**
+     * Redraws temporary layer. Highlights targetWay in select mode. Draws
+     * preview lines in improve mode and highlights the candidateNode
+     */
+    @Override
+    public void paint(Graphics2D g, MapView mv, Bounds bbox) {
+        if (mousePos == null) {
+            return;
+        }
+
+        g.setColor(guideColor);
+
+        if (state == State.selecting && targetWay != null) {
+            // Highlighting the targetWay in Selecting state
+            // Non-native highlighting is used, because sometimes highlighted
+            // segments are covered with others, which is bad.
+            g.setStroke(selectTargetWayStroke);
+
+            List<Node> nodes = targetWay.getNodes();
+
+            GeneralPath b = new GeneralPath();
+            Point p0 = mv.getPoint(nodes.get(0));
+            Point pn;
+            b.moveTo(p0.x, p0.y);
+
+            for (Node n : nodes) {
+                pn = mv.getPoint(n);
+                b.lineTo(pn.x, pn.y);
+            }
+            if (targetWay.isClosed()) {
+                b.lineTo(p0.x, p0.y);
+            }
+
+            g.draw(b);
+
+        } else if (state == State.improving) {
+            // Drawing preview lines and highlighting the node
+            // that is going to be moved.
+            // Non-native highlighting is used here as well.
+
+            // Finding endpoints
+            Point p1 = null, p2 = null;
+            if (ctrl && candidateSegment != null) {
+                g.setStroke(addNodeStroke);
+                p1 = mv.getPoint(candidateSegment.getFirstNode());
+                p2 = mv.getPoint(candidateSegment.getSecondNode());
+            } else if (!alt && !ctrl && candidateNode != null) {
+                g.setStroke(moveNodeStroke);
+                List<Pair<Node, Node>> wpps = targetWay.getNodePairs(false);
+                for (Pair<Node, Node> wpp : wpps) {
+                    if (wpp.a == candidateNode) {
+                        p1 = mv.getPoint(wpp.b);
+                    }
+                    if (wpp.b == candidateNode) {
+                        p2 = mv.getPoint(wpp.a);
+                    }
+                    if (p1 != null && p2 != null) {
+                        break;
+                    }
+                }
+            } else if (alt && !ctrl && candidateNode != null) {
+                g.setStroke(deleteNodeStroke);
+                List<Node> nodes = targetWay.getNodes();
+                int index = nodes.indexOf(candidateNode);
+
+                // Only draw line if node is not first and/or last
+                if (index != 0 && index != (nodes.size() - 1)) {
+                    p1 = mv.getPoint(nodes.get(index - 1));
+                    p2 = mv.getPoint(nodes.get(index + 1));
+                }
+                // TODO: indicate what part that will be deleted? (for end nodes)
+            }
+
+
+            // Drawing preview lines
+            GeneralPath b = new GeneralPath();
+            if (alt && !ctrl) {
+                // In delete mode
+                if (p1 != null && p2 != null) {
+                    b.moveTo(p1.x, p1.y);
+                    b.lineTo(p2.x, p2.y);
+                }
+            } else {
+                // In add or move mode
+                if (p1 != null) {
+                    b.moveTo(mousePos.x, mousePos.y);
+                    b.lineTo(p1.x, p1.y);
+                }
+                if (p2 != null) {
+                    b.moveTo(mousePos.x, mousePos.y);
+                    b.lineTo(p2.x, p2.y);
+                }
+            }
+            g.draw(b);
+
+            // Highlighting candidateNode
+            if (candidateNode != null) {
+                p1 = mv.getPoint(candidateNode);
+                g.fillRect(p1.x - 2, p1.y - 2, 6, 6);
+            }
+
+        }
+    }
+
+    // -------------------------------------------------------------------------
+    // Event handlers
+    // -------------------------------------------------------------------------
+    @Override
+    public void eventDispatched(AWTEvent event) {
+        if (Main.map == null || Main.map.mapView == null
+                || !Main.map.mapView.isActiveLayerDrawable()) {
+            return;
+        }
+        updateKeyModifiers((InputEvent) event);
+        updateCursorDependentObjectsIfNeeded();
+        updateCursor();
+        updateStatusLine();
+        Main.map.mapView.repaint();
+    }
+
+    @Override
+    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+        if (selectionChangedBlocked) {
+            return;
+        }
+        updateStateByCurrentSelection();
+    }
+
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        dragging = true;
+        mouseMoved(e);
+    }
+
+    @Override
+    public void mouseMoved(MouseEvent e) {
+        if (!isEnabled()) {
+            return;
+        }
+
+        mousePos = e.getPoint();
+
+        updateKeyModifiers(e);
+        updateCursorDependentObjectsIfNeeded();
+        updateCursor();
+        updateStatusLine();
+        Main.map.mapView.repaint();
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        dragging = false;
+        if (!isEnabled() || e.getButton() != MouseEvent.BUTTON1) {
+            return;
+        }
+
+        updateKeyModifiers(e);
+        mousePos = e.getPoint();
+
+        if (state == State.selecting) {
+            if (targetWay != null) {
+                getCurrentDataSet().setSelected(targetWay.getPrimitiveId());
+                updateStateByCurrentSelection();
+            }
+        } else if (state == State.improving && mousePos != null) {
+            // Checking if the new coordinate is outside of the world
+            if (mv.getLatLon(mousePos.x, mousePos.y).isOutSideWorld()) {
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("Cannot place a node outside of the world."),
+                        tr("Warning"), JOptionPane.WARNING_MESSAGE);
+                return;
+            }
+
+            if (ctrl && !alt && candidateSegment != null) {
+                // Adding a new node to the highlighted segment
+                // Important: If there are other ways containing the same
+                // segment, a node must added to all of that ways.
+                Collection<Command> virtualCmds = new LinkedList<Command>();
+
+                // Creating a new node
+                Node virtualNode = new Node(mv.getEastNorth(mousePos.x,
+                        mousePos.y));
+                virtualCmds.add(new AddCommand(virtualNode));
+
+                // Looking for candidateSegment copies in ways that are
+                // referenced
+                // by candidateSegment nodes
+                List<Way> firstNodeWays = OsmPrimitive.getFilteredList(
+                        candidateSegment.getFirstNode().getReferrers(),
+                        Way.class);
+                List<Way> secondNodeWays = OsmPrimitive.getFilteredList(
+                        candidateSegment.getFirstNode().getReferrers(),
+                        Way.class);
+
+                Collection<WaySegment> virtualSegments = new LinkedList<WaySegment>();
+                for (Way w : firstNodeWays) {
+                    List<Pair<Node, Node>> wpps = w.getNodePairs(true);
+                    for (Way w2 : secondNodeWays) {
+                        if (!w.equals(w2)) {
+                            continue;
+                        }
+                        // A way is referenced in both nodes.
+                        // Checking if there is such segment
+                        int i = -1;
+                        for (Pair<Node, Node> wpp : wpps) {
+                            ++i;
+                            if ((wpp.a.equals(candidateSegment.getFirstNode())
+                                    && wpp.b.equals(candidateSegment.getSecondNode()) || (wpp.b.equals(candidateSegment.getFirstNode()) && wpp.a.equals(candidateSegment.getSecondNode())))) {
+                                virtualSegments.add(new WaySegment(w, i));
+                            }
+                        }
+                    }
+                }
+
+                // Adding the node to all segments found
+                for (WaySegment virtualSegment : virtualSegments) {
+                    Way w = virtualSegment.way;
+                    Way wnew = new Way(w);
+                    wnew.addNode(virtualSegment.lowerIndex + 1, virtualNode);
+                    virtualCmds.add(new ChangeCommand(w, wnew));
+                }
+
+                // Finishing the sequence command
+                String text = trn("Add and a new node to way",
+                        "Add and a new node to {0} ways",
+                        virtualSegments.size(), virtualSegments.size());
+
+                Main.main.undoRedo.add(new SequenceCommand(text, virtualCmds));
+
+            } else if (alt && !ctrl && candidateNode != null) {
+                // Deleting the highlighted node
+
+                //check to see if node has interesting keys
+                Iterator<String> keyIterator = candidateNode.getKeys().keySet().iterator();
+                boolean hasTags = false;
+                while (keyIterator.hasNext()) {
+                    String key = keyIterator.next();
+                    if (!OsmPrimitive.isUninterestingKey(key)) {
+                        hasTags = true;
+                        break;
+                    }
+                }
+
+                //check to see if node is in use by more than one object
+                List<OsmPrimitive> referrers = candidateNode.getReferrers();
+                List<Way> ways = OsmPrimitive.getFilteredList(referrers, Way.class);
+                if (referrers.size() != 1 || ways.size() != 1) {
+                    JOptionPane.showMessageDialog(Main.parent,
+                            tr("Cannot delete node that is referenced by multiple objects"),
+                            tr("Error"), JOptionPane.ERROR_MESSAGE);
+                } else if (hasTags) {
+                    JOptionPane.showMessageDialog(Main.parent,
+                            tr("Cannot delete node that has tags"),
+                            tr("Error"), JOptionPane.ERROR_MESSAGE);
+                } else {
+                    List<Node> nodeList = new ArrayList<Node>();
+                    nodeList.add(candidateNode);
+                    Command deleteCmd = DeleteCommand.delete(getEditLayer(), nodeList, true);
+                    Main.main.undoRedo.add(deleteCmd);
+                }
+
+
+            } else if (candidateNode != null) {
+                // Moving the highlighted node
+                EastNorth nodeEN = candidateNode.getEastNorth();
+                EastNorth cursorEN = mv.getEastNorth(mousePos.x, mousePos.y);
+
+                Main.main.undoRedo.add(new MoveCommand(candidateNode, cursorEN.east() - nodeEN.east(), cursorEN.north()
+                        - nodeEN.north()));
+            }
+        }
+
+        mousePos = null;
+        updateCursor();
+        updateStatusLine();
+        Main.map.mapView.repaint();
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+        if (!isEnabled()) {
+            return;
+        }
+
+        if (!dragging) {
+            mousePos = null;
+        }
+        Main.map.mapView.repaint();
+    }
+
+    // -------------------------------------------------------------------------
+    // Custom methods
+    // -------------------------------------------------------------------------
+    /**
+     * Updates shift and ctrl key states
+     */
+    @Override
+    protected void updateKeyModifiers(InputEvent e) {
+        ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+        shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
+        // accept either Alt key (including AltGr)
+        alt = ((e.getModifiers() & (ActionEvent.ALT_MASK | InputEvent.ALT_GRAPH_MASK)) != 0);
+    }
+
+    /**
+     * Sets new cursor depending on state, mouse position
+     */
+    private void updateCursor() {
+        if (!isEnabled()) {
+            mv.setNewCursor(null, this);
+            return;
+        }
+
+        if (state == State.selecting) {
+            mv.setNewCursor(targetWay == null ? cursorSelect
+                    : cursorSelectHover, this);
+        } else if (state == State.improving) {
+            if (alt && !ctrl) {
+                mv.setNewCursor(cursorImproveDelete, this);
+            } else if (shift || dragging) {
+                if (ctrl) {
+                    mv.setNewCursor(cursorImproveAddLock, this);
+                } else {
+                    mv.setNewCursor(cursorImproveLock, this);
+                }
+            } else if (ctrl && !alt) {
+                mv.setNewCursor(cursorImproveAdd, this);
+            } else {
+                mv.setNewCursor(cursorImprove, this);
+            }
+        }
+    }
+
+    /**
+     * Updates these objects under cursor: targetWay, candidateNode,
+     * candidateSegment
+     */
+    public void updateCursorDependentObjectsIfNeeded() {
+        if (state == State.improving && (shift || dragging)
+                && !(candidateNode == null && candidateSegment == null)) {
+            return;
+        }
+
+        if (mousePos == null) {
+            candidateNode = null;
+            candidateSegment = null;
+            return;
+        }
+
+        if (state == State.selecting) {
+            targetWay = IWATargetWayHelper.findWay(mv, mousePos);
+        } else if (state == State.improving) {
+            if (ctrl && !alt) {
+                candidateSegment = IWATargetWayHelper.findCandidateSegment(mv,
+                        targetWay, mousePos);
+                candidateNode = null;
+            } else {
+                candidateNode = IWATargetWayHelper.findCandidateNode(mv,
+                        targetWay, mousePos);
+                candidateSegment = null;
+            }
+        }
+    }
+
+    /**
+     * Switches to Selecting state
+     */
+    public void startSelecting() {
+        state = State.selecting;
+
+        targetWay = null;
+        if (getCurrentDataSet() != null) {
+            getCurrentDataSet().clearSelection();
+        }
+
+        mv.repaint();
+        updateStatusLine();
+    }
+
+    /**
+     * Switches to Improving state
+     *
+     * @param targetWay Way that is going to be improved
+     */
+    public void startImproving(Way targetWay) {
+        state = State.improving;
+
+        List<OsmPrimitive> currentSelection = (List<OsmPrimitive>) getCurrentDataSet().getSelected();
+        if (currentSelection.size() != 1
+                || !currentSelection.get(0).equals(targetWay)) {
+            selectionChangedBlocked = true;
+            getCurrentDataSet().clearSelection();
+            getCurrentDataSet().setSelected(targetWay.getPrimitiveId());
+            selectionChangedBlocked = false;
+        }
+
+        this.targetWay = targetWay;
+        this.candidateNode = null;
+        this.candidateSegment = null;
+
+        mv.repaint();
+        updateStatusLine();
+    }
+
+    /**
+     * Updates the state according to the current selection. Goes to Improve
+     * state if a single way or node is selected. Extracts a way by a node in
+     * the second case.
+     *
+     */
+    private void updateStateByCurrentSelection() {
+        final ArrayList<Node> nodeList = new ArrayList<Node>();
+        final ArrayList<Way> wayList = new ArrayList<Way>();
+        final Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected();
+
+        // Collecting nodes and ways from the selection
+        for (OsmPrimitive p : sel) {
+            if (p instanceof Way) {
+                wayList.add((Way) p);
+            }
+            if (p instanceof Node) {
+                nodeList.add((Node) p);
+            }
+        }
+
+        if (wayList.size() == 1) {
+            // Starting improving the single selected way
+            startImproving(wayList.get(0));
+            return;
+        } else if (nodeList.size() > 0) {
+            // Starting improving the only way of the single selected node
+            if (nodeList.size() == 1) {
+                List<OsmPrimitive> r = nodeList.get(0).getReferrers();
+                if (r.size() == 1 && (r.get(0) instanceof Way)) {
+                    startImproving((Way) r.get(0));
+                    return;
+                }
+            }
+        }
+
+        // Starting selecting by default
+        startSelecting();
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
index a0de622..6121eea 100644
--- a/src/org/openstreetmap/josm/gui/MapFrame.java
+++ b/src/org/openstreetmap/josm/gui/MapFrame.java
@@ -41,6 +41,7 @@ import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.mapmode.DeleteAction;
 import org.openstreetmap.josm.actions.mapmode.DrawAction;
 import org.openstreetmap.josm.actions.mapmode.ExtrudeAction;
+import org.openstreetmap.josm.actions.mapmode.ImproveWayAccuracyAction;
 import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.actions.mapmode.ParallelWayAction;
 import org.openstreetmap.josm.actions.mapmode.SelectAction;
@@ -147,6 +148,7 @@ public class MapFrame extends JPanel implements Destroyable, LayerChangeListener
         addMapMode(new IconToggleButton(new DeleteAction(this), !Main.pref.getBoolean("expert", false)));
         addMapMode(new IconToggleButton(new ExtrudeAction(this), !Main.pref.getBoolean("expert", false)));
         addMapMode(new IconToggleButton(new ParallelWayAction(this), !Main.pref.getBoolean("expert", false)));
+        addMapMode(new IconToggleButton(new ImproveWayAccuracyAction(Main.map), !Main.pref.getBoolean("expert", false)));
 
         toolGroup.setSelected(((AbstractButton)toolBarActions.getComponent(0)).getModel(), true);
 
diff --git a/src/org/openstreetmap/josm/plugins/PluginHandler.java b/src/org/openstreetmap/josm/plugins/PluginHandler.java
index 342979e..da3b5d7 100644
--- a/src/org/openstreetmap/josm/plugins/PluginHandler.java
+++ b/src/org/openstreetmap/josm/plugins/PluginHandler.java
@@ -105,6 +105,7 @@ public class PluginHandler {
             new DeprecatedPlugin("wmsplugin", IN_CORE),
             new DeprecatedPlugin("ParallelWay", IN_CORE),
             new DeprecatedPlugin("dumbutils", tr("replaced by new {0} plugin","utilsplugin2")),
+            new DeprecatedPlugin("ImproveWayAccuracy", IN_CORE),
         });
     }
 
