Index: /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/AdvancedSettingsDialog.java
===================================================================
--- /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/AdvancedSettingsDialog.java	(revision 21845)
+++ /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/AdvancedSettingsDialog.java	(revision 21846)
@@ -18,4 +18,5 @@
 	JTextField tBTag = new JTextField();
 	JCheckBox cBigMode = new JCheckBox(tr("Big buildings mode"));
+	JCheckBox cSoftCur = new JCheckBox(tr("Rotate crosshair"));
 
 	public AdvancedSettingsDialog() {
@@ -29,7 +30,9 @@
 		addLabelled(panel, tr("Building tag:"), tBTag);
 		panel.add(cBigMode, GBC.eol().fill(GBC.HORIZONTAL));
+		panel.add(cSoftCur, GBC.eol().fill(GBC.HORIZONTAL));
 
 		tBTag.setText(ToolSettings.getTag());
 		cBigMode.setSelected(ToolSettings.isBBMode());
+		cSoftCur.setSelected(ToolSettings.isSoftCursor());
 
 		setContent(panel);
@@ -45,3 +48,13 @@
 		return cBigMode.isSelected();
 	}
+
+	public boolean isSoftCursor() {
+		return cSoftCur.isSelected();
+	}
+
+	public void saveSettings() {
+		ToolSettings.setTag(getTag());
+		ToolSettings.setBBMode(isBBMode());
+		ToolSettings.setSoftCursor(isSoftCursor());
+	}
 }
Index: /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/AngleSnap.java
===================================================================
--- /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/AngleSnap.java	(revision 21846)
+++ /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/AngleSnap.java	(revision 21846)
@@ -0,0 +1,100 @@
+package buildings_tools;
+
+import static buildings_tools.BuildingsToolsPlugin.latlon2eastNorth;
+
+import java.util.TreeSet;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.Pair;
+
+public class AngleSnap {
+	private static final double PI_2 = Math.PI / 2;
+	TreeSet<Double> snapSet = new TreeSet<Double>();
+
+	public void clear() {
+		snapSet.clear();
+	}
+
+	public void addSnap(double snap) {
+		snapSet.add(snap % PI_2);
+	}
+
+	public Double addSnap(Node[] nodes) {
+		if (nodes.length == 2) {
+			EastNorth p1, p2;
+			p1 = latlon2eastNorth(((Node) nodes[0]).getCoor());
+			p2 = latlon2eastNorth(((Node) nodes[1]).getCoor());
+			double heading = p1.heading(p2);
+			addSnap(heading);
+			addSnap(heading + Math.PI / 4);
+			return heading;
+		} else {
+			return null;
+		}
+	}
+
+	public void addSnap(Way way) {
+		for (Pair<Node, Node> pair : way.getNodePairs(false)) {
+			EastNorth a, b;
+			a = latlon2eastNorth(pair.a.getCoor());
+			b = latlon2eastNorth(pair.b.getCoor());
+			double heading = a.heading(b);
+			addSnap(heading);
+		}
+	}
+
+	public Double getAngle() {
+		if (snapSet.isEmpty()) {
+			return null;
+		}
+		double first = snapSet.first();
+		double last = snapSet.last();
+		if (first < Math.PI / 4 && last > Math.PI / 4) {
+			last -= PI_2;
+		}
+		if (Math.abs(first - last) < 0.001) {
+			double center = (first + last) / 2;
+			if (center < 0)
+				center += PI_2;
+			return center;
+		} else {
+			return null;
+		}
+	}
+
+	public double snapAngle(double angle) {
+		if (snapSet.isEmpty()) {
+			return angle;
+		}
+		int quadrant = (int) Math.floor(angle / PI_2);
+		double ang = angle % PI_2;
+		Double prev = snapSet.floor(ang);
+		if (prev == null)
+			prev = snapSet.last() - PI_2;
+		Double next = snapSet.ceiling(ang);
+		if (next == null)
+			next = snapSet.first() + PI_2;
+
+		if (Math.abs(ang - next) > Math.abs(ang - prev)) {
+			if (Math.abs(ang - prev) > Math.PI / 8) {
+				return angle;
+			} else {
+				double ret = prev + PI_2 * quadrant;
+				if (ret < 0)
+					ret += 2 * Math.PI;
+				return ret;
+			}
+		} else {
+			if (Math.abs(ang - next) > Math.PI / 8) {
+				return angle;
+			} else {
+				double ret = next + PI_2 * quadrant;
+				if (ret > 2 * Math.PI)
+					ret -= 2 * Math.PI;
+				return ret;
+			}
+		}
+	}
+}
Index: /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/Building.java
===================================================================
--- /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/Building.java	(revision 21845)
+++ /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/Building.java	(revision 21846)
@@ -36,14 +36,21 @@
 	private double width;
 	private double heading;
-	private boolean angConstrained;
-	private double angConstraint = 0;
-
-	public void disableAngConstraint() {
-		angConstrained = false;
-	}
-
-	public void setAngConstraint(double angle) {
-		angConstrained = true;
-		angConstraint = angle;
+	private AngleSnap angleSnap = new AngleSnap();
+	private Double drawingAngle;
+
+	public void clearAngleSnap() {
+		angleSnap.clear();
+		drawingAngle = null;
+	}
+
+	public void addAngleSnap(Node[] nodes) {
+		drawingAngle = angleSnap.addSnap(nodes);
+	}
+
+	public void addAngleSnap(Way way) {
+		angleSnap.addSnap(way);
+		if (drawingAngle == null) {
+			drawingAngle = angleSnap.getAngle();
+		}
 	}
 
@@ -57,5 +64,9 @@
 
 	public boolean isRectDrawing() {
-		return angConstrained && ToolSettings.getWidth() == 0 && ToolSettings.getLenStep() == 0;
+		return drawingAngle != null && ToolSettings.getWidth() == 0 && ToolSettings.getLenStep() == 0;
+	}
+
+	public Double getDrawingAngle() {
+		return drawingAngle;
 	}
 
@@ -108,9 +119,8 @@
 		final EastNorth p1 = en[0];
 		en[1] = new EastNorth(p1.east() + Math.sin(heading) * len * meter, p1.north() + Math.cos(heading) * len * meter);
-		en[2] = new EastNorth(p1.east() + Math.sin(heading) * len * meter + Math.cos(heading) * width * meter, p1
-				.north()
-				+ Math.cos(heading) * len * meter - Math.sin(heading) * width * meter);
-		en[3] = new EastNorth(p1.east() + Math.cos(heading) * width * meter, p1.north() - Math.sin(heading) * width
-				* meter);
+		en[2] = new EastNorth(p1.east() + Math.sin(heading) * len * meter + Math.cos(heading) * width * meter,
+				p1.north() + Math.cos(heading) * len * meter - Math.sin(heading) * width * meter);
+		en[3] = new EastNorth(p1.east() + Math.cos(heading) * width * meter,
+				p1.north() - Math.sin(heading) * width	* meter);
 	}
 
@@ -130,12 +140,6 @@
 			throw new IllegalStateException("setPlace() called without the base point");
 		this.heading = en[0].heading(p2);
-		double hdang = 0;
-		if (angConstrained && !ignoreConstraints) {
-			hdang = Math.round((heading - angConstraint) / Math.PI * 4);
-			hdang = hdang % 8;
-			if (hdang < 0)
-				hdang += 8;
-			heading = (hdang * Math.PI / 4 + angConstraint) % (2 * Math.PI);
-		}
+		if (!ignoreConstraints)
+			this.heading = angleSnap.snapAngle(this.heading);
 
 		this.width = width;
@@ -147,6 +151,11 @@
 
 		Main.map.statusLine.setHeading(Math.toDegrees(heading));
-		if (angConstrained && !ignoreConstraints) {
-			Main.map.statusLine.setAngle(hdang * 45);
+		if (this.drawingAngle != null && !ignoreConstraints) {
+			double ang = Math.toDegrees(heading - this.drawingAngle);
+			if (ang < 0)
+				ang += 360;
+			if (ang > 360)
+				ang -= 360;
+			Main.map.statusLine.setAngle(ang);
 		}
 	}
@@ -157,5 +166,5 @@
 		if (!isRectDrawing())
 			throw new IllegalStateException("Invalid drawing mode");
-		heading = angConstraint;
+		heading = drawingAngle;
 		setLengthWidth(projection1(p2), projection2(p2));
 		Main.map.statusLine.setHeading(Math.toDegrees(heading));
@@ -163,8 +172,11 @@
 
 	public void angFix(EastNorth point) {
-		EastNorth en3 = this.en[2];
-		heading = en[0].heading(point);
+		EastNorth en3 = en[2];
+		EastNorth mid = en[0].getCenter(en3);
+		double radius = en3.distance(mid);
+		heading = mid.heading(point);
+		heading = en[0].heading(mid.add(Math.sin(heading) * radius, Math.cos(heading) * radius));
 		setLengthWidth(projection1(en3), projection2(en3));
-		this.en[2] = en3;
+		en[2] = en3;
 	}
 
@@ -226,5 +238,5 @@
 		Way w = new Way();
 		w.addNode(nodes[0]);
-		if (projection1(en[2]) > 0) {
+		if (projection2(en[2]) > 0) {
 			w.addNode(nodes[1]);
 			w.addNode(nodes[2]);
Index: /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/BuildingSizeAction.java
===================================================================
--- /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/BuildingSizeAction.java	(revision 21845)
+++ /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/BuildingSizeAction.java	(revision 21846)
@@ -24,6 +24,5 @@
 		BuildingSizeDialog dlg = new BuildingSizeDialog();
 		if (dlg.getValue() == 1) {
-			ToolSettings.setSizes(dlg.width(), dlg.lenstep());
-			ToolSettings.setAddrDialog(dlg.useAddr());
+			dlg.saveSettings();
 		}
 	}
Index: /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/BuildingSizeDialog.java
===================================================================
--- /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/BuildingSizeDialog.java	(revision 21845)
+++ /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/BuildingSizeDialog.java	(revision 21846)
@@ -55,6 +55,5 @@
 				AdvancedSettingsDialog dlg = new AdvancedSettingsDialog();
 				if (dlg.getValue() == 1) {
-					ToolSettings.setTag(dlg.getTag());
-					ToolSettings.setBBMode(dlg.isBBMode());
+					dlg.saveSettings();
 				}
 			}
@@ -86,3 +85,8 @@
 		return caddr.isSelected();
 	}
+
+	public void saveSettings() {
+		ToolSettings.setSizes(width(), lenstep());
+		ToolSettings.setAddrDialog(useAddr());
+	}
 }
Index: /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/DrawBuildingAction.java
===================================================================
--- /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/DrawBuildingAction.java	(revision 21845)
+++ /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/DrawBuildingAction.java	(revision 21846)
@@ -13,9 +13,13 @@
 import java.awt.Graphics2D;
 import java.awt.Point;
+import java.awt.RenderingHints;
 import java.awt.Toolkit;
 import java.awt.event.AWTEventListener;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
+import java.awt.geom.GeneralPath;
+import java.awt.image.BufferedImage;
 import java.util.Collection;
+import java.util.LinkedList;
 
 import org.openstreetmap.josm.Main;
@@ -37,6 +41,5 @@
 
 @SuppressWarnings("serial")
-public class DrawBuildingAction extends MapMode
-		implements MapViewPaintable, AWTEventListener, SelectionChangedListener {
+public class DrawBuildingAction extends MapMode implements MapViewPaintable, AWTEventListener, SelectionChangedListener {
 	enum Mode {
 		None, Drawing, DrawingWidth, DrawingAngFix
@@ -46,4 +49,5 @@
 	final private Cursor cursorJoinNode;
 	private Cursor currCursor;
+	private Cursor customCursor;
 
 	private Mode mode = Mode.None;
@@ -51,6 +55,8 @@
 
 	private Color selectedColor;
+	private Point drawStartPos;
 	private Point mousePos;
-	private Point drawStartPos;
+	private boolean isCtrlDown;
+	private boolean isShiftDown;
 
 	Building building = new Building();
@@ -129,5 +135,5 @@
 		Main.map.mapView.addTemporaryLayer(this);
 		DataSet.selListeners.add(this);
-		updateConstraint(getCurrentDataSet().getSelected());
+		updateSnap(getCurrentDataSet().getSelected());
 		try {
 			Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
@@ -167,11 +173,25 @@
 			return;
 		KeyEvent ev = (KeyEvent) arg0;
-		if (ev.getKeyCode() == KeyEvent.VK_ESCAPE)
+		int modifiers = ev.getModifiersEx();
+		boolean isCtrlDown = (modifiers & KeyEvent.CTRL_DOWN_MASK) != 0;
+		boolean isShiftDown = (modifiers & KeyEvent.SHIFT_DOWN_MASK) != 0;
+		if (this.isCtrlDown != isCtrlDown || this.isShiftDown != isShiftDown) {
+			this.isCtrlDown = isCtrlDown;
+			this.isShiftDown = isShiftDown;
+			updCursor();
+		}
+		ev.getID();
+
+		if (ev.getKeyCode() == KeyEvent.VK_ESCAPE && ev.getID() == KeyEvent.KEY_PRESSED) {
+			if (mode != Mode.None)
+				ev.consume();
+
 			cancelDrawing();
-	}
-
-	private EastNorth getPoint(MouseEvent e) {
+		}
+	}
+
+	private EastNorth getEastNorth() {
 		Node n;
-		if (e.isControlDown()) {
+		if (isCtrlDown) {
 			n = null;
 		} else {
@@ -185,12 +205,15 @@
 	}
 
-	private Mode modeDrawing(MouseEvent e) {
-		EastNorth p = getPoint(e);
-		if (building.isRectDrawing() && (!e.isShiftDown() || ToolSettings.isBBMode())) {
+	private boolean isRectDrawing() {
+		return building.isRectDrawing() && (!isShiftDown || ToolSettings.isBBMode());
+	}
+
+	private Mode modeDrawing() {
+		EastNorth p = getEastNorth();
+		if (isRectDrawing()) {
 			building.setPlaceRect(p);
-			return e.isShiftDown() ? Mode.DrawingAngFix : Mode.None;
-		} else {
-			building.setPlace(p, ToolSettings.getWidth(),
-					ToolSettings.getLenStep(), e.isShiftDown());
+			return isShiftDown ? Mode.DrawingAngFix : Mode.None;
+		} else {
+			building.setPlace(p, ToolSettings.getWidth(), ToolSettings.getLenStep(), isShiftDown);
 			Main.map.statusLine.setDist(building.getLength());
 			return this.nextMode = ToolSettings.getWidth() == 0 ? Mode.DrawingWidth : Mode.None;
@@ -198,12 +221,12 @@
 	}
 
-	private Mode modeDrawingWidth(MouseEvent e) {
-		building.setWidth(getPoint(e));
+	private Mode modeDrawingWidth() {
+		building.setWidth(getEastNorth());
 		Main.map.statusLine.setDist(Math.abs(building.getWidth()));
 		return Mode.None;
 	}
 
-	private Mode modeDrawingAngFix(MouseEvent e) {
-		building.angFix(getPoint(e));
+	private Mode modeDrawingAngFix() {
+		building.angFix(getEastNorth());
 		return Mode.None;
 	}
@@ -211,4 +234,6 @@
 	private void processMouseEvent(MouseEvent e) {
 		mousePos = e.getPoint();
+		isCtrlDown = e.isControlDown();
+		isShiftDown = e.isShiftDown();
 		if (mode == Mode.None) {
 			nextMode = Mode.None;
@@ -217,9 +242,9 @@
 
 		if (mode == Mode.Drawing) {
-			nextMode = modeDrawing(e);
+			nextMode = modeDrawing();
 		} else if (mode == Mode.DrawingWidth) {
-			nextMode = modeDrawingWidth(e);
+			nextMode = modeDrawingWidth();
 		} else if (mode == Mode.DrawingAngFix) {
-			nextMode = modeDrawingAngFix(e);
+			nextMode = modeDrawingAngFix();
 		} else
 			throw new AssertionError("Invalid drawing mode");
@@ -315,9 +340,15 @@
 		if (mousePos == null)
 			return;
-		Node n = Main.map.mapView.getNearestNode(mousePos, OsmPrimitive.isUsablePredicate);
-		if (n != null)
+		Node n = null;
+		if (!isCtrlDown)
+			n = Main.map.mapView.getNearestNode(mousePos, OsmPrimitive.isUsablePredicate);
+		if (n != null) {
 			setCursor(cursorJoinNode);
-		else
-			setCursor(cursorCrosshair);
+		} else {
+			if (customCursor != null && (!isShiftDown || isRectDrawing()))
+				setCursor(customCursor);
+			else
+				setCursor(cursorCrosshair);
+		}
 
 	}
@@ -349,19 +380,64 @@
 	}
 
-	public void updateConstraint(Collection<? extends OsmPrimitive> newSelection) {
-		building.disableAngConstraint();
-		if (newSelection.size() != 2)
-			return;
-		Object[] arr = newSelection.toArray();
-		if (!(arr[0] instanceof Node && arr[1] instanceof Node))
-			return;
-		EastNorth p1, p2;
-		p1 = latlon2eastNorth(((Node) arr[0]).getCoor());
-		p2 = latlon2eastNorth(((Node) arr[1]).getCoor());
-		building.setAngConstraint(p1.heading(p2));
+	public void updateSnap(Collection<? extends OsmPrimitive> newSelection) {
+		building.clearAngleSnap();
+		// update snap only if selection isn't too big
+		if (newSelection.size() <= 10) {
+			LinkedList<Node> nodes = new LinkedList<Node>();
+			LinkedList<Way> ways = new LinkedList<Way>();
+
+			for (OsmPrimitive p : newSelection) {
+				switch (p.getType()) {
+				case NODE:
+					nodes.add((Node) p);
+					break;
+				case WAY:
+					ways.add((Way) p);
+					break;
+				}
+			}
+
+			building.addAngleSnap(nodes.toArray(new Node[0]));
+			for (Way w : ways) {
+				building.addAngleSnap(w);
+			}
+		}
+		updateCustomCursor();
+	}
+
+	private void updateCustomCursor() {
+		Double angle = building.getDrawingAngle();
+		if (angle == null || !ToolSettings.isSoftCursor()) {
+			customCursor = null;
+			return;
+		}
+		final int r = 11; // crosshair radius
+		BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
+		Graphics2D g = img.createGraphics();
+
+		GeneralPath b = new GeneralPath();
+		b.moveTo(16 - Math.cos(angle) * r, 16 - Math.sin(angle) * r);
+		b.lineTo(16 + Math.cos(angle) * r, 16 + Math.sin(angle) * r);
+		b.moveTo(16 + Math.sin(angle) * r, 16 - Math.cos(angle) * r);
+		b.lineTo(16 - Math.sin(angle) * r, 16 + Math.cos(angle) * r);
+
+		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+		g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+
+		g.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+		g.setColor(Color.WHITE);
+		g.draw(b);
+
+		g.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+		g.setColor(Color.BLACK);
+		g.draw(b);
+
+		customCursor = Toolkit.getDefaultToolkit().createCustomCursor(img, new Point(16, 16), "custom crosshair");
+
+		updCursor();
 	}
 
 	public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
-		updateConstraint(newSelection);
+		updateSnap(newSelection);
 	}
 }
Index: /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/ToolSettings.java
===================================================================
--- /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/ToolSettings.java	(revision 21845)
+++ /applications/editors/josm/plugins/buildings_tools/src/buildings_tools/ToolSettings.java	(revision 21846)
@@ -1,3 +1,5 @@
 package buildings_tools;
+
+import org.openstreetmap.josm.Main;
 
 public class ToolSettings {
@@ -6,5 +8,4 @@
 	private static boolean useAddr;
 	private static String tag = "yes";
-	private static boolean bbMode;
 
 	public static void setAddrDialog(boolean _useAddr) {
@@ -37,10 +38,18 @@
 	}
 
-	public static void setBBMode(boolean _bbmode) {
-		bbMode = _bbmode;
+	public static void setBBMode(boolean bbmode) {
+		Main.pref.put("buildings_tools.bbmode", bbmode);
 	}
 
 	public static boolean isBBMode() {
-		return bbMode;
+		return Main.pref.getBoolean("buildings_tools.bbmode", false);
+	}
+
+	public static void setSoftCursor(boolean softCursor) {
+		Main.pref.put("buildings_tools.softcursor", softCursor);
+	}
+
+	public static boolean isSoftCursor() {
+		return Main.pref.getBoolean("buildings_tools.softcursor", false);
 	}
 }
