Index: /applications/editors/josm/plugins/alignways/.project
===================================================================
--- /applications/editors/josm/plugins/alignways/.project	(revision 34488)
+++ /applications/editors/josm/plugins/alignways/.project	(revision 34489)
@@ -17,7 +17,13 @@
 			</arguments>
 		</buildCommand>
+		<buildCommand>
+			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
 	</buildSpec>
 	<natures>
 		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
 	</natures>
 </projectDescription>
Index: /applications/editors/josm/plugins/alignways/.settings/org.eclipse.jdt.core.prefs
===================================================================
--- /applications/editors/josm/plugins/alignways/.settings/org.eclipse.jdt.core.prefs	(revision 34488)
+++ /applications/editors/josm/plugins/alignways/.settings/org.eclipse.jdt.core.prefs	(revision 34489)
@@ -13,4 +13,5 @@
 org.eclipse.jdt.core.compiler.debug.localVariable=generate
 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
 org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
@@ -36,4 +37,9 @@
 org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
 org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
 org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
 org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
@@ -42,4 +48,12 @@
 org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
 org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
Index: /applications/editors/josm/plugins/alignways/build.xml
===================================================================
--- /applications/editors/josm/plugins/alignways/build.xml	(revision 34488)
+++ /applications/editors/josm/plugins/alignways/build.xml	(revision 34489)
@@ -10,5 +10,5 @@
     -->
     <property name="plugin.author" value="Attila Szász"/>
-    <property name="plugin.class" value="com.tilusnet.josm.plugins.alignways.AlignWaysPlugin"/>
+    <property name="plugin.class" value="org.openstreetmap.josm.plugins.alignways.AlignWaysPlugin"/>
     <property name="plugin.description" value="Makes a pair of selected way segments parallel by rotating one of them around a chosen pivot."/>
     <property name="plugin.icon" value="images/alignways.png"/>
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysAction.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysAction.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysAction.java	(revision 34489)
@@ -0,0 +1,76 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.UndoRedoHandler;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.plugins.alignways.AlignWaysDialog.AligningModeOption;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Makes a pair of selected way segments parallel by rotating one of them around a chosen pivot.
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ */
+public class AlignWaysAction extends JosmAction {
+
+    private static final long serialVersionUID = -1540319652562985458L;
+
+    public AlignWaysAction() {
+        super(
+                tr("Align Way Segments"),
+                "alignways",
+                tr("Makes a pair of selected way segments parallel by rotating one of them "
+                        + "around a chosen pivot."),
+                Shortcut.registerShortcut("tools:alignways", tr("Tool: {0}", tr("Align Ways")),
+                                KeyEvent.VK_SPACE, Shortcut.SHIFT),
+                true);
+        setEnabled(false);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        if (!isEnabled())
+            return;
+        DataSet ds = getLayerManager().getEditDataSet();
+        if (ds == null)
+            return;
+
+        Collection<Node> affectableNodes = AlignWaysSegmentMgr.getInstance(
+                MainApplication.getMap().mapView).getSelectedNodes();
+
+        // c is the last command launched, if any
+        Command c = !UndoRedoHandler.getInstance().commands.isEmpty() ? UndoRedoHandler.getInstance().commands
+                .getLast() : null;
+
+                // Potentially add my type of command only if last command wasn't my type
+                // or, if it was, the rotated nodes were not the same as now
+                if (!(c instanceof AlignWaysCmdKeepLength && affectableNodes
+                        .equals(((AlignWaysCmdKeepLength) c).getPrevAffectedNodes()))) {
+
+                    AlignWaysCmdKeepLength cmdAW;
+                    if (AlignWaysPlugin.getAwDialog().getAwOpt() == AligningModeOption.ALGN_OPT_KEEP_ANGLE) {
+                        cmdAW = new AlignWaysCmdKeepAngles(ds);
+                    } else {
+                        cmdAW = new AlignWaysCmdKeepLength(ds);
+                    }
+
+                    if (cmdAW.executable()) {
+                        // This will also trigger AlignWaysCmdKeepLength.executeCommand()
+                        UndoRedoHandler.getInstance().add(cmdAW);
+                    }
+                }
+
+                MainApplication.getMap().mapView.repaint();
+
+                return;
+    }
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysAlgnSegment.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysAlgnSegment.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysAlgnSegment.java	(revision 34489)
@@ -0,0 +1,293 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.Bounds;
+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.WaySegment;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ * The segment to be aligned to the reference segment. Actions it can do:
+ *         - remember its selected pivot point
+ *         - keeps its potential pivot point list up to date
+ *         - rotate itself
+ *         - paint itself and its selected pivot point
+ *
+ */
+public class AlignWaysAlgnSegment extends AlignWaysSegment {
+
+    private enum PivotLocations {
+        NONE, NODE1, NODE2, CENTRE
+    }
+
+    private PivotLocations currPivot;
+    Map<PivotLocations, EastNorth> pivotList = new EnumMap<>(
+            PivotLocations.class);
+    private final Color pivotColor = Color.YELLOW;
+    private final Color crossColor = pivotColor;
+    private final Map<Node, ArrayList<WaySegment>> adjWaySegs = new HashMap<>();
+
+    public AlignWaysAlgnSegment(MapView mapview, Point p)
+            throws IllegalArgumentException {
+        super(mapview, p);
+        setSegment(getNearestWaySegment(p));
+        segmentColor = Color.ORANGE;
+    }
+
+    /**
+     * Sets segment and initialises its pivot list and activates the centre
+     * rotation pivot.
+     */
+    @Override
+    public void setSegment(WaySegment segment) {
+        super.setSegment(segment);
+        setPivots();
+    }
+
+    @Override
+    void setSegmentEndpoints(WaySegment segment) {
+        super.setSegmentEndpoints(segment);
+
+        // Update the list of adjacent waysegments to the endpoints
+        for (Node nA : getSegmentEndPoints()) {
+            adjWaySegs.put(nA, new ArrayList<>(determineAdjacentWaysegments(nA)));
+        }
+    }
+
+    /**
+     * Useful when segments moves (or e.g. rotates) on the map. Updates the end
+     * segment points and the pivot coordinates without changing the current
+     * pivot.
+     */
+    public void updatePivotsEndpoints() {
+        setPivots(currPivot);
+        setSegmentEndpoints(segment);
+    }
+
+    /**
+     * Updates the segment's pivot list and sets the rotation pivot to centre.
+     * @param pivotRef pivot reference
+     */
+    private void setPivots(PivotLocations pivotRef) {
+        if (segment != null) {
+            for (PivotLocations pl : PivotLocations.values()) {
+                pivotList.put(pl, getPivotCoord(pl));
+            }
+            setPivotReference(pivotRef);
+        } else {
+            setPivotReference(PivotLocations.NONE);
+        }
+    }
+
+    private void setPivots() {
+        setPivots(PivotLocations.CENTRE);
+    }
+
+    private void setPivotReference(PivotLocations pp) {
+        currPivot = pp;
+    }
+
+    /**
+     * Returns the EastNorth of the specified pivot point pp. It always returns
+     * up-to-date data from dataset. Assumes segment is not null.
+     *
+     * @param pp
+     *            The pivot location
+     * @return east/north coordinates of pivot location
+     */
+    private EastNorth getPivotCoord(PivotLocations pp) {
+        try {
+            EastNorth n1;
+            EastNorth n2;
+            switch (pp) {
+            case NODE1:
+                return segment.getFirstNode().getEastNorth();
+            case NODE2:
+                return segment.getSecondNode().getEastNorth();
+            case CENTRE:
+                n1 = getPivotCoord(PivotLocations.NODE1);
+                n2 = getPivotCoord(PivotLocations.NODE2);
+                return n1 != null && n2 != null ? n1.getCenter(n2) : null;
+            case NONE:
+            default:
+                return null;
+            }
+        } catch (IndexOutOfBoundsException e) {
+            Logging.error(e);
+            return null;
+        }
+    }
+
+    /**
+     * @return The EastNorth of the currently selected pivot.
+     */
+    public EastNorth getCurrPivotCoord() {
+        if (segment != null)
+            return getPivotCoord(currPivot);
+        return null;
+    }
+
+    /**
+     * @param clickedPoint
+     *            Pivot may be updated in the vicinity of this point
+     * @return true if a pivot is within reach on the segment, false otherwise
+     */
+    public boolean updatePivot(Point clickedPoint) {
+        // tHQ Done.
+        PivotLocations tmpPivot = findNearbyPivot(clickedPoint);
+        if (tmpPivot != PivotLocations.NONE) {
+            setPivotReference(tmpPivot);
+            return true;
+        } else
+            return false;
+    }
+
+    private PivotLocations findNearbyPivot(Point clickedPoint) {
+        PivotLocations nearest = PivotLocations.NONE;
+        int snapDistance = NavigatableComponent.PROP_SNAP_DISTANCE.get();
+
+        // If no alignee selected yet, there's no point to carry on
+        if (segment == null)
+            return PivotLocations.NONE;
+
+        for (PivotLocations pl : PivotLocations.values()) {
+            if (pl.equals(PivotLocations.NONE)) {
+                continue;
+            }
+            if (mapview.getPoint(pivotList.get(pl)).distance(clickedPoint) <= snapDistance) {
+                nearest = pl;
+                break;
+            }
+        }
+        return nearest;
+    }
+
+    /**
+     * Given a Node (usually an endpoint), it will return a collection of way segments that are adjacently
+     * connected to it. The current alignee waysegment is not added to the collection.
+     *
+     * @param node The Node (endpoint) to analyse.
+     * @return The collection of the adjacent waysegments.
+     */
+    private Collection<WaySegment> determineAdjacentWaysegments(Node node) {
+        Collection<WaySegment> wsSet = new HashSet<>();
+        final double radius = 10.0;
+        final int stepsOnCircle = 24;
+        final double incrementOnCircle = 2 * Math.PI / stepsOnCircle;
+
+        Point p = MainApplication.getMap().mapView.getPoint(node);
+        for (int i = 0; i < stepsOnCircle; i++) {
+            double ang = i * incrementOnCircle;
+            double x = p.getX() + (Math.cos(ang) * radius);
+            double y = p.getY() + (Math.sin(ang) * radius);
+            Point pnew = new Point();
+            pnew.setLocation(x, y);
+            WaySegment ws = MainApplication.getMap().mapView.getNearestWaySegment(pnew, OsmPrimitive::isUsable);
+            if (ws != null && !ws.equals(this.segment) &&
+                    (ws.getFirstNode().equals(node) || ws.getSecondNode().equals(node))) {
+                // We won't want to add a:
+                // - 'no match' (=null)
+                // - segment that is not connected to the alignee endpoint
+                wsSet.add(ws);
+            }
+        }
+
+        return wsSet;
+    }
+
+    /**
+     * Returns the collection of adjacent way segments to Node node.
+     * The node is normally a valid endpoint of the segment.
+     * If it isn't, null may be returned.
+     *
+     * @param node The (endpoint) node.
+     * @return Collection of the adjacent way segments.
+     */
+    public ArrayList<WaySegment> getAdjacentWaySegments(Node node) {
+        return adjWaySegs.get(node);
+    }
+
+    @Override
+    public void paint(Graphics2D g, MapView mv, Bounds bbox) {
+        super.paint(g, mv, bbox);
+
+        // Note: segment should never be null here, and its nodes should never be missing.
+        // If they are, it's a bug, possibly related to tracking of DataSet deletions.
+        if (segment.way.getNodesCount() <= segment.lowerIndex + 1) {
+            Logging.warn("Not drawing AlignWays pivot points: underlying nodes disappeared");
+            return;
+        }
+
+        // Ensure consistency
+        updatePivotsEndpoints();
+
+        // Highlight potential pivot points
+        for (PivotLocations pl : PivotLocations.values()) {
+            if (pl != PivotLocations.NONE) {
+                highlightCross(g, mv, pivotList.get(pl));
+            }
+        }
+
+        // Highlight active pivot
+        highlightPivot(g, mv, getPivotCoord(currPivot));
+    }
+
+    private void highlightPivot(Graphics2D g, MapView mv, EastNorth pivot) {
+        g.setColor(pivotColor);
+        g.setStroke(new BasicStroke());
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+        Shape pvCentrePoint = new Ellipse2D.Double(
+                mv.getPoint(pivot).getX() - 5.0f,
+                mv.getPoint(pivot).getY() - 5.0f, 10.0f, 10.0f);
+        g.fill(pvCentrePoint);
+        Shape pvPoint = new Ellipse2D.Double(mv.getPoint(pivot).getX() - 8.0f,
+                mv.getPoint(pivot).getY() - 8.0f, 16.0f, 16.0f);
+
+        g.draw(pvCentrePoint);
+        g.draw(pvPoint);
+    }
+
+    private void highlightCross(Graphics2D g, MapView mv, EastNorth en) {
+
+        double crossX = mv.getPoint(en).getX();
+        double crossY = mv.getPoint(en).getY();
+        double crossSize = 10.0;
+
+        Line2D crossV = new Line2D.Double(crossX, crossY - crossSize, crossX,
+                crossY + crossSize);
+        Line2D crossH = new Line2D.Double(crossX - crossSize, crossY, crossX
+                + crossSize, crossY);
+
+        g.setColor(crossColor);
+        g.setStroke(new BasicStroke());
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+        g.draw(crossV);
+        g.draw(crossH);
+
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysCmdKeepAngles.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysCmdKeepAngles.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysCmdKeepAngles.java	(revision 34489)
@@ -0,0 +1,242 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import javax.swing.JOptionPane;
+
+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.WaySegment;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.plugins.alignways.geometry.AlignWaysGeomLine;
+import org.openstreetmap.josm.plugins.alignways.geometry.AlignWaysGeomLine.IntersectionStatus;
+import org.openstreetmap.josm.plugins.alignways.geometry.AlignWaysGeomPoint;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysCmdKeepAngles extends AlignWaysCmdKeepLength {
+
+    private AlignableStatus alignableStatKeepAngles = AlignableStatus.ALGN_VALID;
+
+    public AlignWaysCmdKeepAngles(DataSet ds) {
+        super(ds);
+        // Now the calculatedNodes reflect the coordinates that we'd have
+        // without preserving the angles, i.e. preserving the length.
+
+        Map<Node, EastNorth> calcNodesKeepLength = calculatedNodes;
+
+        // Now we'll proceed with the hypothetical recalculation of the endpoint coordinates
+        // following the rule of preserving angles instead. The new nodes will be stored in nodeArr[].
+
+        Node[] nodeArr = algnSeg.getSegmentEndPoints().toArray(new Node[2]);
+
+        EastNorth enCalc1 = calcNodesKeepLength.get(nodeArr[0]);
+        EastNorth enCalc2 = calcNodesKeepLength.get(nodeArr[1]);
+        AlignWaysGeomLine lineKeepLength = new AlignWaysGeomLine(enCalc1.getX(), enCalc1.getY(), enCalc2.getX(), enCalc2.getY());
+
+        recalculateNodesAndValidate(lineKeepLength, nodeArr[0]);
+        recalculateNodesAndValidate(lineKeepLength, nodeArr[1]);
+    }
+
+    private void recalculateNodesAndValidate(AlignWaysGeomLine alignedLineKeepLength, Node endpoint) {
+
+        if (endpoint.getEastNorth().equals(pivot)) {
+            // endpoint is pivot: the coordinates won't change
+            return;
+        }
+
+        ArrayList<WaySegment> alws = algnSeg.getAdjacentWaySegments(endpoint);
+        int alwsSize = alws.size();
+        if (0 < alwsSize && alwsSize <= 2) {
+            // We need the intersection point of
+            //  - the alignee following the keep length rule
+            //  - the adjacent way segment
+
+            Node adjOther1 = getNonEqualNode(alws.get(0), endpoint);
+            EastNorth enAdjOther1 = adjOther1.getEastNorth();
+            Node adjOther2 = null;
+            EastNorth enAdjOther2 = null;
+
+            if (alwsSize == 2) {
+                adjOther2 = getNonEqualNode(alws.get(1), endpoint);
+                enAdjOther2 = adjOther2.getEastNorth();
+
+                // In order have a chance to align, (enAdjOther1, enAdjOther2 and endpoint) must be collinear
+                ArrayList<EastNorth> enAdjPts = new ArrayList<>(3);
+                enAdjPts.add(enAdjOther1);
+                enAdjPts.add(endpoint.getEastNorth());
+                enAdjPts.add(enAdjOther2);
+                if (!isEnSetCollinear(enAdjPts)) {
+                    // Not collinear, no point to proceed
+                    alignableStatKeepAngles = AlignableStatus.ALGN_INV_ANGLE_PRESERVING_CONFLICT;
+                    return;
+                }
+
+            }
+
+            // Update the calculated node for angle preserving alignment
+            AlignWaysGeomPoint isectPnt = alignedLineKeepLength.getIntersection(
+                    new AlignWaysGeomLine(enAdjOther1.getX(), enAdjOther1.getY(),
+                            endpoint.getEastNorth().getX(), endpoint.getEastNorth().getY()));
+            EastNorth enIsectPt = null;
+            // If the intersection is null, the adjacent and the alignee are parallel already:
+            // there's no need to update this node
+            if (isectPnt != null) {
+                enIsectPt = new EastNorth(isectPnt.getX(), isectPnt.getY());
+                // Don't "record" it yet as it may not be valid
+            } else if (alignedLineKeepLength.getIntersectionStatus() == IntersectionStatus.LINES_PARALLEL) {
+                alignableStatKeepAngles = AlignableStatus.ALGN_INV_ANGLE_PRESERVING_CONFLICT;
+                return;
+            }
+
+            // For the case of two adjacent segments with collinear points, the new endpoint may
+            // not fall between enAdjOther1 and enAdjOther2;
+            // this scenario is not allowed for the time being as placing the new intersection point on the line
+            // triggers complications.
+            // TODO - find a solution
+            if (alwsSize == 2 && enIsectPt != null) {
+                int middlePtIdx = AlignWaysGeomPoint.getMiddleOf3(
+                        new AlignWaysGeomPoint(enIsectPt),
+                        new AlignWaysGeomPoint(enAdjOther1),
+                        new AlignWaysGeomPoint(enAdjOther2));
+                if (middlePtIdx != 0) {
+                    EastNorth middlePt = null;
+                    switch(middlePtIdx) {
+                        case 1:
+                            middlePt = enIsectPt;
+                            break;
+                        case 2:
+                            middlePt = enAdjOther1;
+                            break;
+                        case 3:
+                            middlePt = enAdjOther2;
+                            break;
+                    }
+                    if (middlePt != null) {
+                        double eps = 1E-6;
+                        if (!middlePt.equalsEpsilon(enIsectPt, eps)) {
+                            // Intersection point didn't fall between the two adjacent points; not allowed
+                            alignableStatKeepAngles = AlignableStatus.ALGN_INV_XPOINT_FALLSOUT;
+                            return;
+
+                            /*
+                            if (middlePt.equalsEpsilon(enAdjOther1, eps)) {
+                                // Delete adjOther1
+                                // adjOther1.setDeleted(true);
+                            } else
+                                if (true);
+                                // Delete adjOther2
+                                // adjOther2.setDeleted(true);
+                             */
+                        }
+                    }
+                }
+            }
+
+            if (isectPnt != null) {
+                // Angle preserving alignment passed all verification tests: record it.
+                calculatedNodes.put(endpoint, enIsectPt);
+            }
+
+
+        } else {
+            // angle preserving alignment not possible
+            alignableStatKeepAngles = AlignableStatus.ALGN_INV_TOOMANY_CONNECTED_WS;
+        }
+    }
+
+    private boolean isEnSetCollinear(ArrayList<EastNorth> enAdjPts) {
+        ArrayList<AlignWaysGeomPoint> awAdjPts = new ArrayList<>();
+
+        for (EastNorth en : enAdjPts) {
+            AlignWaysGeomPoint pt = new AlignWaysGeomPoint(en.getX(), en.getY());
+            awAdjPts.add(pt);
+        }
+
+        return AlignWaysGeomPoint.isSetCollinear(awAdjPts);
+    }
+
+    private Node getNonEqualNode(WaySegment waySegment, Node endpoint) {
+        if (waySegment.getFirstNode().equals(endpoint)) {
+            return waySegment.getSecondNode();
+        } else if (waySegment.getSecondNode().equals(endpoint)) {
+            return waySegment.getFirstNode();
+        } else
+            return null;
+    }
+
+    /**
+     * Reports invalid alignable statuses on screen in dialog boxes.
+     *
+     * @param stat The invalid status to report
+     */
+    @Override
+    void reportInvalidCommand(AlignableStatus stat) {
+        String statMsg;
+
+        switch (stat) {
+        case ALGN_INV_CONNECTED_UNSHARED_PIVOT:
+            statMsg = tr("Please select two segments that don''t share any nodes.\n"
+                    + "Alternatively put the pivot on their common node.\n");
+            break;
+        case ALGN_INV_OUTSIDE_WORLD:
+            statMsg = tr("Aligning would result nodes ''outside the world''.\n"
+                    + "Alignment not possible.\n");
+            break;
+        case ALGN_INV_TOOMANY_CONNECTED_WS:
+            statMsg = tr("There is at least a non-pivot endpoint of the alignee that joins more than two way segments.\n"
+                    + "Preserved angles type alignment is not possible.\n");
+            break;
+        case ALGN_INV_ANGLE_PRESERVING_CONFLICT:
+            statMsg = tr("The alignment is not possible with maintaining the angles of the joint segments.\n"
+                    + "Either choose the ''keep length'' aligning method or select other segments.\n");
+            break;
+        case ALGN_INV_XPOINT_FALLSOUT:
+            statMsg = tr("An intersection point would fall outside its adjacent nodes.\n"
+                    + "This is an unsupported scenario.\n");
+            break;
+        default:
+            statMsg = tr("Undocumented problem occured.\n");
+            break;
+        }
+
+        JOptionPane.showMessageDialog(
+                MainApplication.getMainFrame(),
+                tr(statMsg),
+                tr("AlignWayS: Alignment not possible"),
+                JOptionPane.WARNING_MESSAGE
+                );
+    }
+
+    /**
+     * Returns true if the two selected segments are alignable.
+     * They are not if they are connected *and* the pivot is not the connection
+     * node.
+     * They are also not if the moving segment endpoints connect to more than
+     * one other way segment.
+     */
+    @Override
+    AlignableStatus areSegsAlignable() {
+        AlignableStatus status = super.areSegsAlignable();
+
+        // The constructor may already have reported some problems: check.
+        if (alignableStatKeepAngles != AlignableStatus.ALGN_VALID)
+            return alignableStatKeepAngles;
+
+        // The superclass may have reported problems: check.
+        if (status != AlignableStatus.ALGN_VALID)
+            return status;
+
+        // Check the remainder of the potential problems: N/A
+
+        // In all other cases alignment is possible
+        return AlignableStatus.ALGN_VALID;
+    }
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysCmdKeepLength.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysCmdKeepLength.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysCmdKeepLength.java	(revision 34489)
@@ -0,0 +1,313 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import javax.swing.Icon;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.command.Command;
+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.WaySegment;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Executes one of the desired geometric actions
+ * needed to align the ways.
+ *
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ */
+public class AlignWaysCmdKeepLength extends Command {
+
+    enum AlignableStatus {
+        ALGN_VALID,
+        ALGN_INV_CONNECTED_UNSHARED_PIVOT,
+        ALGN_INV_OUTSIDE_WORLD,
+        ALGN_INV_TOOMANY_CONNECTED_WS,      // for AlignWaysCmdKeepAngles
+        ALGN_INV_ANGLE_PRESERVING_CONFLICT, // for AlignWaysCmdKeepAngles
+        ALGN_INV_XPOINT_FALLSOUT            // for AlignWaysCmdKeepAngles
+    }
+
+    final AlignWaysAlgnSegment algnSeg;
+
+    /**
+     * The nodes that will move as result of the aligning.
+     */
+    final Collection<Node> displaceableNodes;
+
+    /**
+     * Maps the alignee nodes with their calculated coordinates.
+     * Useful for validation.
+     */
+    final Map<Node, EastNorth> calculatedNodes = new HashMap<>();
+
+    /**
+     * Set of nodes that were affected by the last command execution.
+     */
+    Collection<Node> lastAffectedNodes;
+
+    /**
+     * Pivot point
+     */
+    final EastNorth pivot;
+
+    /**
+     * Computed rotation angle to rotate the segment
+     *
+     */
+    private final double rotationAngle;
+
+    /**
+     * List of all old states of the objects.
+     */
+    final Map<Node, Node> oldNodes = new HashMap<>();
+
+    /**
+     * Creates an AlignWaysRotateCommand.
+     * TODO Limitation (for now): constructor assumes areSegsAlignable() returns true.
+     * @param ds the data set. Must not be null.
+     */
+    public AlignWaysCmdKeepLength(DataSet ds) {
+        super(ds);
+        lastAffectedNodes = null;
+
+        MapView mapView = MainApplication.getMap().mapView;
+        algnSeg = AlignWaysSegmentMgr.getInstance(mapView).getAlgnSeg();
+        WaySegment algnWS = algnSeg.getSegment();
+        WaySegment refWS = AlignWaysSegmentMgr.getInstance(mapView).getRefSeg().getSegment();
+
+        this.pivot = algnSeg.getCurrPivotCoord();
+        this.displaceableNodes = algnSeg.getSegmentEndPoints();
+
+        EastNorth enRefNode1 = refWS.getFirstNode().getEastNorth();
+        EastNorth enRefNode2 = refWS.getSecondNode().getEastNorth();
+
+        EastNorth enAlgnNode1 = algnWS.getFirstNode().getEastNorth();
+        EastNorth enAlgnNode2 = algnWS.getSecondNode().getEastNorth();
+
+        // Calculate the rotation angle
+        double refAngle = Math.atan2(enRefNode1.north() - enRefNode2.north(),
+                enRefNode1.east() - enRefNode2.east());
+        double algnAngle = Math.atan2(
+                enAlgnNode1.north() - enAlgnNode2.north(), enAlgnNode1.east()
+                - enAlgnNode2.east());
+
+        rotationAngle = normalise_angle(refAngle - algnAngle);
+
+        // Rotate using the rotation matrix known in algebra:
+        // [ x' ] = [ cos(Phi) -sin(Phi) ] [ x ]
+        // [ y' ]   [ sin(Phi)  cos(Phi) ] [ y ]
+        for (Node n : displaceableNodes) {
+            double cosPhi = Math.cos(rotationAngle);
+            double sinPhi = Math.sin(rotationAngle);
+            EastNorth oldEastNorth = n.getEastNorth();
+            double x = oldEastNorth.east() - pivot.east();
+            double y = oldEastNorth.north() - pivot.north();
+            double nx = cosPhi * x - sinPhi * y + pivot.east();
+            double ny = sinPhi * x + cosPhi * y + pivot.north();
+
+            calculatedNodes.put(n, new EastNorth(nx, ny));
+        }
+
+        /*
+         * For debug only
+         * String s = "Ref Angle: " + refAngle + " (" + Math.toDegrees(refAngle)
+         * + ")\n";
+         * s += "Algn Angle: " + algnAngle + " (" + Math.toDegrees(algnAngle)
+         * + ")\n";
+         * s += "Rotation angle: " + rotationAngle + " ("
+         * + Math.toDegrees(rotationAngle) + ")";
+         */
+    }
+
+    /**
+     * Helper for actually rotating the nodes.
+     *
+     * @param setModified
+     *            - true if rotated nodes should be flagged "modified"
+     */
+    private void rotateNodes(boolean setModified) {
+
+        // "Backup" state
+        lastAffectedNodes = new HashSet<>();
+        for (Node n : this.displaceableNodes) {
+            Node nodeBackup = new Node(n);
+            // Set other fields that clone doesn't copy
+            nodeBackup.setEastNorth(n.getEastNorth());
+            oldNodes.put(n, nodeBackup);
+
+            lastAffectedNodes.add(n);
+        }
+
+        // Physical change occurs here:
+        for (Node n : displaceableNodes) {
+            n.setEastNorth(calculatedNodes.get(n));
+            if (setModified) {
+                n.setModified(true);
+            }
+        }
+        algnSeg.updatePivotsEndpoints();
+    }
+
+    /**
+     * Make sure angle is in interval ( -Pi/2, Pi/2 ].
+     * @param a angle
+     * @return normalized angle
+     */
+    private double normalise_angle(double a) {
+        while (a > Math.PI) {
+            a -= 2 * Math.PI;
+        }
+        while (a <= -Math.PI) {
+            a += 2 * Math.PI;
+        }
+
+        if (a > Math.PI / 2) {
+            a -= Math.PI;
+        } else if (a < -Math.PI / 2) {
+            a += Math.PI;
+        }
+        return a;
+    }
+
+    @Override
+    public String getDescriptionText() {
+        return tr("Align way segment");
+    }
+
+    @Override
+    public Icon getDescriptionIcon() {
+        return ImageProvider.get("alignways");
+    }
+
+    @Override
+    public void fillModifiedData(Collection<OsmPrimitive> modified,
+            Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
+        for (OsmPrimitive osm : displaceableNodes) {
+            modified.add(osm);
+        }
+    }
+
+    @Override
+    public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
+        Collection<OsmPrimitive> prims = new HashSet<>(displaceableNodes);
+        prims.add(algnSeg.getSegment().way);
+        return Collections.unmodifiableCollection(prims);
+    }
+
+    @Override
+    public boolean executeCommand() {
+        rotateNodes(true);
+        return true;
+    }
+
+    @Override
+    public void undoCommand() {
+        for (Node n : displaceableNodes) {
+            Node oldNode = oldNodes.get(n);
+            n.setCoor(oldNode.getCoor());
+        }
+        algnSeg.updatePivotsEndpoints();
+    }
+
+    public Collection<Node> getPrevAffectedNodes() {
+        return lastAffectedNodes;
+    }
+
+    /**
+     * Returns true if the two selected segments are alignable.
+     * They are not if they are connected *and* the pivot is not the connection
+     * node.
+     * @return alignable status
+     */
+    AlignableStatus areSegsAlignable() {
+        Collection<Node> algnNodes = displaceableNodes;
+        Collection<Node> refNodes = AlignWaysSegmentMgr
+                .getInstance(MainApplication.getMap().mapView).getRefSeg()
+                .getSegmentEndPoints();
+
+        // First check if the pivot node of the alignee exists in the reference:
+        // in this case the pivot is the shared node and alignment is possible
+        for (Node nR : refNodes) {
+            if (nR.getEastNorth().equals(pivot))
+                return AlignableStatus.ALGN_VALID;
+        }
+
+        // Otherwise if the segments are connected, alignment is not possible
+        for (Node nA : algnNodes) {
+            for (Node nR : refNodes) {
+                if (nA.equals(nR))
+                    return AlignableStatus.ALGN_INV_CONNECTED_UNSHARED_PIVOT;
+            }
+        }
+
+        // Deny action if the nodes would end up outside world
+        for (EastNorth en : calculatedNodes.values()) {
+            if (ProjectionRegistry.getProjection().eastNorth2latlon(en).isOutSideWorld())
+                return AlignableStatus.ALGN_INV_OUTSIDE_WORLD;
+        }
+
+        // In all other cases alignment is possible
+        return AlignableStatus.ALGN_VALID;
+    }
+
+    /**
+     * Validates the circumstances of the alignment command to be executed.
+     *
+     * @return true if the aligning action can be done, false otherwise.
+     */
+    public boolean executable() {
+        AlignableStatus stat = areSegsAlignable();
+
+        if (stat != AlignableStatus.ALGN_VALID) {
+            reportInvalidCommand(stat);
+            return false;
+        } else
+            // Action valid
+            return true;
+    }
+
+    /**
+     * Reports invalid alignable statuses on screen in dialog boxes.
+     *
+     * @param stat The invalid status to report
+     */
+    void reportInvalidCommand(AlignableStatus stat) {
+        String statMsg;
+
+        switch (stat) {
+        case ALGN_INV_CONNECTED_UNSHARED_PIVOT:
+            statMsg = tr("Please select two segments that don''t share any nodes\n"
+                    + " or put the pivot on their common node.\n");
+            break;
+        case ALGN_INV_OUTSIDE_WORLD:
+            statMsg = tr("Aligning would result nodes ''outside the world''.\n"
+                    + "Alignment not possible.\n");
+            break;
+        default:
+            statMsg = tr("Undocumented problem occured.\n");
+            break;
+        }
+
+        JOptionPane.showMessageDialog(
+                MainApplication.getMainFrame(),
+                tr(statMsg),
+                tr("AlignWayS: Alignment not possible"),
+                JOptionPane.WARNING_MESSAGE
+                );
+
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysDialog.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysDialog.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysDialog.java	(revision 34489)
@@ -0,0 +1,162 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Objects;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JToggleButton;
+
+import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysDialog extends ToggleDialog implements ActionListener {
+
+    private static final long serialVersionUID = 2949349330750246969L;
+
+    private final JLabel infoText;
+    enum AligningModeOption {
+        ALGN_OPT_KEEP_LENGTH,
+        ALGN_OPT_KEEP_ANGLE
+    }
+
+    AligningModeOption awOpt;
+    JPanel activateInfoPanel, modesPanel, dlgPane;
+
+    public AlignWaysDialog(AlignWaysMode awMode) {
+        super(tr("Align Way Segments: Modes"), "alignways_cfg", tr("Align Ways control panel"),
+                null, 70);
+
+        infoText = new JLabel();
+
+        dlgPane = new JPanel();
+        dlgPane.setLayout(new GridLayout(0, 1, 20, 20));
+
+
+        // Create the panel that shows instruction when Align Ways mode is *not* active
+        activateInfoPanel = new JPanel();
+        activateInfoPanel.setLayout(new BoxLayout(activateInfoPanel, BoxLayout.PAGE_AXIS));
+        activateInfoPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+        JPanel lbl1Pnl = new JPanel();
+        lbl1Pnl.setLayout(new FlowLayout(FlowLayout.LEADING));
+        JLabel lbl1 = new JLabel(tr("This panel activates in Align Ways mode:"));
+        lbl1Pnl.add(lbl1);
+        activateInfoPanel.add(lbl1Pnl);
+
+        JPanel tglbtnPnl = new JPanel();
+        tglbtnPnl.setLayout(new FlowLayout(FlowLayout.CENTER));
+        JToggleButton tglBtn = new JToggleButton(awMode);
+        tglBtn.setPreferredSize(new Dimension(50, 50));
+        tglBtn.setText(null);
+        tglbtnPnl.add(tglBtn);
+        activateInfoPanel.add(tglbtnPnl);
+
+
+        // Create the Align Ways mode control panel for when Align Ways *is* active
+        modesPanel = new JPanel();
+        modesPanel.setLayout(new BoxLayout(modesPanel, BoxLayout.PAGE_AXIS));
+        /*
+        modesPanel.setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createEmptyBorder(10, 10, 10, 10),
+                BorderFactory.createTitledBorder(tr("Align with:")))
+                );
+         */
+        modesPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+        modesPanel.setAlignmentX(LEFT_ALIGNMENT);
+
+        JRadioButton btnKeepLength = new JRadioButton(tr("Length preserved"));
+        btnKeepLength.setActionCommand("awOptKeepLen");
+        btnKeepLength.addActionListener(this);
+
+        JRadioButton btnKeepAngle = new JRadioButton(tr("Angle preserved"));
+        btnKeepAngle.setActionCommand("awOptKeepAng");
+        btnKeepAngle.addActionListener(this);
+
+        ButtonGroup btnGrp = new ButtonGroup();
+        btnGrp.add(btnKeepLength);
+        btnGrp.add(btnKeepAngle);
+
+        modesPanel.add(new JLabel(tr("Align with:")));
+        modesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
+        modesPanel.add(btnKeepLength);
+        modesPanel.add(btnKeepAngle);
+        modesPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+        // modesPanel.add(new JSeparator(SwingConstants.HORIZONTAL));
+        infoText.setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createEtchedBorder(),
+                BorderFactory.createEmptyBorder(10, 10, 10, 10))
+                );
+        modesPanel.add(infoText);
+
+        // Start inactivated - JOSM cannot start directly in awMode
+        activate(false);
+
+        createLayout(dlgPane, false, null);
+
+        // Select length preserved mode by default
+        btnKeepLength.doClick();
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        if (Objects.equals(e.getActionCommand(), "awOptKeepLen")) {
+            awOpt = AligningModeOption.ALGN_OPT_KEEP_LENGTH;
+            infoText.setText(tr("<html>Aligns the way segment to the reference so that its length is preserved.</html>"));
+        } else if (Objects.equals(e.getActionCommand(), "awOptKeepAng")) {
+            awOpt = AligningModeOption.ALGN_OPT_KEEP_ANGLE;
+            infoText.setText(tr("<html>Aligns the way segment to the reference so that the angles of its adjacent segments are preserved.<br/>" +
+                    "The length of the aligned segment is likely to change as result.</html>"));
+        }
+    }
+
+    /**
+     * @return the awOpt
+     */
+    public AligningModeOption getAwOpt() {
+        return awOpt;
+    }
+
+
+    /**
+     * @param activeMode If set to true, the dialog will show the mode options, otherwise it will show some instructions
+     */
+    public void activate(boolean activeMode) {
+
+        if (activeMode == true) {
+            // we're in alignways mode
+            activateInfoPanel.setVisible(false);
+            modesPanel.setVisible(true);
+            this.setPreferredSize(new Dimension(0, 200));
+
+            dlgPane.remove(activateInfoPanel);
+            dlgPane.add(modesPanel);
+            dlgPane.validate();
+        } else {
+            // we're not in alignways mode
+            activateInfoPanel.setVisible(true);
+            modesPanel.setVisible(false);
+            this.setPreferredSize(new Dimension(0, 70));
+
+            dlgPane.remove(modesPanel);
+            dlgPane.add(activateInfoPanel);
+            dlgPane.validate();
+        }
+
+    }
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysMode.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysMode.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysMode.java	(revision 34489)
@@ -0,0 +1,302 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Cursor;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
+import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
+import org.openstreetmap.josm.data.osm.event.DataSetListener;
+import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
+import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
+import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
+import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
+import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
+import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ * Handles the state machine and user interaction (mouse clicks).
+ *
+ */
+public class AlignWaysMode extends MapMode implements DataSetListener {
+
+    private static final long serialVersionUID = -1090955708412011141L;
+    private final AlignWaysState noneSelected;
+    private final AlignWaysState referenceSelected;
+    private final AlignWaysState aligneeSelected;
+    private final AlignWaysState bothSelected;
+    private AlignWaysState currentState;
+    private AlignWaysSegmentMgr awSegs;
+    boolean tipShown;
+
+    public AlignWaysMode(String name, String desc) {
+        super(tr(name), "alignways.png", tr(desc),
+                Shortcut.registerShortcut("mapmode:alignways",
+                        tr("Mode: {0}", tr("Align Ways")),
+                        KeyEvent.VK_N, Shortcut.SHIFT),
+                        Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+        noneSelected = new AlignWaysSelNoneState();
+        referenceSelected = new AlignWaysSelRefState();
+        aligneeSelected = new AlignWaysSelAlgnState();
+        bothSelected = new AlignWaysSelBothState();
+        tipShown = false;
+    }
+
+    @Override
+    public void enterMode() {
+        super.enterMode();
+
+        AlignWaysPlugin.getAwDialog().activate(true);
+        IconToggleButton optBtn = AlignWaysPlugin.getOptBtn();
+        if (!optBtn.isSelected()) {
+            // Make sure the option panel is visible when align mode starts
+            optBtn.doClick();
+        }
+
+        boolean showTips = Boolean.parseBoolean(Config.getPref().get("alignways.showtips", "true"));
+        if ((showTips) && (!tipShown)) {
+            showTips();
+        }
+        int majorVer = Integer.parseInt(Config.getPref().get("alignways.majorver", "-1"));
+        if (majorVer != AlignWaysPlugin.AlignWaysMajorVersion) {
+            showWhatsNew();
+        }
+
+        awSegs = AlignWaysSegmentMgr.getInstance(MainApplication.getMap().mapView);
+        MainApplication.getMap().mapView.addMouseListener(this);
+        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
+        if (ds != null)
+            ds.addDataSetListener(this);
+        setCurrentState(noneSelected);
+    }
+
+    @Override
+    public void exitMode() {
+        super.exitMode();
+
+        AlignWaysPlugin.getAwDialog().activate(false);
+        IconToggleButton optBtn = AlignWaysPlugin.getOptBtn();
+        if (optBtn.isSelected()) {
+            // The option panel will be switched off
+            optBtn.doClick();
+        }
+
+        setCurrentState(noneSelected);
+        MainApplication.getMap().mapView.removeMouseListener(this);
+        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
+        if (ds != null)
+            ds.removeDataSetListener(this);
+        AlignWaysPlugin.getAwAction().setEnabled(false);
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+
+        boolean ctrlPressed = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+        boolean altPressed = (e.getModifiers() & (ActionEvent.ALT_MASK | InputEvent.ALT_GRAPH_MASK)) != 0;
+
+        if (e.getButton() == MouseEvent.BUTTON1) {
+
+            if (altPressed) {
+                currentState.altLClick(this);
+
+            } else {
+                Point clickedPoint = new Point(e.getX(), e.getY());
+
+                if (!ctrlPressed) {
+                    // Alignee could change
+
+                    if (awSegs.algnUpdate(clickedPoint)) {
+                        currentState.leftClick(this);
+                    }
+
+                } else {
+                    // Reference could change
+                    if (awSegs.refUpdate(clickedPoint)) {
+                        currentState.ctrlLClick(this);
+                    }
+                }
+            }
+        }
+
+        MainApplication.getMap().mapView.repaint();
+    }
+
+    /**
+     * Sets the current state based on the selected segments.
+     * @param mgr AlignWays segment manager singleton
+     */
+    public void setCurrentState(AlignWaysSegmentMgr mgr) {
+
+        boolean algnSelected = mgr.getAlgnSeg() != null;
+        boolean refSelected = mgr.getRefSeg() != null;
+
+        if (algnSelected && refSelected)
+            setCurrentState(getBothSelected());
+        else if (algnSelected)
+            setCurrentState(getAligneeSelected());
+        else if (refSelected)
+            setCurrentState(getReferenceSelected());
+        else
+            setCurrentState(getNoneSelected());
+    }
+
+    /**
+     * Sets the current state.
+     * @param currentState One of the AlignWays states
+     */
+    public void setCurrentState(AlignWaysState currentState) {
+        this.currentState = currentState;
+        currentState.setHelpText();
+
+        // Activate the Align Ways button if we have enough selections
+        AlignWaysPlugin.getAwAction().setEnabled(currentState == bothSelected);
+
+        if (currentState == noneSelected) {
+            awSegs.cleanupWays();
+
+            // getEditDataSet() may return null when the editable layer had
+            // already been removed by JOSM. This happens e.g. when the user closes
+            // JOSM while AlignWays mode is still active.
+            DataSet ds = getLayerManager().getEditDataSet();
+            if (ds != null) {
+                ds.clearSelection();
+            }
+        }
+    }
+
+    /**
+     * @return the noneSelected
+     */
+    public AlignWaysState getNoneSelected() {
+        return noneSelected;
+    }
+
+    /**
+     * @return the referenceSelected
+     */
+    public AlignWaysState getReferenceSelected() {
+        return referenceSelected;
+    }
+
+    /**
+     * @return the aligneeSelected
+     */
+    public AlignWaysState getAligneeSelected() {
+        return aligneeSelected;
+    }
+
+    /**
+     * @return the bothSelected
+     */
+    public AlignWaysState getBothSelected() {
+        return bothSelected;
+    }
+
+    private void showTips() {
+
+        AlignWaysTipsPanel atp = new AlignWaysTipsPanel();
+        Object[] okButton = {tr("I''m ready!")};
+        JOptionPane tipPane = new JOptionPane(atp, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION,
+                null, okButton, okButton[0]);
+        tipPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 30, 10));
+        JDialog tipDialog = tipPane.createDialog(MainApplication.getMainFrame(), tr("AlignWays Tips"));
+        tipDialog.setIconImage(new ImageIcon(getClass().getResource("/images/alignways.png")).getImage());
+
+        tipDialog.setResizable(true);
+        tipDialog.setVisible(true);
+        tipShown = true;
+
+        tipDialog.dispose();
+
+        Config.getPref().putBoolean("alignways.showtips", !atp.isChkBoxSelected());
+    }
+
+    private void showWhatsNew() {
+
+        AlignWaysWhatsNewPanel awnp = new AlignWaysWhatsNewPanel();
+        JOptionPane wnPane = new JOptionPane(awnp, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null);
+        wnPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+        JDialog wnDialog = wnPane.createDialog(MainApplication.getMainFrame(), tr("AlignWays: What''s New..."));
+        wnDialog.setIconImage(new ImageIcon(getClass().getResource("/images/alignways.png")).getImage());
+
+        wnDialog.setResizable(true);
+        wnDialog.setVisible(true);
+
+        wnDialog.dispose();
+
+        Config.getPref().put("alignways.majorver", Integer.toString(AlignWaysPlugin.AlignWaysMajorVersion));
+    }
+
+    @Override
+    public boolean layerIsSupported(Layer l) {
+        return l instanceof OsmDataLayer;
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(getLayerManager().getEditLayer() != null);
+    }
+
+    /* --------------- *
+     * DataSetListener *
+     * --------------- */
+
+    @Override
+    public void primitivesAdded(PrimitivesAddedEvent event) {
+    }
+
+    @Override
+    public void primitivesRemoved(PrimitivesRemovedEvent event) {
+        awSegs = AlignWaysSegmentMgr.getInstance(MainApplication.getMap().mapView);
+
+        // Check whether any of the removed primitives were part of a highlighted alignee or reference segment.
+        // If so: remove the affected segment and update the state accordingly.
+        if (awSegs.primitivesRemoved(event.getPrimitives()))
+            setCurrentState(awSegs);
+    }
+
+    @Override
+    public void tagsChanged(TagsChangedEvent event) {
+    }
+
+    @Override
+    public void nodeMoved(NodeMovedEvent event) {
+    }
+
+    @Override
+    public void wayNodesChanged(WayNodesChangedEvent event) {
+    }
+
+    @Override
+    public void relationMembersChanged(RelationMembersChangedEvent event) {
+    }
+
+    @Override
+    public void otherDatasetChange(AbstractDatasetChangedEvent event) {
+    }
+
+    @Override
+    public void dataChanged(DataChangedEvent event) {
+    }
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysPlugin.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysPlugin.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysPlugin.java	(revision 34489)
@@ -0,0 +1,82 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.PluginInformation;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysPlugin extends Plugin {
+
+    private static AlignWaysMode awMode;
+    private static IconToggleButton btn;
+    private static JosmAction awAction;
+    private static AlignWaysDialog awDialog;
+    private static IconToggleButton optBtn;
+
+    // The major version is e.g. used to decide when to trigger What's New windows
+    public static final int AlignWaysMajorVersion = 2;
+
+    public AlignWaysPlugin(PluginInformation info) {
+        super(info);
+
+        // Add the action entries to the Tools Menu
+        MainApplication.getMenu().moreToolsMenu.addSeparator();
+        awAction = new AlignWaysAction();
+        MainMenu.add(MainApplication.getMenu().moreToolsMenu, awAction);
+    }
+
+    @Override
+    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+        if (newFrame != null) {
+            // Construct the AlignWays mode toggle button
+            awMode = new AlignWaysMode("alignways", tr("Align Ways mode"));
+            btn = new IconToggleButton(awMode);
+            btn.setVisible(true);
+            newFrame.addMapMode(btn);
+            optBtn = newFrame.addToggleDialog(awDialog = new AlignWaysDialog(awMode));
+        } else {
+            awDialog = null;
+            optBtn = null;
+            btn = null;
+            awMode = null;
+        }
+    }
+
+    /**
+     * @return the awAction
+     */
+    public static JosmAction getAwAction() {
+        return awAction;
+    }
+
+    /**
+     * @return the awMode
+     */
+    public static AlignWaysMode getAwMode() {
+        return awMode;
+    }
+
+    /**
+     * @return the awDialog
+     */
+    public static AlignWaysDialog getAwDialog() {
+        return awDialog;
+    }
+
+    /**
+     * @return the optBtn
+     */
+    public static IconToggleButton getOptBtn() {
+        return optBtn;
+    }
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysRefSegment.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysRefSegment.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysRefSegment.java	(revision 34489)
@@ -0,0 +1,25 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import java.awt.Color;
+import java.awt.Point;
+
+import org.openstreetmap.josm.gui.MapView;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt; The basic segment treated as reference.
+ *
+ */
+public class AlignWaysRefSegment extends AlignWaysSegment {
+
+    // Note: segment may be null. This is normal.
+
+    public AlignWaysRefSegment(MapView mapview, Point p)
+    throws IllegalArgumentException {
+        super(mapview, p);
+        setSegment(getNearestWaySegment(p));
+        segmentColor = Color.GREEN;
+
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSegment.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSegment.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSegment.java	(revision 34489)
@@ -0,0 +1,143 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.geom.Line2D;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+
+import org.openstreetmap.josm.data.Bounds;
+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.gui.layer.MapViewPaintable;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysSegment implements MapViewPaintable {
+
+    protected WaySegment segment;
+    protected MapView mapview;
+    protected Color segmentColor = Color.WHITE;
+    protected Collection<Node> segmentEndPoints;
+
+    public AlignWaysSegment(MapView mapview, Point p) throws IllegalArgumentException {
+        if (mapview == null)
+            throw new IllegalArgumentException(tr(
+                    "Parameter ''{0}'' must not be null", "mapview"));
+        if (p == null)
+            throw new IllegalArgumentException(tr(
+                    "Parameter ''{0}'' must not be null", "p"));
+
+        this.mapview = mapview;
+    }
+
+    void setSegment(WaySegment segment) {
+        this.segment = segment;
+        if (segment != null) {
+            setSegmentEndpoints(segment);
+            mapview.addTemporaryLayer(this);
+        }
+    }
+
+    void setSegmentEndpoints(WaySegment segment) {
+        if (segment != null) {
+            try {
+                segmentEndPoints = new HashSet<>();
+                segmentEndPoints.add(segment.getFirstNode());
+                segmentEndPoints.add(segment.getSecondNode());
+            } catch (IndexOutOfBoundsException e) {
+                Logging.error(e);
+            }
+        }
+    }
+
+    protected WaySegment getNearestWaySegment(Point p) {
+        return mapview.getNearestWaySegment(p, OsmPrimitive::isUsable);
+    }
+
+    public void destroy() {
+        if (segment != null) {
+            mapview.removeTemporaryLayer(this);
+        }
+    }
+
+    public WaySegment getSegment() {
+        return segment;
+    }
+
+    public Collection<Node> getSegmentEndPoints() {
+        return segmentEndPoints;
+    }
+
+    @Override
+    public void paint(Graphics2D g, MapView mv, Bounds bbox) {
+        // Note: segment should never be null here, and its nodes should never be missing.
+        // If they are, it's a bug, possibly related to tracking of DataSet deletions.
+        if (segment.way.getNodesCount() <= segment.lowerIndex + 1) {
+            Logging.warn("Not drawing AlignWays highlighting segment: underlying nodes disappeared");
+            return;
+        }
+
+        highlightSegment(segmentColor, g, mv);
+    }
+
+    protected void highlightSegment(Color c, Graphics2D g, MapView mv) {
+        g.setColor(c);
+        g.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        drawSegment(g, mv);
+    }
+
+    protected void drawSegment(Graphics2D g, MapView mv) {
+        try {
+            Node n1 = segment.getFirstNode();
+            Node n2 = segment.getSecondNode();
+
+            g.draw(new Line2D.Double(mv.getPoint(n1), mv.getPoint(n2)));
+        } catch (IndexOutOfBoundsException e) {
+            Logging.error(e);
+        }
+    }
+
+    protected boolean containsPrimitive(OsmPrimitive primitive) {
+        if (segment == null)
+            return false;
+
+        return (primitive instanceof Way && segment.way.equals(primitive)) ||
+                (primitive instanceof Node && segmentEndPoints.contains(primitive));
+
+    }
+
+     @Override
+     public int hashCode() {
+        if (segment == null) {
+            return System.identityHashCode(this);
+        }
+
+        // hashCode and equals should be consistent during the lifetime of this segment,
+        // otherwise the temporary mapview overlay will not be properly removed on destroy()
+        return segment.hashCode();
+     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AlignWaysSegment)) return false;
+        AlignWaysSegment that = (AlignWaysSegment) o;
+        return Objects.equals(segment, that.segment);
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSegmentMgr.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSegmentMgr.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSegmentMgr.java	(revision 34489)
@@ -0,0 +1,187 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Point;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MapView;
+
+/**
+ * Should be a singleton class. The active alignee and reference objects
+ * manager. Can request alignee, reference segment updates and pivot updates on
+ * these.
+ *
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public final class AlignWaysSegmentMgr {
+
+    private static volatile AlignWaysSegmentMgr singleton;
+    private AlignWaysRefSegment refSeg = null;
+    private AlignWaysAlgnSegment algnSeg = null;
+    private final MapView mv;
+
+    private AlignWaysSegmentMgr(MapView mapView) {
+        mv = mapView;
+    }
+
+    /**
+     * Get or creates the segment manager instance belonging to a given MapView.
+     * @param mapView Map view
+     * @return segment manager instance for this map view
+     */
+    public static AlignWaysSegmentMgr getInstance(MapView mapView) {
+        synchronized (AlignWaysSegmentMgr.class) {
+            if (singleton == null || !singleton.getMapView().equals(mapView)) {
+                singleton = new AlignWaysSegmentMgr(mapView);
+            }
+        }
+        return singleton;
+    }
+
+    /**
+     * @return The MapView this segment manager belongs to.
+     */
+    private MapView getMapView() {
+        return mv;
+    }
+
+    /**
+     * @param clickedPoint
+     *            Point nearby where user probably clicked
+     * @return true, if alignee changed, false otherwise
+     */
+    public boolean algnUpdate(Point clickedPoint) {
+
+        // Check first if there is a pivot point nearby that needs selection
+        if (algnSeg != null && algnSeg.updatePivot(clickedPoint)) {
+            // Updated pivot, alignee reference unchanged
+            return false;
+        }
+
+        // Previous attempt of pivot update unsuccessful, check alignee update
+        AlignWaysAlgnSegment tmpAlgnSeg = new AlignWaysAlgnSegment(mv, clickedPoint);
+        if (tmpAlgnSeg.getSegment() == null)
+            return false;
+
+        // Found a segment - it may happen that the new segment is identical with the already
+        // selected asignee or reference segment. This action is then ignored.
+        if (algnSeg != null && tmpAlgnSeg.equals(algnSeg)) {
+            return false;
+        } else if (refSeg != null && tmpAlgnSeg.equals(refSeg)) {
+            JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
+                    tr("Segment to be aligned cannot be the same with the reference segment.\n" +
+                    "Please choose a different segment to be aligned."),
+                    tr("AlignWayS message"), JOptionPane.WARNING_MESSAGE);
+            return false;
+        }
+
+        // This will be a new alignee, old alignee (if any) will be lost:
+        if (algnSeg != null) {
+            algnSeg.destroy();
+        }
+
+        // Update alignee
+        algnSeg = tmpAlgnSeg;
+        return true;
+    }
+
+    /**
+     * @param clickedPoint
+     *            Point nearby where user probably clicked
+     * @return true, if reference changed, false otherwise
+     */
+    public boolean refUpdate(Point clickedPoint) {
+
+        AlignWaysRefSegment tmpRefSeg = new AlignWaysRefSegment(mv, clickedPoint);
+        if (tmpRefSeg.getSegment() == null)
+            return false;
+
+        // Found a segment - it may happen that the new segment is identical with the already
+        // selected asignee or reference segment. This action is then ignored.
+        if (refSeg != null && refSeg.equals(tmpRefSeg)) {
+            return false;
+        } else if (algnSeg != null && tmpRefSeg.equals(algnSeg)) {
+            JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
+                    tr("Reference segment cannot be the same with the segment to be aligned.\n" +
+                    "Please choose a different reference segment."),
+                    tr("AlignWayS message"), JOptionPane.WARNING_MESSAGE);
+            return false;
+        }
+
+        // This will be a new reference, old reference (if any) will be lost:
+        if (refSeg != null) {
+            refSeg.destroy();
+        }
+
+        // Update reference
+        refSeg = tmpRefSeg;
+        return true;
+    }
+
+    /**
+     * @return Collection of the nodes that belong to the selected alignee.
+     */
+    public Collection<Node> getSelectedNodes() {
+        if (algnSeg != null)
+            return algnSeg.getSegmentEndPoints();
+        return null;
+    }
+
+    /**
+     * Performs "clean-up" on the initialised segments
+     */
+    public void cleanupWays() {
+        if (algnSeg != null) {
+            algnSeg.destroy();
+            algnSeg = null;
+        }
+        if (refSeg != null) {
+            refSeg.destroy();
+            refSeg = null;
+        }
+    }
+
+    public AlignWaysAlgnSegment getAlgnSeg() {
+        return algnSeg;
+    }
+
+    /**
+     * @return the refSeg
+     */
+    public AlignWaysRefSegment getRefSeg() {
+        return refSeg;
+    }
+
+
+    /**
+     * Handles the event that OSM primitives were removed and checks whether
+     * this invalidates the currently selected alignee or reference segment.
+     * @param primitives List of primitives that were removed.
+     * @return Whether any of the selected segments were invalidated.
+     */
+    public boolean primitivesRemoved(List<? extends OsmPrimitive> primitives) {
+        boolean changed = false;
+        for (OsmPrimitive p : primitives) {
+            if (algnSeg != null && algnSeg.containsPrimitive(p)) {
+                algnSeg.destroy();
+                algnSeg = null;
+                changed = true;
+            }
+            if (refSeg != null && refSeg.containsPrimitive(p)) {
+                refSeg.destroy();
+                refSeg = null;
+                changed = true;
+            }
+        }
+        return changed;
+    }
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelAlgnState.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelAlgnState.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelAlgnState.java	(revision 34489)
@@ -0,0 +1,30 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.gui.MainApplication;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysSelAlgnState extends AlignWaysState {
+
+    @Override
+    public void leftClick(AlignWaysMode alignWaysMode) {
+        // No state change, nothing to do
+    }
+
+    @Override
+    public void ctrlLClick(AlignWaysMode alignWaysMode) {
+        alignWaysMode.setCurrentState(alignWaysMode.getBothSelected());
+    }
+
+    @Override
+    public void setHelpText() {
+        MainApplication.getMap().statusLine
+                .setHelpText(tr("Ctrl-Click: select reference way segment; Alt-click: Clear selection"));
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelBothState.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelBothState.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelBothState.java	(revision 34489)
@@ -0,0 +1,31 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.gui.MainApplication;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysSelBothState extends AlignWaysState {
+
+    @Override
+    public void leftClick(AlignWaysMode alignWaysMode) {
+        // No state change, nothing to do
+    }
+
+    @Override
+    public void ctrlLClick(AlignWaysMode alignWaysMode) {
+        // No state change, nothing to do
+    }
+
+    @Override
+    public void setHelpText() {
+        MainApplication.getMap().statusLine
+        .setHelpText(AlignWaysPlugin.getAwAction().getShortcut().getKeyText() +
+                tr(": Align segments; Alt-click: Clear selection"));
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelNoneState.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelNoneState.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelNoneState.java	(revision 34489)
@@ -0,0 +1,33 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.gui.MainApplication;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysSelNoneState extends AlignWaysState {
+
+    @Override
+    public void leftClick(AlignWaysMode alignWaysMode) {
+        // Reference way segment selected successfully
+        alignWaysMode.setCurrentState(alignWaysMode.getAligneeSelected());
+
+    }
+
+    @Override
+    public void ctrlLClick(AlignWaysMode alignWaysMode) {
+        // Reference way segment selected successfully
+        alignWaysMode.setCurrentState(alignWaysMode.getReferenceSelected());
+    }
+
+    @Override
+    public void setHelpText() {
+        MainApplication.getMap().statusLine
+        .setHelpText(tr("Ctrl-click: select reference way segment; Click: select way segment to be aligned"));
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelRefState.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelRefState.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysSelRefState.java	(revision 34489)
@@ -0,0 +1,30 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.gui.MainApplication;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysSelRefState extends AlignWaysState {
+
+    @Override
+    public void leftClick(AlignWaysMode alignWaysMode) {
+        alignWaysMode.setCurrentState(alignWaysMode.getBothSelected());
+    }
+
+    @Override
+    public void ctrlLClick(AlignWaysMode alignWaysMode) {
+        // No state change, nothing to do
+    }
+
+    @Override
+    public void setHelpText() {
+        MainApplication.getMap().statusLine
+                .setHelpText(tr("Click: select way segment to be aligned; Alt-click: Clear selection"));
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysState.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysState.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysState.java	(revision 34489)
@@ -0,0 +1,26 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.gui.MainApplication;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public abstract class AlignWaysState {
+
+    public abstract void leftClick(AlignWaysMode alignWaysMode);
+
+    public abstract void ctrlLClick(AlignWaysMode alignWaysMode);
+
+    public abstract void setHelpText();
+
+    public void altLClick(AlignWaysMode alignWaysMode) {
+        alignWaysMode.setCurrentState(alignWaysMode.getNoneSelected());
+        MainApplication.getMap().statusLine
+        .setHelpText(tr("Ctrl-Click: select reference way segment; Click: select way segment to be aligned"));
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysTipsPanel.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysTipsPanel.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysTipsPanel.java	(revision 34489)
@@ -0,0 +1,205 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Cursor;
+import java.awt.Dimension;
+
+import javax.swing.GroupLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.LayoutStyle;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingConstants;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysTipsPanel extends JPanel {
+
+    private static final long serialVersionUID = -8583989497599985140L;
+
+    public AlignWaysTipsPanel() {
+        initComponents();
+    }
+
+    private void initComponents() {
+
+        Title = new JPanel();
+        WelcomeTo = new JLabel();
+        Icon = new JLabel();
+        separator = new JSeparator();
+        Intro = new JPanel();
+        introText = new JLabel();
+        scrollableSteps = new JScrollPane();
+        steps = new JPanel();
+        step01 = new JLabel();
+        step02 = new JLabel();
+        step03 = new JLabel();
+        step04 = new JLabel();
+        lastHint = new JLabel();
+        dontShow = new JCheckBox();
+
+        setAutoscrolls(true);
+
+        WelcomeTo.setText("<html>\n<div style=\"font-family: \"sans-serif\"; font-weight: bold; font-style: italic;\">\n<span style=\"font-size: large;\">"
+                + tr("Welcome to the</span><br>\n<span style=\"font-size: xx-large;\">AlignWay<span style=\"color: rgb(204, 85, 0);\">S</span> Plugin<br>\n</span>"
+                        + "<span style=\"font-size: medium;\"><br>\n...or it rather should be called <br>\n<span style=\"font-size: large;\">AlignWayS(egments)</span> Plugin...")
+                        + "</span>\n</div>\n</html>");
+
+        WelcomeTo.setVerticalAlignment(SwingConstants.TOP);
+        WelcomeTo.setPreferredSize(new Dimension(400, 128));
+
+        Icon.setIcon(new ImageIcon(getClass().getResource("/images/tipsdialog/alignways128.png"))); // NOI18N
+        GroupLayout TitleLayout = new GroupLayout(Title);
+        Title.setLayout(TitleLayout);
+        TitleLayout.setHorizontalGroup(
+                TitleLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(GroupLayout.Alignment.TRAILING, TitleLayout.createSequentialGroup()
+                        .addComponent(WelcomeTo, GroupLayout.DEFAULT_SIZE, 396, Short.MAX_VALUE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(Icon, GroupLayout.PREFERRED_SIZE, 132, GroupLayout.PREFERRED_SIZE))
+                );
+        TitleLayout.setVerticalGroup(
+                TitleLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(TitleLayout.createSequentialGroup()
+                        .addComponent(Icon)
+                        .addContainerGap())
+                        .addComponent(WelcomeTo, GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE)
+                );
+
+        Intro.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+
+        introText.setText(tr("<html>\n<p style=\"font-family: sans-serif; font-weight: bold;\">AlignWays will\nhelp you to align two way segments. This can be handy when for instance\nyou sketch the outlines of a building and want its side to be parallel\nwith a street or road.<br>\n<br>\nSome tips may help before you start:\n</p>\n</html>\n\n"));
+        introText.setVerticalAlignment(SwingConstants.TOP);
+
+        scrollableSteps.setBorder(null);
+        scrollableSteps.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+
+        step01.setIcon(new ImageIcon(getClass().getResource("/images/tipsdialog/hlpRefSel.png"))); // NOI18N
+        step01.setText(tr("<html>\n<div style=\"font-family: sans-serif;\">\n<ul>\n<li><b>Select a reference segment.</b> You can do this by <b><i><span style=\"color:green\">Ctrl-click</span></i></b>ing\non a segment. The other, to be aligned segment will become parallel to\nthis one. </li>\n</ul>\n</div>\n</html>\n\n"));
+        step01.setVerticalAlignment(SwingConstants.TOP);
+
+        step02.setIcon(new ImageIcon(getClass().getResource("/images/tipsdialog/hlpAlgSel.png"))); // NOI18N
+        step02.setText(tr("<html>\n<div style=\"font-family:sans-serif\">\n<ul>\n  <li><b>Select the to be aligned segment.</b> You can do this by simply <b><i><span style=\"color:green\">click</span></i></b>ing on a different segment. \nThe rotation pivot will be highlighted by default in the centre of the segment.\n  </li>\n</ul>\n</div>\n</html>\n\n"));
+        step02.setVerticalAlignment(SwingConstants.TOP);
+
+        step03.setIcon(new ImageIcon(getClass().getResource("/images/tipsdialog/hlpPvtSel.png"))); // NOI18N
+        step03.setText(tr("<html>\n<div style=\"font-family:sans-serif\">\n<ul>\n  <li>Optionally <b>change the rotation pivot point</b>. In order to get parallel with the reference segment, the to be aligned segment will rotate around this point. You can choose the two extremities or the centre of the segment by <b><i><span style=\"color:green\">click</span></i></b>ing nearby. \n  </li>\n</ul>\n</div>\n</html>\n\n"));
+        step03.setVerticalAlignment(SwingConstants.TOP);
+
+        step04.setIcon(new ImageIcon(getClass().getResource("/images/tipsdialog/hlpAlgCmd.png"))); // NOI18N
+        step04.setText(tr("<html>\n<div style=\"font-family:sans-serif\">\n<ul>\n  <li><b>Align the segments.</b> Press <b><i><span style=\"color:green\">{0}"
+                + "</span></i></b>. Alternatively you''ll find the command in the <b>Tools</b>\n menu or may want to place the action on the <b>toolbar</b>.\n  </li>\n</ul>\n</div>\n</html>\n\n",
+                AlignWaysPlugin.getAwAction().getShortcut().getKeyText()));
+        step04.setVerticalAlignment(SwingConstants.TOP);
+
+        lastHint.setText(tr("<html>\n<div style=\"font-family:sans-serif\">\n<b>Last hint:</b> There is an easy way to start over your selections if you want: <b><i><span style=\"color:green\">Alt-Click</span></i></b> somewhere on the map.\n</div>\n</html>\n\n"));
+        lastHint.setVerticalAlignment(SwingConstants.TOP);
+
+        GroupLayout stepsLayout = new GroupLayout(steps);
+        steps.setLayout(stepsLayout);
+        stepsLayout.setHorizontalGroup(
+                stepsLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(stepsLayout.createSequentialGroup()
+                        .addContainerGap()
+                        .addGroup(stepsLayout.createParallelGroup(GroupLayout.Alignment.TRAILING)
+                                .addComponent(lastHint, GroupLayout.Alignment.LEADING, 0, 0, Short.MAX_VALUE)
+                                .addComponent(step04, GroupLayout.Alignment.LEADING, 0, 0, Short.MAX_VALUE)
+                                .addComponent(step03, GroupLayout.Alignment.LEADING, 0, 0, Short.MAX_VALUE)
+                                .addComponent(step02, GroupLayout.Alignment.LEADING, 0, 0, Short.MAX_VALUE)
+                                .addComponent(step01, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 496, Short.MAX_VALUE))
+                                .addGap(18, 18, 18))
+                );
+        stepsLayout.setVerticalGroup(
+                stepsLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(stepsLayout.createSequentialGroup()
+                        .addComponent(step01, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(step02, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(step03, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(step04, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(lastHint, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addContainerGap(22, Short.MAX_VALUE))
+                );
+
+        scrollableSteps.setViewportView(steps);
+
+        dontShow.setText(tr("Don''t show this again"));
+
+        GroupLayout IntroLayout = new GroupLayout(Intro);
+        Intro.setLayout(IntroLayout);
+        IntroLayout.setHorizontalGroup(
+                IntroLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(IntroLayout.createSequentialGroup()
+                        .addContainerGap()
+                        .addComponent(dontShow, GroupLayout.PREFERRED_SIZE, 245, GroupLayout.PREFERRED_SIZE)
+                        .addContainerGap(283, Short.MAX_VALUE))
+                        .addComponent(scrollableSteps, GroupLayout.DEFAULT_SIZE, 534, Short.MAX_VALUE)
+                        .addComponent(introText, GroupLayout.DEFAULT_SIZE, 534, Short.MAX_VALUE)
+                );
+        IntroLayout.setVerticalGroup(
+                IntroLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(GroupLayout.Alignment.TRAILING, IntroLayout.createSequentialGroup()
+                        .addComponent(introText, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(scrollableSteps, GroupLayout.PREFERRED_SIZE, 209, GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
+                        .addComponent(dontShow)
+                        .addContainerGap())
+                );
+
+        GroupLayout layout = new GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+                layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                        .addContainerGap()
+                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING)
+                                .addComponent(separator, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 534, Short.MAX_VALUE)
+                                .addComponent(Title, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(Intro, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                                .addContainerGap())
+                );
+        layout.setVerticalGroup(
+                layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(layout.createSequentialGroup()
+                        .addContainerGap()
+                        .addComponent(Title, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
+                        .addComponent(separator, GroupLayout.PREFERRED_SIZE, 17, GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(Intro, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                        .addContainerGap(45, Short.MAX_VALUE))
+                );
+    }
+
+
+    private JLabel Icon;
+    private JPanel Intro;
+    private JPanel Title;
+    private JLabel WelcomeTo;
+    private JCheckBox dontShow;
+    private JLabel introText;
+    private JLabel lastHint;
+    private JScrollPane scrollableSteps;
+    private JSeparator separator;
+    private JLabel step01;
+    private JLabel step02;
+    private JLabel step03;
+    private JLabel step04;
+    private JPanel steps;
+
+    public boolean isChkBoxSelected() {
+        return dontShow.isSelected();
+    }
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysWhatsNewPanel.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysWhatsNewPanel.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/AlignWaysWhatsNewPanel.java	(revision 34489)
@@ -0,0 +1,135 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.GroupLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSeparator;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysWhatsNewPanel extends JPanel {
+
+    private static final long serialVersionUID = 3691600157957492583L;
+
+    public AlignWaysWhatsNewPanel() {
+        initComponents();
+    }
+
+    private void initComponents() {
+
+        lblWhatsNew = new JLabel();
+        icnLogo = new JLabel();
+        jSeparator1 = new JSeparator();
+        newItem1 = new JLabel();
+        btnHelpItem1 = new JButton();
+        newItem2 = new JLabel();
+
+        lblWhatsNew.setText("<html><div style=\"font-family: sans-serif; font-weight: bold; font-style: italic;\">"+
+                            "<span style=\"font-size: large;\"><span style=\"font-size: x-large;\">"
+                            + tr("What''s new...")
+                            + "</span></div></html>");
+
+        icnLogo.setIcon(new ImageIcon(getClass().getResource("/images/wndialog/alignways64.png"))); // NOI18N
+
+        newItem1.setText("<html><div style=\"font-family: sans-serif;\"><ul style=\"margin-left: 20px;\"><li>"
+                         + tr("Added <b>angle preserving</b> aligning mode")
+                         + "</li></ul></div></html>");
+
+        btnHelpItem1.setIcon(new ImageIcon(getClass().getResource("/images/wndialog/extlink10.png"))); // NOI18N
+        btnHelpItem1.setText("More Info");
+        btnHelpItem1.setToolTipText("Preserving angle aligning");
+        btnHelpItem1.setBorder(null);
+        btnHelpItem1.setBorderPainted(false);
+        btnHelpItem1.setFocusPainted(false);
+        btnHelpItem1.setFocusable(false);
+        btnHelpItem1.setIconTextGap(6);
+        btnHelpItem1.setOpaque(false);
+        btnHelpItem1.setPreferredSize(new Dimension(69, 25));
+        btnHelpItem1.addActionListener(evt -> btnHelpItem1ActionPerformed(evt));
+
+        newItem2.setText("<html><div style=\"font-family: sans-serif;\"><ul style=\"margin-left: 20px;\"><li>"
+                         + tr("Various improvements and bugfixes")
+                         + "</li></ul></div></html>");
+
+        GroupLayout layout = new GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                    .addComponent(jSeparator1, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 349, Short.MAX_VALUE)
+                    .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                        .addComponent(lblWhatsNew, GroupLayout.DEFAULT_SIZE, 267, Short.MAX_VALUE)
+                        .addGap(18, 18, 18)
+                        .addComponent(icnLogo))
+                    .addGroup(layout.createSequentialGroup()
+                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING, false)
+                            .addComponent(newItem2, GroupLayout.Alignment.LEADING)
+                            .addComponent(newItem1, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 249, Short.MAX_VALUE))
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                        .addComponent(btnHelpItem1, GroupLayout.PREFERRED_SIZE, 90, GroupLayout.PREFERRED_SIZE)))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                    .addComponent(icnLogo)
+                    .addComponent(lblWhatsNew, GroupLayout.PREFERRED_SIZE, 69, GroupLayout.PREFERRED_SIZE))
+                .addGap(20, 20, 20)
+                .addComponent(jSeparator1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                    .addComponent(newItem1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(btnHelpItem1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(newItem2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                .addContainerGap(23, Short.MAX_VALUE))
+        );
+    }
+
+    private void btnHelpItem1ActionPerformed(ActionEvent evt) {
+       openURI();
+    }
+
+    private JButton btnHelpItem1;
+    private JLabel icnLogo;
+    private JSeparator jSeparator1;
+    private JLabel lblWhatsNew;
+    private JLabel newItem1;
+    private JLabel newItem2;
+
+    private void openURI() {
+        if (Desktop.isDesktopSupported()) {
+          try {
+            URI uri = new URI("https://wiki.openstreetmap.org/wiki/JOSM/Plugins/AlignWayS#Preserving_angles");
+            Desktop.getDesktop().browse(uri);
+          } catch (URISyntaxException ex) {
+              Logger.getLogger(AlignWaysWhatsNewPanel.class.getName()).log(Level.SEVERE, null, ex);
+          } catch (IOException e) {
+              JOptionPane.showMessageDialog(this, e, tr("Errr..."), JOptionPane.WARNING_MESSAGE);
+          }
+        } else {
+             JOptionPane.showMessageDialog(this, tr("Browser not supported."), tr("Errr..."), JOptionPane.WARNING_MESSAGE);
+        }
+    }
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomLine.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomLine.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomLine.java	(revision 34489)
@@ -0,0 +1,208 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways.geometry;
+
+import java.util.Objects;
+
+/**
+ * @author tilusnet &lt;tilusnet@gmail.com&gt;
+ *
+ */
+public class AlignWaysGeomLine {
+
+    double coef_a, coef_b, coef_c;
+
+    public enum IntersectionStatus {
+        UNDEFINED,
+        INTERSECT_POINT,
+        LINES_PARALLEL,
+        LINES_OVERLAP
+    }
+
+    IntersectionStatus isectStat = IntersectionStatus.UNDEFINED;
+
+    /**
+     * Constructor defining the line with the coordinates of two points.
+     * @param x1 x coordinate of point 1.
+     * @param y1 y coordinate of point 1.
+     * @param x2 x coordinate of point 2.
+     * @param y2 y coordinate of point 2.
+     */
+    public AlignWaysGeomLine(double x1, double y1, double x2, double y2) {
+        // ax + by + c = 0
+        //
+        //     y2 - y1
+        // a = ------- (the slope);  b = -1; c = y1 - a*x1
+        //     x2 - x1
+        //
+        // See conversion guidelines: http://www.webmath.com/equline1.html
+        if (x1 == x2) {
+            // Vertical line (would result div by zero if equation applied)
+            coef_a = 1;
+            coef_b = 0;
+            coef_c = -x1;
+        } else {
+            coef_a = ((y2 - y1)/(x2 - x1));
+            coef_b = -1;
+            coef_c = y1 - coef_a * x1;
+        }
+    }
+
+    /**
+     * Constructor defining the line with the 3 coefficients of its equation, i.e. ax + by + c = 0.
+     * @param a Coefficient a.
+     * @param b Coefficient b.
+     * @param c Coefficient c.
+     */
+    public AlignWaysGeomLine(double a, double b, double c) {
+        coef_a = a;
+        coef_b = b;
+        coef_c = c;
+    }
+
+    /**
+     * Constructor defining the line with the slope m and the y-intercept b, i.e. y = mx + b;
+     * @param m Slope.
+     * @param b Y-intercept.
+     */
+    public AlignWaysGeomLine(double m, double b) {
+        coef_a = m;
+        coef_b = -1;
+        coef_c = b;
+    }
+
+    public AlignWaysGeomLine(AlignWaysGeomLine line) {
+        this(line.coef_a, line.coef_b, line.coef_c);
+    }
+
+    public AlignWaysGeomLine(AlignWaysGeomPoint awPt1, AlignWaysGeomPoint awPt2) {
+        this(awPt1.getX(), awPt1.getY(), awPt2.getX(), awPt2.getY());
+    }
+
+    /**
+     * Returns the intersection point of the line with another line.
+     * If the lines are parallel or overlap, returns null.
+     * Use getIntersectionStatus() to determine the case.
+     * @param other_line The other line.
+     * @return The intersection point of the lines.
+     */
+    public AlignWaysGeomPoint getIntersection(AlignWaysGeomLine other_line) {
+        AlignWaysGeomPoint result = null;
+
+        // Use Cramer-rule, i.e.:
+        // - if (det1 != 0), there is an intersection in a point
+        // - if (det1 == 0, det2 == 0, det3 == 0), the lines overlap
+        // - if (det1 == 0) and any of det2 or det3 != 0, the lines are parallel
+
+        // See: http://www.mathwizz.com/algebra/help/help21.htm
+        //  and https://en.wikipedia.org/wiki/Cramer%27s_rule
+
+
+        double det1 = (coef_a * other_line.coef_b) - (other_line.coef_a * coef_b);
+        double det2 = (-coef_c * other_line.coef_b) - (-other_line.coef_c * coef_b);
+        double det3 = (coef_a * -other_line.coef_c) - (other_line.coef_a * -coef_c);
+
+        if (Math.abs(det1) < 0.01) {
+            if ((Math.abs(det2) < 0.01) && (Math.abs(det3) < 0.01)) {
+                // Lines overlap
+                isectStat = IntersectionStatus.LINES_OVERLAP;
+            } else {
+                // Lines are parallel
+                isectStat = IntersectionStatus.LINES_PARALLEL;
+            }
+        } else {
+            // Lines intersect in a point
+            result = new AlignWaysGeomPoint(det2/det1, det3/det1);
+            isectStat = IntersectionStatus.INTERSECT_POINT;
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Return the last result of getIntersection(), therefore use in conjuction with getIntersection().
+     * If getIntersection() was never  called before, it returns IntersectionStatus.UNDEFINED.
+     * @return The last result of getIntersection().
+     */
+    public IntersectionStatus getIntersectionStatus() {
+        return isectStat;
+    }
+
+    /**
+     * Get the Y coordinate on the line of a point with the given X coordinate.
+     *
+     * @param x The x-coordinate of the given point.
+     * @return The calculated y-coordinate or Double.NaN if the line is vertical.
+     */
+    public Double getYonLine(double x) {
+
+        Double y = Double.valueOf((-coef_a*x - coef_c)/coef_b);
+
+        if (y.isInfinite() || y.isNaN())
+            // Vertical line
+            return Double.NaN;
+        else
+            return y;
+    }
+
+    /**
+     * Get the X coordinate on the line of a point with the given Y coordinate.
+     *
+     * @param y The y-coordinate of the given point.
+     * @return The calculated x-coordinate or Double.NaN if the line is horizontal.
+     */
+    public Double getXonLine(double y) {
+
+        Double x = Double.valueOf((-coef_b*y - coef_c)/coef_a);
+
+        if (x.isInfinite() || x.isNaN())
+            // Horizontal line
+            return Double.NaN;
+        else
+            return x;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof AlignWaysGeomLine))
+            return false;
+        AlignWaysGeomLine other = (AlignWaysGeomLine) obj;
+
+        if (Math.abs(this.coef_a - other.coef_a) < 0.01 &&
+                Math.abs(this.coef_b - other.coef_b) < 0.01 &&
+                Math.abs(this.coef_c - other.coef_c) < 0.01)
+            return true;
+        else
+            return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(coef_a, coef_b, coef_c);
+    }
+
+    public boolean isPointOnLine(AlignWaysGeomPoint awPt) {
+        // Method:
+        // 1. create a new line from awPt and one point of 'this'
+        // 2. check getIntersectionStatus of the two lines
+        // 3. if status is LINES_OVERLAP, the point os one the line, otherwise not
+
+        // Need an arbitrary point on this line; let it be (x, y)
+        Double x = 0.0;
+        Double y = getYonLine(x);
+        if (y.isNaN()) y = 0.0;
+
+        AlignWaysGeomLine line2 = new AlignWaysGeomLine(awPt, new AlignWaysGeomPoint(x, y));
+        getIntersection(line2);
+        if (getIntersectionStatus() == IntersectionStatus.LINES_OVERLAP)
+            return true;
+        else
+            return false;
+
+    }
+
+}
Index: /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomPoint.java
===================================================================
--- /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomPoint.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/src/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomPoint.java	(revision 34489)
@@ -0,0 +1,124 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways.geometry;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+
+public class AlignWaysGeomPoint {
+    double x;
+    double y;
+
+    public AlignWaysGeomPoint(double x, double y) {
+        setX(x);
+        setY(y);
+    }
+
+    public AlignWaysGeomPoint(EastNorth eastNorth) {
+        this.x = eastNorth.getX();
+        this.y = eastNorth.getY();
+    }
+
+    public double getX() {
+        return x;
+    }
+
+    public double getY() {
+        return y;
+    }
+
+    public void setX(double x) {
+        this.x = x;
+    }
+
+    public void setY(double y) {
+        this.y = y;
+    }
+
+    public static boolean isSetCollinear(ArrayList<AlignWaysGeomPoint> awPts) {
+        if (awPts.size() <= 1)
+            return false;
+
+        if (awPts.size() == 2)
+            return true;
+        else {
+            // at least 3 points
+            // First create a line of the first two points in the set
+            AlignWaysGeomLine line = new AlignWaysGeomLine(awPts.get(0), awPts.get(1));
+            // ...then check the subsequent points whether they are on the line
+            for (int i = 2; i < awPts.size(); i++) {
+                if (!line.isPointOnLine(awPts.get(i))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Determines which (EastNorth) point falls between the other two.
+     * Ideally to be used with collinear points.
+     * @param pt1 point 1
+     * @param pt2 point 2
+     * @param pt3 point 3
+     *
+     * @return 1, 2 or 3 for pt1, pt2 and pt3, respectively.
+     * 0 if middle value cannot be determined (i.e. some values are equal).
+     */
+    public static int getMiddleOf3(
+            AlignWaysGeomPoint pt1,
+            AlignWaysGeomPoint pt2,
+            AlignWaysGeomPoint pt3) {
+
+        int midPtXIdx = getMiddleOf3(pt1.x, pt2.x, pt3.x);
+        int midPtYIdx = getMiddleOf3(pt1.y, pt2.y, pt3.y);
+
+        if ((midPtXIdx == 0) && (midPtYIdx == 0))
+            // All 3 points overlap:
+            // Design decision: return the middle point (could be any other or none)
+            return 2;
+
+        if (midPtXIdx == 0) return midPtYIdx;
+        if (midPtYIdx == 0) return midPtXIdx;
+
+        // Both x and y middle points could be determined;
+        // their indexes must coincide
+        if (midPtXIdx == midPtYIdx)
+            // Success
+            return midPtXIdx; // (or midPtYIdx)
+        else
+            // Fail
+            return 0;
+
+    }
+
+    /**
+     * Determine which value, d1, d2 or d3 falls in the middle of the other two.
+     * @param d1 first value
+     * @param d2 second value
+     * @param d3 third value
+     * @return 1, 2 or 3 for d1, d2 and d3, respectively.
+     * 0 if middle value cannot be determined (i.e. some values are equal).
+     */
+    private static int getMiddleOf3(double d1, double d2, double d3) {
+
+        Double[] dValues = {d1, d2, d3};
+        ArrayList<Double> alValues = new ArrayList<>(Arrays.asList(dValues));
+        Collections.sort(alValues);
+
+        if ((Math.abs(alValues.get(1) - alValues.get(0)) < 0.01) ||
+            (Math.abs(alValues.get(1) - alValues.get(2)) < 0.01))
+            // Cannot determine absolute middle value
+            return 0;
+        else {
+            if (Math.abs(alValues.get(1) - d1) < 0.01) return 1;
+            if (Math.abs(alValues.get(1) - d2) < 0.01) return 2;
+            if (Math.abs(alValues.get(1) - d3) < 0.01) return 3;
+        }
+
+        // Should never happen
+        return 0;
+    }
+}
Index: /applications/editors/josm/plugins/alignways/test/unit/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomLineTest.java
===================================================================
--- /applications/editors/josm/plugins/alignways/test/unit/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomLineTest.java	(revision 34489)
+++ /applications/editors/josm/plugins/alignways/test/unit/org/openstreetmap/josm/plugins/alignways/geometry/AlignWaysGeomLineTest.java	(revision 34489)
@@ -0,0 +1,92 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.alignways.geometry;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.josm.plugins.alignways.geometry.AlignWaysGeomLine.IntersectionStatus;
+
+/**
+ * Tests of {@link AlignWaysGeomLine}
+ */
+public class AlignWaysGeomLineTest {
+    private AlignWaysGeomLine line_x1y1x2y2, line_par_x1y1x2y2;
+    private AlignWaysGeomLine line_abc;
+    private AlignWaysGeomLine line_mb;
+    private AlignWaysGeomLine line_line;
+    private AlignWaysGeomLine line_horiz, line_vert;
+
+    @Before
+    public void setUp() {
+        line_x1y1x2y2 = new AlignWaysGeomLine(-2.0, -1.0, 4.0, 3.0);
+        line_abc = new AlignWaysGeomLine(2.0/3, -1.0, 1.0/3);
+        line_mb = new AlignWaysGeomLine(2.0/3, 1.0/3);
+        line_line = new AlignWaysGeomLine(line_x1y1x2y2);
+        line_par_x1y1x2y2 = new AlignWaysGeomLine(-2.0, -2.0, 4.0, 2.0);
+        line_horiz = new AlignWaysGeomLine(-5.0, 3.0, 6.0, 3.0);
+        line_vert = new AlignWaysGeomLine(2.0, -3.0, 2.0, -10.5);
+    }
+
+    @Test
+    public void LineLineEquiv() {
+        assertTrue(line_x1y1x2y2.equals(line_line));
+    }
+
+    @Test
+    public void LineAbcLineEquiv() {
+        assertTrue(line_x1y1x2y2.equals(line_abc));
+    }
+
+    @Test
+    public void LineMbLineEquiv() {
+        assertTrue(line_x1y1x2y2.equals(line_mb));
+    }
+
+    @Test
+    public void LineAbcMbEquiv() {
+        assertTrue(line_abc.equals(line_mb));
+    }
+
+    @Test
+    public void LineLineParallel() {
+        line_x1y1x2y2.getIntersection(line_par_x1y1x2y2);
+        assertTrue(line_x1y1x2y2.getIntersectionStatus() == IntersectionStatus.LINES_PARALLEL);
+    }
+
+    @Test
+    public void LineAbcOverlap() {
+        line_x1y1x2y2.getIntersection(line_abc);
+        assertTrue(line_x1y1x2y2.getIntersectionStatus() == IntersectionStatus.LINES_OVERLAP);
+    }
+
+    @Test
+    public void LineMbOverlap() {
+        line_x1y1x2y2.getIntersection(line_mb);
+        assertTrue(line_x1y1x2y2.getIntersectionStatus() == IntersectionStatus.LINES_OVERLAP);
+    }
+
+    @Test
+    public void AbcMbOverlap() {
+        line_abc.getIntersection(line_mb);
+        assertTrue(line_abc.getIntersectionStatus() == IntersectionStatus.LINES_OVERLAP);
+    }
+
+    @Test
+    public void GetXOnHoriz() {
+        assertTrue(line_horiz.getXonLine(2.0).isNaN());
+    }
+
+    @Test
+    public void GetYOnVert() {
+        assertTrue(line_vert.getYonLine(2.0).isNaN());
+    }
+
+    @Test
+    public void StatusUndefAfterConstruct() throws Exception {
+        AlignWaysGeomLine newLine = new AlignWaysGeomLine(1.0, 2.0);
+        assertTrue(newLine.getIntersectionStatus() == IntersectionStatus.UNDEFINED);
+
+    }
+
+}
