Index: applications/editors/josm/plugins/buildings_tools/build.xml
===================================================================
--- applications/editors/josm/plugins/buildings_tools/build.xml	(revision 34571)
+++ applications/editors/josm/plugins/buildings_tools/build.xml	(revision 34572)
@@ -10,5 +10,5 @@
     -->
     <property name="plugin.author" value="Upliner"/>
-    <property name="plugin.class" value="buildings_tools.BuildingsToolsPlugin"/>
+    <property name="plugin.class" value="org.openstreetmap.josm.plugins.buildings_tools.BuildingsToolsPlugin"/>
     <property name="plugin.description" value="Tools for drawing buildings."/>
     <property name="plugin.icon" value="images/mapmode/building.png"/>
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AddressDialog.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AddressDialog.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AddressDialog.java	(revision 34572)
@@ -0,0 +1,61 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.SpinnerNumberModel;
+
+public class AddressDialog extends MyDialog {
+    private static String lhousenum, lstreetname;
+    private static int inc = 0;
+    private JTextField housenum = new JTextField();
+    private JTextField streetname = new JTextField();
+    private JSpinner incSpinner;
+
+    public AddressDialog() {
+        super(tr("Building address"));
+
+        addLabelled(tr("House number:"), housenum);
+        addLabelled(tr("Street Name:"), streetname);
+        housenum.setText(nextHouseNum());
+        streetname.setText(lstreetname);
+
+        SpinnerNumberModel inc_model = new SpinnerNumberModel(0, Integer.MIN_VALUE, Integer.MAX_VALUE, 1);
+        incSpinner = new JSpinner(inc_model);
+        incSpinner.setValue(inc);
+        addLabelled(tr("House number increment:"), incSpinner);
+
+        setContent(panel);
+        setupDialog();
+    }
+
+    private static String nextHouseNum() {
+        if (lhousenum == null)
+            return "";
+        try {
+            Integer num = NumberFormat.getInstance().parse(lhousenum).intValue() + inc;
+            return num.toString();
+        } catch (ParseException e) {
+            return lhousenum;
+        }
+    }
+
+    public final void saveValues() {
+        lhousenum = housenum.getText();
+        lstreetname = streetname.getText();
+        inc = (Integer) incSpinner.getValue();
+    }
+
+    public final String getHouseNum() {
+        return housenum.getText();
+    }
+
+    public final String getStreetName() {
+        return streetname.getText();
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AdvancedSettingsDialog.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AdvancedSettingsDialog.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AdvancedSettingsDialog.java	(revision 34572)
@@ -0,0 +1,47 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Map.Entry;
+
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.gui.tagging.TagEditorModel;
+import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
+import org.openstreetmap.josm.tools.GBC;
+
+public class AdvancedSettingsDialog extends MyDialog {
+    private final TagEditorModel tagsModel = new TagEditorModel();
+
+    private final JCheckBox cBigMode = new JCheckBox(tr("Big buildings mode"));
+    private final JCheckBox cSoftCur = new JCheckBox(tr("Rotate crosshair"));
+
+    public AdvancedSettingsDialog() {
+        super(tr("Advanced settings"));
+
+        panel.add(new JLabel(tr("Buildings tags:")), GBC.eol().fill(GBC.HORIZONTAL));
+
+        for (Entry<String, String> entry : ToolSettings.getTags().entrySet()) {
+            tagsModel.add(entry.getKey(), entry.getValue());
+        }
+        panel.add(new TagEditorPanel(tagsModel, null, Changeset.MAX_CHANGESET_TAG_LENGTH), GBC.eop().fill(GBC.BOTH));
+
+        panel.add(cBigMode, GBC.eol().fill(GBC.HORIZONTAL));
+        panel.add(cSoftCur, GBC.eol().fill(GBC.HORIZONTAL));
+
+        cBigMode.setSelected(ToolSettings.isBBMode());
+        cSoftCur.setSelected(ToolSettings.isSoftCursor());
+
+        setupDialog();
+        showDialog();
+    }
+
+    public final void saveSettings() {
+        ToolSettings.saveTags(tagsModel.getTags());
+        ToolSettings.setBBMode(cBigMode.isSelected());
+        ToolSettings.setSoftCursor(cSoftCur.isSelected());
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AngleSnap.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AngleSnap.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/AngleSnap.java	(revision 34572)
@@ -0,0 +1,101 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.plugins.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;
+    final TreeSet<Double> snapSet = new TreeSet<>();
+
+    public final void clear() {
+        snapSet.clear();
+    }
+
+    public final void addSnap(double snap) {
+        snapSet.add(snap % PI_2);
+    }
+
+    public final Double addSnap(Node[] nodes) {
+        if (nodes.length == 2) {
+            EastNorth p1, p2;
+            p1 = latlon2eastNorth(nodes[0].getCoor());
+            p2 = latlon2eastNorth(nodes[1].getCoor());
+            double heading = p1.heading(p2);
+            addSnap(heading);
+            addSnap(heading + Math.PI / 4);
+            return heading;
+        } else {
+            return null;
+        }
+    }
+
+    public final 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 final 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 final 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/org/openstreetmap/josm/plugins/buildings_tools/Building.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/Building.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/Building.java	(revision 34572)
@@ -0,0 +1,430 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.plugins.buildings_tools.BuildingsToolsPlugin.eastNorth2latlon;
+import static org.openstreetmap.josm.plugins.buildings_tools.BuildingsToolsPlugin.latlon2eastNorth;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.geom.GeneralPath;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map.Entry;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.actions.CreateCircleAction;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.UndoRedoHandler;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.BBox;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MapView;
+
+class Building {
+    private final EastNorth[] en = new EastNorth[4];
+
+    double meter = 0;
+
+    private double len = 0;
+    private double width;
+    private double heading;
+    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();
+        }
+    }
+
+    public double getLength() {
+        return len;
+    }
+
+    public double getWidth() {
+        return width;
+    }
+
+    public boolean isRectDrawing() {
+        return drawingAngle != null && ToolSettings.getWidth() == 0 && ToolSettings.getLenStep() == 0
+                && ToolSettings.Shape.RECTANGLE.equals(ToolSettings.getShape());
+    }
+
+    public Double getDrawingAngle() {
+        return drawingAngle;
+    }
+
+    public void reset() {
+        len = 0;
+
+        for (int i = 0; i < 4; i++) {
+            en[i] = null;
+        }
+    }
+
+    public EastNorth getPoint(int num) {
+        return en[num];
+    }
+
+    private void updMetrics() {
+        meter = 1 / Math.cos(Math.toRadians(eastNorth2latlon(en[0]).lat()));
+        len = 0;
+    }
+
+    public void setBase(EastNorth base) {
+        en[0] = base;
+        updMetrics();
+    }
+
+    public void setBase(Node base) {
+        en[0] = latlon2eastNorth(base.getCoor());
+        updMetrics();
+    }
+
+    /**
+     * @param p
+     *            The point to project
+     * @return Projection of the point to the heading vector in metres
+     */
+    private double projection1(EastNorth p) {
+        final EastNorth vec = p.subtract(en[0]);
+        return (Math.sin(heading) * vec.east() + Math.cos(heading) * vec.north()) / meter;
+    }
+
+    /**
+     * @param p
+     *            The point to project
+     * @return Projection of the point to the perpendicular of the heading
+     *         vector in metres
+     */
+    private double projection2(EastNorth p) {
+        final EastNorth vec = p.subtract(en[0]);
+        return (Math.cos(heading) * vec.east() - Math.sin(heading) * vec.north()) / meter;
+    }
+
+    private void updatePos() {
+        if (len == 0)
+            return;
+        final EastNorth p1 = en[0];
+        en[1] = new EastNorth(p1.east() + Math.sin(heading) * len * meter, p1.north() + Math.cos(heading) * len * meter);
+        if (ToolSettings.Shape.RECTANGLE.equals(ToolSettings.getShape())) {
+            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);
+        }
+    }
+
+    public void setLengthWidth(double length, double width) {
+        this.len = length;
+        this.width = width;
+        updatePos();
+    }
+
+    public void setWidth(EastNorth p3) {
+        this.width = projection2(p3);
+        updatePos();
+    }
+
+    public void setPlace(EastNorth p2, double width, double lenstep, boolean ignoreConstraints) {
+        if (en[0] == null)
+            throw new IllegalStateException("setPlace() called without the base point");
+        this.heading = en[0].heading(p2);
+        if (!ignoreConstraints)
+            this.heading = angleSnap.snapAngle(this.heading);
+
+        this.width = width;
+        this.len = projection1(p2);
+        if (lenstep > 0 && !ignoreConstraints)
+            this.len = Math.round(this.len / lenstep) * lenstep;
+
+        updatePos();
+
+        MainApplication.getMap().statusLine.setHeading(Math.toDegrees(heading));
+        if (this.drawingAngle != null && !ignoreConstraints) {
+            double ang = Math.toDegrees(heading - this.drawingAngle);
+            if (ang < 0)
+                ang += 360;
+            if (ang > 360)
+                ang -= 360;
+            MainApplication.getMap().statusLine.setAngle(ang);
+        }
+    }
+
+    public void setPlaceCircle(EastNorth p2, double width, boolean ignoreConstraints) {
+        if (en[0] == null)
+            throw new IllegalStateException("setPlace() called without the base point");
+        this.heading = en[0].heading(p2);
+        if (!ignoreConstraints)
+            this.heading = angleSnap.snapAngle(this.heading);
+        this.len = width;
+
+        updatePos();
+    }
+
+    public void setPlaceRect(EastNorth p2) {
+        if (en[0] == null)
+            throw new IllegalStateException("SetPlaceRect() called without the base point");
+        if (!isRectDrawing())
+            throw new IllegalStateException("Invalid drawing mode");
+        heading = drawingAngle;
+        setLengthWidth(projection1(p2), projection2(p2));
+        MainApplication.getMap().statusLine.setHeading(Math.toDegrees(heading));
+    }
+
+    public void angFix(EastNorth 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));
+        en[2] = en3;
+    }
+
+    public void paint(Graphics2D g, MapView mv) {
+        if (len == 0)
+            return;
+        GeneralPath b = new GeneralPath();
+        Point pp1 = mv.getPoint(eastNorth2latlon(en[0]));
+        Point pp2 = mv.getPoint(eastNorth2latlon(en[1]));
+        b.moveTo(pp1.x, pp1.y);
+        b.lineTo(pp2.x, pp2.y);
+        if (ToolSettings.Shape.RECTANGLE.equals(ToolSettings.getShape())) {
+            Point pp3 = mv.getPoint(eastNorth2latlon(en[2]));
+            Point pp4 = mv.getPoint(eastNorth2latlon(en[3]));
+            b.lineTo(pp3.x, pp3.y);
+            b.lineTo(pp4.x, pp4.y);
+        }
+        b.lineTo(pp1.x, pp1.y);
+        g.draw(b);
+    }
+
+    private Node findNode(EastNorth pos) {
+        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
+        LatLon l = eastNorth2latlon(pos);
+        List<Node> nodes = ds.searchNodes(new BBox(l.lon() - 0.0000001, l.lat() - 0.0000001,
+                l.lon() + 0.0000001, l.lat() + 0.0000001));
+        Node bestnode = null;
+        double mindist = 0.0003;
+        for (Node n : nodes) {
+            double dist = n.getCoor().distanceSq(l);
+            if (dist < mindist && n.isUsable()) {
+                bestnode = n;
+                mindist = dist;
+            }
+        }
+        return bestnode;
+    }
+
+    /**
+     * Returns a node with address tags under the building.
+     *
+     * @return A node with address tags under the building.
+     */
+    private Node getAddressNode() {
+        BBox bbox = new BBox(eastNorth2latlon(en[0]), eastNorth2latlon(en[1]));
+        if (ToolSettings.Shape.RECTANGLE.equals(ToolSettings.getShape())) {
+            bbox.add(eastNorth2latlon(en[2]));
+            bbox.add(eastNorth2latlon(en[3]));
+        }
+        List<Node> nodes = new LinkedList<>();
+        nodesloop:
+        for (Node n : MainApplication.getLayerManager().getEditDataSet().searchNodes(bbox)) {
+            if (!n.isUsable())
+                continue;
+            tagcheck: do {
+                for (String key : n.getKeys().keySet()) {
+                    if (key.equals("building") || key.startsWith("addr:"))
+                        break tagcheck;
+                }
+                continue nodesloop;
+            } while (false);
+            double x = projection1(latlon2eastNorth(n.getCoor()));
+            double y = projection2(latlon2eastNorth(n.getCoor()));
+            if (Math.signum(x) != Math.signum(len) || Math.signum(y) != Math.signum(width))
+                continue;
+            if (Math.abs(x) > Math.abs(len) || Math.abs(y) > Math.abs(width))
+                continue;
+            for (OsmPrimitive p : n.getReferrers()) {
+                // Don't use nodes if they're referenced by ways
+                if (p.getType() == OsmPrimitiveType.WAY)
+                    continue nodesloop;
+            }
+            nodes.add(n);
+        }
+        if (nodes.size() != 1)
+            return null;
+        return nodes.get(0);
+    }
+
+    public Way createCircle() {
+        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
+        Collection<OsmPrimitive> selectedPrimitives = ds.getAllSelected();
+        ds.clearSelection();
+
+        if (len == 0)
+            return null;
+        final boolean[] created = new boolean[2];
+        final Node[] nodes = new Node[2];
+        for (int i = 0; i < 2; i++) {
+
+            Node n = findNode(en[i]);
+            if (n == null) {
+                nodes[i] = new Node(eastNorth2latlon(en[i]));
+                created[i] = true;
+            } else {
+                nodes[i] = n;
+                created[i] = false;
+            }
+            if (nodes[i].getCoor().isOutSideWorld()) {
+                JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
+                        tr("Cannot place building outside of the world."));
+                return null;
+            }
+        }
+        Way w = new Way();
+        w.addNode(nodes[0]);
+        w.addNode(nodes[1]);
+
+        Collection<Command> addNodesCmd = new LinkedList<>();
+
+        for (int i = 0; i < 2; i++) {
+            if (created[i]) {
+                AddCommand addNode = new AddCommand(ds, nodes[i]);
+                addNodesCmd.add(addNode);
+            }
+        }
+
+        if (addNodesCmd.size() > 0) {
+            Command addNodes = new SequenceCommand(tr("Add nodes for building"), addNodesCmd);
+            UndoRedoHandler.getInstance().add(addNodes);
+        }
+
+        // Nodes must be selected for create circle action
+        for (int i = 0; i < 2; i++) {
+            if (created[i]) {
+                ds.addSelected(nodes[i]);
+            }
+        }
+
+        CreateCircleAction action = new CreateCircleAction();
+        action.setEnabled(true);
+        action.actionPerformed(null);
+
+        ds.clearSelection();
+        ds.addSelected(selectedPrimitives);
+
+        // get the way with the smallest id with the assumption that it is
+        // newest way created by CreateCirclAction
+        List<Way> ways = new ArrayList<>(ds.getWays());
+        Collections.sort(ways);
+        w = ways.get(0);
+
+        addAddress(w);
+
+        return w;
+    }
+
+    public Way createRectangle() {
+        if (len == 0)
+            return null;
+        final boolean[] created = new boolean[4];
+        final Node[] nodes = new Node[4];
+        for (int i = 0; i < 4; i++) {
+
+            Node n = findNode(en[i]);
+            if (n == null) {
+                nodes[i] = new Node(eastNorth2latlon(en[i]));
+                created[i] = true;
+            } else {
+                nodes[i] = n;
+                created[i] = false;
+            }
+            if (nodes[i].getCoor().isOutSideWorld()) {
+                JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
+                        tr("Cannot place building outside of the world."));
+                return null;
+            }
+        }
+        Way w = new Way();
+        w.addNode(nodes[0]);
+        if (projection2(en[2]) > 0 ^ len < 0) {
+            w.addNode(nodes[1]);
+            w.addNode(nodes[2]);
+            w.addNode(nodes[3]);
+        } else {
+            w.addNode(nodes[3]);
+            w.addNode(nodes[2]);
+            w.addNode(nodes[1]);
+        }
+        w.addNode(nodes[0]);
+        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
+        Collection<Command> cmds = new LinkedList<>();
+        for (int i = 0; i < 4; i++) {
+            if (created[i])
+                cmds.add(new AddCommand(ds, nodes[i]));
+        }
+        cmds.add(new AddCommand(ds, w));
+
+        addAddress(w);
+
+        Command c = new SequenceCommand(tr("Create building"), cmds);
+        UndoRedoHandler.getInstance().add(c);
+        return w;
+    }
+
+    private void addAddress(Way w) {
+        if (ToolSettings.PROP_USE_ADDR_NODE.get()) {
+            Node addrNode = getAddressNode();
+            if (addrNode != null) {
+                Collection<Command> addressCmds = new LinkedList<>();
+                for (Entry<String, String> entry : addrNode.getKeys().entrySet()) {
+                    w.put(entry.getKey(), entry.getValue());
+                }
+                for (OsmPrimitive p : addrNode.getReferrers()) {
+                    Relation r = (Relation) p;
+                    Relation rnew = new Relation(r);
+                    for (int i = 0; i < r.getMembersCount(); i++) {
+                        RelationMember member = r.getMember(i);
+                        if (addrNode.equals(member.getMember())) {
+                            rnew.removeMember(i);
+                            rnew.addMember(i, new RelationMember(member.getRole(), w));
+                        }
+                    }
+                    addressCmds.add(new ChangeCommand(r, rnew));
+                }
+                addressCmds.add(new DeleteCommand(addrNode));
+                Command c = new SequenceCommand(tr("Add address for building"), addressCmds);
+                UndoRedoHandler.getInstance().add(c);
+            }
+        }
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingCircleAction.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingCircleAction.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingCircleAction.java	(revision 34572)
@@ -0,0 +1,26 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class BuildingCircleAction extends JosmAction {
+
+    public BuildingCircleAction() {
+        super(tr("Set building shape to circle"), "mapmode/silo", tr("Set building shape to circle"),
+                Shortcut.registerShortcut("buildings_tools:circle",
+                        tr("Data: {0}", tr("Set buildings shape to circle")),
+                        KeyEvent.VK_Z, Shortcut.ALT),
+                true);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        ToolSettings.saveShape(ToolSettings.Shape.CIRCLE);
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingRectangleAction.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingRectangleAction.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingRectangleAction.java	(revision 34572)
@@ -0,0 +1,26 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class BuildingRectangleAction extends JosmAction {
+
+    public BuildingRectangleAction() {
+        super(tr("Set building shape to rectangle"), "mapmode/rectangular", tr("Set building shape to rectangle"),
+                Shortcut.registerShortcut("buildings_tools:rectangle",
+                        tr("Data: {0}", tr("Set buildings shape to rectangle")),
+                        KeyEvent.VK_R, Shortcut.ALT),
+                true);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        ToolSettings.saveShape(ToolSettings.Shape.RECTANGLE);
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingSizeAction.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingSizeAction.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingSizeAction.java	(revision 34572)
@@ -0,0 +1,28 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class BuildingSizeAction extends JosmAction {
+
+    public BuildingSizeAction() {
+        super(tr("Set buildings size"), "mapmode/building", tr("Set buildings size"),
+                Shortcut.registerShortcut("edit:buildingsdialog", tr("Edit: {0}", tr("Set buildings size")),
+                KeyEvent.VK_B, Shortcut.ALT_CTRL),
+                true);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent arg0) {
+        BuildingSizeDialog dlg = new BuildingSizeDialog();
+        if (dlg.getValue() == 1) {
+            dlg.saveSettings();
+        }
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingSizeDialog.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingSizeDialog.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingSizeDialog.java	(revision 34572)
@@ -0,0 +1,99 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JFormattedTextField;
+import javax.swing.JRadioButton;
+
+import org.openstreetmap.josm.tools.GBC;
+
+public class BuildingSizeDialog extends MyDialog {
+    private final JFormattedTextField twidth = new JFormattedTextField(NumberFormat.getInstance());
+    private final JFormattedTextField tlenstep = new JFormattedTextField(NumberFormat.getInstance());
+    private final JCheckBox caddr = new JCheckBox(tr("Use Address dialog"));
+    private final JCheckBox cAutoSelect = new JCheckBox(tr("Auto-select building"));
+    private final JCheckBox cAddrNode = new JCheckBox(tr("Use address nodes under buildings"));
+    private final JRadioButton circleRadio = new JRadioButton(tr("Circle"));
+    private final JRadioButton rectangleRadio = new JRadioButton(tr("Rectangle"));
+
+    public BuildingSizeDialog() {
+        super(tr("Set buildings size and shape"));
+
+        ButtonGroup shapeGroup = new ButtonGroup();
+        shapeGroup.add(circleRadio);
+        shapeGroup.add(rectangleRadio);
+        circleRadio.setSelected(ToolSettings.Shape.CIRCLE.equals(ToolSettings.getShape()));
+        rectangleRadio.setSelected(ToolSettings.Shape.RECTANGLE.equals(ToolSettings.getShape()));
+
+        panel.add(rectangleRadio, GBC.eol().fill(GBC.HORIZONTAL));
+        panel.add(circleRadio, GBC.eol().fill(GBC.HORIZONTAL));
+
+        addLabelled(tr("Buildings width/diameter:"), twidth);
+        addLabelled(tr("Length step:"), tlenstep);
+        panel.add(caddr, GBC.eol().fill(GBC.HORIZONTAL));
+        panel.add(cAutoSelect, GBC.eol().fill(GBC.HORIZONTAL));
+        panel.add(cAddrNode, GBC.eol().fill(GBC.HORIZONTAL));
+
+        twidth.setValue(ToolSettings.getWidth());
+        tlenstep.setValue(ToolSettings.getLenStep());
+        caddr.setSelected(ToolSettings.isUsingAddr());
+        cAutoSelect.setSelected(ToolSettings.isAutoSelect());
+        cAddrNode.setSelected(ToolSettings.PROP_USE_ADDR_NODE.get());
+
+        JButton bAdv = new JButton(tr("Advanced..."));
+        bAdv.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent arg0) {
+                AdvancedSettingsDialog dlg = new AdvancedSettingsDialog();
+                if (dlg.getValue() == 1) {
+                    dlg.saveSettings();
+                }
+            }
+        });
+        panel.add(bAdv, GBC.eol().insets(0, 5, 0, 0).anchor(GBC.EAST));
+
+        setupDialog();
+        showDialog();
+    }
+
+    public final double width() {
+        try {
+            return NumberFormat.getInstance().parse(twidth.getText()).doubleValue();
+        } catch (ParseException e) {
+            return 0;
+        }
+    }
+
+    public double lenstep() {
+        try {
+            return NumberFormat.getInstance().parse(tlenstep.getText()).doubleValue();
+        } catch (ParseException e) {
+            return 0;
+        }
+    }
+
+    public final boolean useAddr() {
+        return caddr.isSelected();
+    }
+
+    public final void saveSettings() {
+        if (circleRadio.isSelected()) {
+            ToolSettings.saveShape(ToolSettings.Shape.CIRCLE);
+        } else if (rectangleRadio.isSelected()) {
+            ToolSettings.saveShape(ToolSettings.Shape.RECTANGLE);
+        }
+        ToolSettings.setSizes(width(), lenstep());
+        ToolSettings.setAddrDialog(useAddr());
+        ToolSettings.setAutoSelect(cAutoSelect.isSelected());
+        ToolSettings.PROP_USE_ADDR_NODE.put(cAddrNode.isSelected());
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingsToolsPlugin.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingsToolsPlugin.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/BuildingsToolsPlugin.java	(revision 34572)
@@ -0,0 +1,43 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import javax.swing.JMenu;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.Projections;
+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;
+
+public class BuildingsToolsPlugin extends Plugin {
+    public static final Projection MERCATOR = Projections.getProjectionByCode("EPSG:3857"); // Mercator
+
+    public static EastNorth latlon2eastNorth(LatLon p) {
+        return MERCATOR.latlon2eastNorth(p);
+    }
+
+    public static LatLon eastNorth2latlon(EastNorth p) {
+        return MERCATOR.eastNorth2latlon(p);
+    }
+
+    public BuildingsToolsPlugin(PluginInformation info) {
+        super(info);
+        JMenu dataMenu = MainApplication.getMenu().dataMenu;
+        MainMenu.add(dataMenu, new BuildingSizeAction());
+        MainMenu.add(dataMenu, new BuildingCircleAction());
+        MainMenu.add(dataMenu, new BuildingRectangleAction());
+        MainMenu.add(dataMenu, new MergeAddrPointsAction());
+    }
+
+    @Override
+    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+        if (oldFrame == null && newFrame != null) {
+            MainApplication.getMap().addMapMode(new IconToggleButton(new DrawBuildingAction()));
+        }
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/DrawBuildingAction.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/DrawBuildingAction.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/DrawBuildingAction.java	(revision 34572)
@@ -0,0 +1,495 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.plugins.buildings_tools.BuildingsToolsPlugin.latlon2eastNorth;
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.EventQueue;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.Toolkit;
+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 java.util.Map.Entry;
+
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.DataSelectionListener;
+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.event.SelectionEventManager;
+import org.openstreetmap.josm.data.preferences.NamedColorProperty;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.util.KeyPressReleaseListener;
+import org.openstreetmap.josm.gui.util.ModifierExListener;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class DrawBuildingAction extends MapMode implements MapViewPaintable, DataSelectionListener,
+        KeyPressReleaseListener, ModifierExListener {
+    private enum Mode {
+        None, Drawing, DrawingWidth, DrawingAngFix
+    }
+
+    private final Cursor cursorCrosshair;
+    private final Cursor cursorJoinNode;
+    private Cursor currCursor;
+    private Cursor customCursor;
+
+    private Mode mode = Mode.None;
+    private Mode nextMode = Mode.None;
+
+    private Color selectedColor = Color.red;
+    private Point drawStartPos;
+    private Point mousePos;
+
+    final Building building = new Building();
+
+    public DrawBuildingAction() {
+        super(tr("Draw buildings"), "building", tr("Draw buildings"),
+                Shortcut.registerShortcut("mapmode:buildings",
+                        tr("Mode: {0}", tr("Draw buildings")),
+                        KeyEvent.VK_B, Shortcut.DIRECT),
+                getCursor());
+
+        cursorCrosshair = getCursor();
+        cursorJoinNode = ImageProvider.getCursor("crosshair", "joinnode");
+        currCursor = cursorCrosshair;
+    }
+
+    private static Cursor getCursor() {
+        try {
+            if (ToolSettings.Shape.CIRCLE.equals(ToolSettings.getShape())) {
+                return ImageProvider.getCursor("crosshair", "silo");
+            } else {
+                return ImageProvider.getCursor("crosshair", "building");
+            }
+        } catch (Exception e) {
+            Logging.error(e);
+        }
+        return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
+    }
+
+    /**
+     * Displays the given cursor instead of the normal one.
+     *
+     * @param c One of the available cursors
+     */
+    private void setCursor(final Cursor c) {
+        if (currCursor.equals(c))
+            return;
+        try {
+            // We invoke this to prevent strange things from happening
+            EventQueue.invokeLater(() -> {
+                MapFrame map = MainApplication.getMap();
+                // Don't change cursor when mode has changed already
+                if (!(map.mapMode instanceof DrawBuildingAction))
+                    return;
+                map.mapView.setCursor(c);
+            });
+            currCursor = c;
+        } catch (Exception e) {
+            Logging.error(e);
+        }
+    }
+
+    private void showAddrDialog(Way w) {
+        AddressDialog dlg = new AddressDialog();
+        if (!alt) {
+            dlg.showDialog();
+            if (dlg.getValue() != 1)
+                return;
+        }
+        dlg.saveValues();
+        String tmp = dlg.getHouseNum();
+        if (tmp != null && !tmp.isEmpty())
+            w.put("addr:housenumber", tmp);
+        tmp = dlg.getStreetName();
+        if (tmp != null && !tmp.isEmpty())
+            w.put("addr:street", tmp);
+    }
+
+    @Override
+    public void enterMode() {
+        super.enterMode();
+        MapFrame map = MainApplication.getMap();
+        if (getLayerManager().getEditDataSet() == null) {
+            map.selectSelectTool(false);
+            return;
+        }
+        selectedColor = new NamedColorProperty(marktr("selected"), selectedColor).get();
+        currCursor = cursorCrosshair;
+        map.mapView.addMouseListener(this);
+        map.mapView.addMouseMotionListener(this);
+        map.mapView.addTemporaryLayer(this);
+        map.keyDetector.addKeyListener(this);
+        map.keyDetector.addModifierExListener(this);
+        SelectionEventManager.getInstance().addSelectionListener(this);
+        updateSnap(getLayerManager().getEditDataSet().getSelected());
+    }
+
+    @Override
+    public void exitMode() {
+        super.exitMode();
+        MapFrame map = MainApplication.getMap();
+        map.mapView.removeMouseListener(this);
+        map.mapView.removeMouseMotionListener(this);
+        map.mapView.removeTemporaryLayer(this);
+        map.keyDetector.removeKeyListener(this);
+        map.keyDetector.removeModifierExListener(this);
+        SelectionEventManager.getInstance().removeSelectionListener(this);
+        if (mode != Mode.None)
+            map.mapView.repaint();
+        mode = Mode.None;
+    }
+
+    public final void cancelDrawing() {
+        mode = Mode.None;
+        MapFrame map = MainApplication.getMap();
+        if (map == null || map.mapView == null)
+            return;
+        map.statusLine.setHeading(-1);
+        map.statusLine.setAngle(-1);
+        building.reset();
+        map.mapView.repaint();
+        updateStatusLine();
+    }
+
+    @Override
+    public void modifiersExChanged(int modifiers) {
+        boolean oldCtrl = ctrl;
+        boolean oldShift = shift;
+        updateKeyModifiersEx(modifiers);
+        if (ctrl != oldCtrl || shift != oldShift) {
+            processMouseEvent(null);
+            updCursor();
+            if (mode != Mode.None)
+                MainApplication.getMap().mapView.repaint();
+        }
+    }
+
+    @Override
+    public void doKeyPressed(KeyEvent e) {
+        if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+            if (mode != Mode.None)
+                e.consume();
+
+            cancelDrawing();
+        }
+    }
+
+    @Override
+    public void doKeyReleased(KeyEvent e) {
+    }
+
+    private EastNorth getEastNorth() {
+        Node n;
+        if (ctrl) {
+            n = null;
+        } else {
+            n = MainApplication.getMap().mapView.getNearestNode(mousePos, OsmPrimitive::isUsable);
+        }
+        if (n == null) {
+            return latlon2eastNorth(MainApplication.getMap().mapView.getLatLon(mousePos.x, mousePos.y));
+        } else {
+            return latlon2eastNorth(n.getCoor());
+        }
+    }
+
+    private boolean isRectDrawing() {
+        return building.isRectDrawing() && (!shift || ToolSettings.isBBMode())
+                && ToolSettings.Shape.RECTANGLE.equals(ToolSettings.getShape());
+    }
+
+    private Mode modeDrawing() {
+        EastNorth p = getEastNorth();
+        if (isRectDrawing()) {
+                building.setPlaceRect(p);
+                return shift ? Mode.DrawingAngFix : Mode.None;
+        } else if (ToolSettings.Shape.CIRCLE.equals(ToolSettings.getShape())) {
+            if (ToolSettings.getWidth() != 0) {
+                building.setPlaceCircle(p, ToolSettings.getWidth(), shift);
+            } else {
+                building.setPlace(p, ToolSettings.getWidth(), ToolSettings.getLenStep(), shift);
+            }
+            MainApplication.getMap().statusLine.setDist(building.getLength());
+            this.nextMode = Mode.None;
+            return this.nextMode;
+        } else {
+            building.setPlace(p, ToolSettings.getWidth(), ToolSettings.getLenStep(), shift);
+            MainApplication.getMap().statusLine.setDist(building.getLength());
+            this.nextMode = ToolSettings.getWidth() == 0 ? Mode.DrawingWidth : Mode.None;
+            return this.nextMode;
+        }
+    }
+
+    private Mode modeDrawingWidth() {
+        building.setWidth(getEastNorth());
+        MainApplication.getMap().statusLine.setDist(Math.abs(building.getWidth()));
+        return Mode.None;
+    }
+
+    private Mode modeDrawingAngFix() {
+        building.angFix(getEastNorth());
+        return Mode.None;
+    }
+
+    private void processMouseEvent(MouseEvent e) {
+        if (e != null) {
+            mousePos = e.getPoint();
+            updateKeyModifiers(e);
+        }
+        if (mode == Mode.None) {
+            nextMode = Mode.None;
+            return;
+        }
+
+        if (mode == Mode.Drawing) {
+            nextMode = modeDrawing();
+        } else if (mode == Mode.DrawingWidth) {
+            nextMode = modeDrawingWidth();
+        } else if (mode == Mode.DrawingAngFix) {
+            nextMode = modeDrawingAngFix();
+        } else {
+            throw new AssertionError("Invalid drawing mode");
+        }
+    }
+
+    @Override
+    public void paint(Graphics2D g, MapView mv, Bounds bbox) {
+        if (mode == Mode.None || building.getLength() == 0) {
+            return;
+        }
+
+        g.setColor(selectedColor);
+        g.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+        building.paint(g, mv);
+
+        g.setStroke(new BasicStroke(1));
+
+    }
+
+    private void drawingStart(MouseEvent e) {
+        mousePos = e.getPoint();
+        drawStartPos = mousePos;
+        if (ToolSettings.Shape.CIRCLE.equals(ToolSettings.getShape())) {
+            building.setBase(latlon2eastNorth(MainApplication.getMap().mapView.getLatLon(mousePos.x, mousePos.y)));
+        } else {
+            Node n = MainApplication.getMap().mapView.getNearestNode(mousePos, OsmPrimitive::isUsable);
+            if (n == null) {
+                building.setBase(latlon2eastNorth(MainApplication.getMap().mapView.getLatLon(mousePos.x, mousePos.y)));
+            } else {
+                building.setBase(n);
+            }
+        }
+        mode = Mode.Drawing;
+        updateStatusLine();
+    }
+
+    private void drawingAdvance(MouseEvent e) {
+        processMouseEvent(e);
+        if (this.mode != Mode.None && this.nextMode == Mode.None) {
+            drawingFinish();
+        } else {
+            mode = this.nextMode;
+            updateStatusLine();
+        }
+    }
+
+    private void drawingFinish() {
+        if (building.getLength() != 0) {
+            Way w;
+            if (ToolSettings.Shape.CIRCLE.equals(ToolSettings.getShape())) {
+                w = building.createCircle();
+            } else {
+                w = building.createRectangle();
+            }
+            if (w != null) {
+                if (!alt || ToolSettings.isUsingAddr())
+                    for (Entry<String, String> kv : ToolSettings.getTags().entrySet()) {
+                        w.put(kv.getKey(), kv.getValue());
+                    }
+                if (ToolSettings.isUsingAddr())
+                    showAddrDialog(w);
+                if (ToolSettings.isAutoSelect()
+                        && (getLayerManager().getEditDataSet().getSelected().isEmpty() || shift)) {
+                    getLayerManager().getEditDataSet().setSelected(w);
+                }
+            }
+        }
+        cancelDrawing();
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        if (e.getButton() != MouseEvent.BUTTON1)
+            return;
+        if (!MainApplication.getMap().mapView.isActiveLayerDrawable())
+            return;
+
+        requestFocusInMapView();
+
+        if (mode == Mode.None)
+            drawingStart(e);
+    }
+
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        processMouseEvent(e);
+        updCursor();
+        if (mode != Mode.None)
+            MainApplication.getMap().mapView.repaint();
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        if (e.getButton() != MouseEvent.BUTTON1)
+            return;
+        if (!MainApplication.getMap().mapView.isActiveLayerDrawable())
+            return;
+        boolean dragged = true;
+        if (drawStartPos != null)
+            dragged = e.getPoint().distance(drawStartPos) > 10;
+        drawStartPos = null;
+
+        if (mode == Mode.Drawing && !dragged)
+            return;
+        if (mode == Mode.None)
+            return;
+
+        drawingAdvance(e);
+    }
+
+    private void updCursor() {
+        if (mousePos == null)
+            return;
+        if (!MainApplication.isDisplayingMapView())
+            return;
+        Node n = null;
+        if (!ctrl)
+            n = MainApplication.getMap().mapView.getNearestNode(mousePos, OsmPrimitive::isUsable);
+        if (n != null) {
+            setCursor(cursorJoinNode);
+        } else {
+            if (customCursor != null && (!ctrl || isRectDrawing()))
+                setCursor(customCursor);
+            else
+                setCursor(getCursor());
+        }
+
+    }
+
+    @Override
+    public void mouseMoved(MouseEvent e) {
+        if (!MainApplication.getMap().mapView.isActiveLayerDrawable())
+            return;
+        processMouseEvent(e);
+        updCursor();
+        if (mode != Mode.None)
+            MainApplication.getMap().mapView.repaint();
+    }
+
+    @Override
+    public String getModeHelpText() {
+        if (mode == Mode.None)
+            return tr("Point on the corner of the building to start drawing");
+        if (mode == Mode.Drawing)
+            return tr("Point on opposite end of the building");
+        if (mode == Mode.DrawingWidth)
+            return tr("Set width of the building");
+        return "";
+    }
+
+    @Override
+    public boolean layerIsSupported(Layer l) {
+        return l instanceof OsmDataLayer;
+    }
+
+    public final 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<>();
+            LinkedList<Way> ways = new LinkedList<>();
+
+            for (OsmPrimitive p : newSelection) {
+                switch (p.getType()) {
+                case NODE:
+                    nodes.add((Node) p);
+                    break;
+                case WAY:
+                    ways.add((Way) p);
+                    break;
+                default:
+                    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 = 9; // crosshair outer radius
+        final int r = 3; // crosshair inner 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.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);
+        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();
+    }
+
+    @Override
+    public void selectionChanged(SelectionChangeEvent event) {
+        updateSnap(event.getSelection());
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/MergeAddrPointsAction.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/MergeAddrPointsAction.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/MergeAddrPointsAction.java	(revision 34572)
@@ -0,0 +1,153 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map.Entry;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.UndoRedoHandler;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.Notification;
+import org.openstreetmap.josm.tools.Geometry;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class MergeAddrPointsAction extends JosmAction {
+
+    public MergeAddrPointsAction() {
+        super(tr("Merge address points"), "mergeaddr",
+                tr("Move tags from address nodes inside buildings to building ways"),
+                Shortcut.registerShortcut("edit:mergeaddrpoints", tr("Edit: {0}", tr("Merge address points")),
+                        KeyEvent.CHAR_UNDEFINED, Shortcut.NONE),
+                true);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent arg0) {
+        if (!isEnabled())
+            return;
+        Collection<OsmPrimitive> selection = getLayerManager().getEditDataSet().getSelected();
+        if (selection.isEmpty()) {
+            new Notification(tr("Select both address nodes and building ways to merge"))
+                    .setIcon(JOptionPane.INFORMATION_MESSAGE).show();
+            return;
+        }
+        List<Node> addrNodes = new LinkedList<>();
+        List<Way> buildings = new LinkedList<>();
+        scanSelection:
+        for (OsmPrimitive p : selection) {
+            if (p.getType() == OsmPrimitiveType.NODE) {
+                for (OsmPrimitive r : p.getReferrers()) {
+                    if (r.getType() == OsmPrimitiveType.WAY)
+                       continue scanSelection; // Don't use nodes if they're referenced by ways
+                }
+                for (String key : p.getKeys().keySet()) {
+                    if (key.startsWith("addr:")) {
+                        addrNodes.add((Node) p); // Found address node
+                        break;
+                    }
+                }
+            } else if (p.getType() == OsmPrimitiveType.WAY && p.getKeys().containsKey("building"))
+                buildings.add((Way) p);
+        }
+        if (addrNodes.isEmpty()) {
+            new Notification(tr("No address nodes found in the selection"))
+                    .setIcon(JOptionPane.ERROR_MESSAGE).show();
+            return;
+        }
+        if (buildings.isEmpty()) {
+            new Notification(tr("No building ways found in the selection"))
+                    .setIcon(JOptionPane.ERROR_MESSAGE).show();
+            return;
+        }
+        List<Command> cmds = new LinkedList<>();
+        int multi = 0;
+        int conflicts = 0;
+        buildingsLoop: for (Way w : buildings) {
+            Node mergeNode = null;
+            for (Node n : addrNodes) {
+                if (Geometry.nodeInsidePolygon(n, w.getNodes()))
+                    if (mergeNode != null) {
+                        multi++;
+                        continue buildingsLoop; // Multiple address nodes inside
+                                                // one building -- skipping
+                    } else
+                        mergeNode = n;
+            }
+            if (mergeNode != null) {
+                boolean hasConflicts = false;
+                AbstractMap<String, String> tags = new HashMap<>();
+                for (Entry<String, String> entry : mergeNode.getKeys().entrySet()) {
+                    String newValue = entry.getValue();
+                    if (newValue == null)
+                        continue;
+                    String oldValue = w.getKeys().get(entry.getKey());
+                    if (!newValue.equals(oldValue)) {
+                        if (oldValue == null) {
+                            tags.put(entry.getKey(), newValue);
+                        } else
+                            hasConflicts = true;
+                    }
+                }
+                if (hasConflicts)
+                    conflicts++;
+                if (!tags.isEmpty())
+                    cmds.add(new ChangePropertyCommand(Collections.singleton(w), tags));
+                if (!hasConflicts) {
+                    for (OsmPrimitive p : mergeNode.getReferrers()) {
+                        Relation r = (Relation) p;
+                        Relation rnew = new Relation(r);
+                        for (int i = 0; i < r.getMembersCount(); i++) {
+                            RelationMember member = r.getMember(i);
+                            if (mergeNode.equals(member.getMember())) {
+                                rnew.removeMember(i);
+                                rnew.addMember(i, new RelationMember(member.getRole(), w));
+                            }
+                        }
+                        cmds.add(new ChangeCommand(r, rnew));
+                    }
+                    cmds.add(new DeleteCommand(mergeNode));
+                }
+            }
+        }
+        if (multi != 0)
+            new Notification(trn("There is {0} building with multiple address nodes inside",
+                    "There are {0} buildings with multiple address nodes inside", multi, multi))
+                    .setIcon(JOptionPane.WARNING_MESSAGE).show();
+        if (conflicts != 0)
+            new Notification(trn("There is {0} building with address conflicts",
+                            "There are {0} buildings with address conflicts", conflicts, conflicts))
+                    .setIcon(JOptionPane.WARNING_MESSAGE).show();
+        if (cmds.isEmpty() && multi == 0 && conflicts == 0)
+            new Notification(tr("No address nodes inside buildings found"))
+                    .setIcon(JOptionPane.INFORMATION_MESSAGE).show();
+        if (!cmds.isEmpty())
+            UndoRedoHandler.getInstance().add(new SequenceCommand("Merge addresses", cmds));
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(getLayerManager().getEditDataSet() != null);
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/MyDialog.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/MyDialog.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/MyDialog.java	(revision 34572)
@@ -0,0 +1,38 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.tools.GBC;
+
+public abstract class MyDialog extends ExtendedDialog {
+    private static final String[] BUTTON_TEXTS = new String[] {tr("OK"), tr("Cancel")};
+    private static final String[] BUTTON_ICONS = new String[] {"ok.png", "cancel.png"};
+
+    protected final JPanel panel = new JPanel(new GridBagLayout());
+
+    protected final void addLabelled(String str, Component c) {
+        JLabel label = new JLabel(str);
+        panel.add(label, GBC.std());
+        label.setLabelFor(c);
+        panel.add(c, GBC.eol().fill(GBC.HORIZONTAL));
+    }
+
+    public MyDialog(String title) {
+        super(MainApplication.getMainFrame(), title, BUTTON_TEXTS, true);
+        contentInsets = new Insets(15, 15, 5, 15);
+        setButtonIcons(BUTTON_ICONS);
+
+        setContent(panel);
+        setDefaultButton(1);
+    }
+}
Index: applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/ToolSettings.java
===================================================================
--- applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/ToolSettings.java	(revision 34572)
+++ applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/ToolSettings.java	(revision 34572)
@@ -0,0 +1,129 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.buildings_tools;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.Logging;
+
+public final class ToolSettings {
+
+    private ToolSettings() {
+        // Hide default constructor for utils classes
+    }
+
+    public static final BooleanProperty PROP_USE_ADDR_NODE = new BooleanProperty("buildings_tools.addrNode", false);
+
+    public enum Shape {
+            CIRCLE, RECTANGLE
+    }
+
+    private static Shape shape = loadShape();
+    private static double width = 0;
+    private static double lenstep = 0;
+    private static boolean useAddr;
+    private static final Map<String, String> TAGS = new HashMap<>();
+    private static boolean autoSelect;
+
+    public static Shape getShape() {
+        loadShape();
+        return shape;
+    }
+
+    public static void setAddrDialog(boolean _useAddr) {
+        useAddr = _useAddr;
+    }
+
+    public static void setSizes(double newwidth, double newlenstep) {
+        width = newwidth;
+        lenstep = newlenstep;
+    }
+
+    public static double getWidth() {
+        return width;
+    }
+
+    public static double getLenStep() {
+        return lenstep;
+    }
+
+    public static boolean isUsingAddr() {
+        return useAddr;
+    }
+
+    public static Map<String, String> getTags() {
+        loadTags();
+        return TAGS;
+    }
+
+    public static void saveTags(Map<String, String> tags) {
+        TAGS.clear();
+        TAGS.putAll(tags);
+        ArrayList<String> values = new ArrayList<>(TAGS.size() * 2);
+        for (Entry<String, String> entry : TAGS.entrySet()) {
+            values.add(entry.getKey());
+            values.add(entry.getValue());
+        }
+        Config.getPref().putList("buildings_tools.tags", values);
+    }
+
+    private static void loadTags() {
+        TAGS.clear();
+        Collection<String> values = Config.getPref().getList("buildings_tools.tags",
+                Arrays.asList("building", "yes"));
+        try {
+            for (Iterator<String> iterator = values.iterator(); iterator.hasNext();) {
+                TAGS.put(iterator.next(), iterator.next());
+            }
+        } catch (NoSuchElementException e) {
+            Logging.warn(e);
+        }
+    }
+
+    public static void saveShape(Shape shape) {
+        Config.getPref().put("buildings_tool.shape", shape.name());
+    }
+
+    private static Shape loadShape() {
+        String shape = Config.getPref().get("buildings_tool.shape");
+        if (ToolSettings.Shape.CIRCLE.name().equals(shape)) {
+            ToolSettings.shape = Shape.CIRCLE;
+            return Shape.CIRCLE;
+        } else {
+            ToolSettings.shape = Shape.RECTANGLE;
+            return (Shape.RECTANGLE);
+        }
+    }
+
+    public static void setBBMode(boolean bbmode) {
+        Config.getPref().putBoolean("buildings_tools.bbmode", bbmode);
+    }
+
+    public static boolean isBBMode() {
+        return Config.getPref().getBoolean("buildings_tools.bbmode", false);
+    }
+
+    public static void setSoftCursor(boolean softCursor) {
+        Config.getPref().putBoolean("buildings_tools.softcursor", softCursor);
+    }
+
+    public static boolean isSoftCursor() {
+        return Config.getPref().getBoolean("buildings_tools.softcursor", false);
+    }
+
+    public static boolean isAutoSelect() {
+        return autoSelect;
+    }
+
+    public static void setAutoSelect(boolean _autoSelect) {
+        autoSelect = _autoSelect;
+    }
+}
