Index: src/org/openstreetmap/josm/actions/mapmode/CircleAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/CircleAction.java	(revision 0)
+++ src/org/openstreetmap/josm/actions/mapmode/CircleAction.java	(revision 0)
@@ -0,0 +1,758 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.actions.mapmode;
+
+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.Graphics;
+import java.awt.Graphics2D;
+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 java.awt.geom.GeneralPath;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.math.*;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.MoveCommand;
+import org.openstreetmap.josm.command.RotateCommand;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.WaySegment;
+import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Makes a circle from a point or way
+ *
+ */
+public class CircleAction extends MapMode implements MapViewPaintable {
+
+    public static boolean needMouseMove = false;
+    enum Mode { circle, rotate, select }
+    private Mode mode = null;
+    private long mouseDownTime = 0;
+    private Node selectedNode = null;
+    private Way selectedWay = null;
+    private WaySegment selectedSegment = null;
+    private Color selectedColor;
+    private boolean ctrl;
+    private boolean alt;
+    private boolean shift;
+
+    double xoff;
+    double yoff;
+    double distance;
+    int NodeCount;
+
+    private Cursor oldCursor;
+    private Point mousePos;
+    private Point oldMousePos;
+    private Point initialMousePos;
+    private int initialMoveDelay = 200;
+
+    /**
+     * Create a new SelectAction
+     * @param mapFrame The MapFrame this action belongs to.
+     */
+    public CircleAction(MapFrame mapFrame) {
+        super(tr("Create circle"), "circle/circle", tr("Create circle"),
+                Shortcut.registerShortcut("mapmode:circle", tr("Mode: {0}", tr("Create circle")), KeyEvent.VK_C, Shortcut.GROUP_EDIT),
+                mapFrame,
+                getCursor("normal", "circle", Cursor.DEFAULT_CURSOR));
+        putValue("help", "Action/Extrude/Circle");
+        initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200);
+        selectedColor = Main.pref.getColor(marktr("selected"), Color.red);
+
+        NodeCount = Main.pref.getInteger("createcircle.nodecount", 12); // TODO: re-use createcircle.nodecount or rename?
+        if (NodeCount < 4) {
+            NodeCount = 4;
+        }
+        if (NodeCount > 128) {
+            NodeCount = 128;
+        }
+        if (NodeCount % 2 != 0) {
+            // allow only even numbers
+            NodeCount += 1;
+        }
+        // TODO: update preference accordingly?
+    }
+
+    private static Cursor getCursor(String name, String mod, int def) {
+        try {
+            return ImageProvider.getCursor(name, mod);
+        } catch (Exception e) {
+        }
+        return Cursor.getPredefinedCursor(def);
+    }
+
+    private void setCursor(Cursor c) {
+        if (oldCursor == null) {
+            oldCursor = Main.map.mapView.getCursor();
+            Main.map.mapView.setCursor(c);
+        }
+    }
+
+    private void restoreCursor() {
+        if (oldCursor != null) {
+            Main.map.mapView.setCursor(oldCursor);
+            oldCursor = null;
+        }
+    }
+
+    @Override public void enterMode() {
+        super.enterMode();
+        Main.map.mapView.addMouseListener(this);
+        Main.map.mapView.addMouseMotionListener(this);
+    }
+
+    @Override public void exitMode() {
+        super.exitMode();
+        Main.map.mapView.removeMouseListener(this);
+        Main.map.mapView.removeMouseMotionListener(this);
+        Main.map.mapView.removeTemporaryLayer(this);
+
+    }
+
+    @Override public void mouseMoved(MouseEvent e) {
+        if(!Main.map.mapView.isActiveLayerDrawable())
+            return;
+        if (needMouseMove && mode == Mode.rotate) {
+            mouseDragged(e);
+        }
+
+        updateKeyModifiers(e);
+        mousePos = e.getPoint();
+    }
+
+    private void updateKeyModifiers(MouseEvent e) {
+        ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+        alt = (e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0;
+        shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
+    }
+
+    @Override public void mouseDragged(MouseEvent e) {
+        if(!Main.map.mapView.isActiveLayerVisible())
+            return;
+
+        if (mode == Mode.select) return;
+
+        // do not count anything as a move if it lasts less than 100 milliseconds.
+        if (((mode == Mode.circle) || (mode == Mode.rotate)) && (System.currentTimeMillis() - mouseDownTime < initialMoveDelay)) return;
+
+        if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0)
+            return;
+
+        if (mousePos == null) {
+            mousePos = e.getPoint();
+            return;
+        }
+
+        if (mode == Mode.rotate) {
+            EastNorth mouseEN = Main.map.mapView.getEastNorth(e.getX(), e.getY());
+            EastNorth mouseStartEN = Main.map.mapView.getEastNorth(mousePos.x, mousePos.y);
+            double dx = mouseEN.east() - mouseStartEN.east();
+            double dy = mouseEN.north() - mouseStartEN.north();
+            if (dx == 0 && dy == 0)
+                return;
+
+            Collection<OsmPrimitive> selection = Main.ds.getSelectedNodesAndWays();
+            Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection);
+
+            if (affectedNodes.size() < 2) return;
+
+            Command c = !Main.main.undoRedo.commands.isEmpty() ? Main.main.undoRedo.commands.getLast() : null;
+            if (c instanceof SequenceCommand) {
+                c = ((SequenceCommand)c).getLastCommand();
+            }
+
+            if (c instanceof RotateCommand && affectedNodes.equals(((RotateCommand)c).getRotatedNodes())) {
+                ((RotateCommand)c).rotateAgain(mouseStartEN, mouseEN);
+            } else {
+                Main.main.undoRedo.add(new RotateCommand(selection, mouseStartEN, mouseEN));
+            }
+        }
+
+        Main.map.mapView.repaint();
+        mousePos = e.getPoint();
+
+    }
+
+    @Override public void mousePressed(MouseEvent e) {
+        if(!Main.map.mapView.isActiveLayerVisible())
+            return;
+        if (!(Boolean)this.getValue("active")) return;
+        if (e.getButton() != MouseEvent.BUTTON1)
+            return;
+
+        ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+        alt = (e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0;
+        shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
+
+        mouseDownTime = System.currentTimeMillis();
+
+        if (e.getClickCount() > 1 && mousePos != null && mousePos.equals(oldMousePos)) {
+            // double-click, work with the selected way right here
+
+            List<OsmPrimitive> sel = new ArrayList<OsmPrimitive>(Main.ds.getSelected());
+            if (sel.size() == 1 && sel.get(0) instanceof Way) {
+                selectedWay = (Way) sel.get(0);
+            } else {
+                selectedWay = null;
+            }
+
+            if (selectedWay != null) {
+                if (selectedWay.nodes.get(0).equals(selectedWay.nodes.get(selectedWay.nodes.size()-1))) {
+                    // we have a closed way
+                    Collection<Command> cmds = new LinkedList<Command>();
+
+                    makeCircle(null, selectedWay, cmds);
+
+                    if (selectedWay.nodes.size() < NodeCount) {
+                        Main.main.undoRedo.add(new SequenceCommand(tr("Adding nodes and align circle"), cmds));
+                    } else {
+                        Main.main.undoRedo.add(new SequenceCommand(tr("Align circle"), cmds));
+                    }
+                } else {
+                    // we have an open way
+                    // check if segment is part of a longer way
+                    if (selectedWay.nodes.size() == 2) {
+                        Collection<Command> cmds = new LinkedList<Command>();
+
+                        makeCircle(null, selectedWay, cmds);
+
+                        Main.main.undoRedo.add(new SequenceCommand(tr("Convert way into a circle"), cmds));
+                    }
+                }
+            }
+        } else {
+            // single-click
+
+            if (ctrl && shift) {
+                // start rotating
+
+                if (!Main.ds.getSelected().isEmpty()) {
+                    mode = Mode.rotate;
+                    setCursor(ImageProvider.getCursor("rotate", null));
+                }
+            } else {
+                List<OsmPrimitive> sel = new ArrayList<OsmPrimitive>(Main.ds.getSelectedNodesAndWays());
+                if (sel.size() == 1) {
+                    if (sel.get(0) instanceof Node) {
+                        selectedNode = (Node) sel.get(0);
+                    }
+                    if (sel.get(0) instanceof Way) {
+                        selectedWay = (Way) sel.get(0);
+                    }
+                }
+
+                Node nearestNode = Main.map.mapView.getNearestNode(e.getPoint());
+                Way nearestWay = Main.map.mapView.getNearestWay(e.getPoint());
+
+                if (nearestNode == null && nearestWay == null) {
+                    // nothing selected: deselect
+                    mode = Mode.select;
+
+                    selectedNode = null;
+                    selectedWay = null;
+
+                    Main.ds.setSelected();
+                } else {
+                    if (nearestNode == null) {
+                        // only way select
+                        selectedNode = null;
+
+                        if (selectedWay != nearestWay) {
+                            // change selection
+                            mode = Mode.select;
+
+                            selectedWay = nearestWay;
+
+                            Main.ds.setSelected(nearestWay);
+
+                        }
+                    } else {
+                        // only node select
+                        selectedWay = null;
+
+                        if (selectedNode != nearestNode) {
+                            // change selection
+                            mode = Mode.select;
+
+                            selectedNode = nearestNode;
+
+                            Main.ds.setSelected(nearestNode);
+                        } else {
+                            // switch to circle mode
+                            mode = Mode.circle;
+                        }
+                    }
+                }
+                oldCursor = Main.map.mapView.getCursor();
+            }
+
+        }
+        oldMousePos = mousePos;
+
+        updateStatusLine();
+        Main.map.mapView.addTemporaryLayer(this);
+        Main.map.mapView.repaint();
+
+        mousePos = e.getPoint();
+        initialMousePos = e.getPoint();
+    }
+
+    @Override public void mouseReleased(MouseEvent e) {
+        if(!Main.map.mapView.isActiveLayerVisible())
+            return;
+
+        if (mode == Mode.circle) {
+            if (selectedNode == null) {
+                mode = null;
+                return;
+            }
+            if (mousePos.distance(initialMousePos) > 10) {
+                // Create the Circle
+
+                Collection<Command> cmds = new LinkedList<Command>();
+                Collection<Node> nodes = new LinkedList<Node>();
+
+                nodes.add(selectedNode);
+                Node n2 = Main.map.mapView.getNearestNode(e.getPoint());
+                if (n2 == null) {
+                    n2 = new Node(Main.proj.eastNorth2latlon(Main.map.mapView.getEastNorth(mousePos.x, mousePos.y)));
+                    cmds.add(new AddCommand(n2));
+                }
+                nodes.add(n2);
+
+                makeCircle(nodes, null, cmds);
+
+                Main.main.undoRedo.add(new SequenceCommand(tr("Create Circle"), cmds));
+
+            }
+            selectedNode = null;
+        }
+
+        restoreCursor();
+
+        Main.map.mapView.removeTemporaryLayer(this);
+        mode = null;
+        updateStatusLine();
+        Main.map.mapView.repaint();
+    }
+
+    private EastNorth calcCenter(Collection<Node> nodes) {
+        // this is a more precise version of the one introduced in Rev.1713 of the AlignInCircle action
+
+        BigDecimal area = new BigDecimal("0");
+        BigDecimal north = new BigDecimal("0");
+        BigDecimal east = new BigDecimal("0");
+
+        // Integrate the area, east and north centroid, we'll compute the final value based on the result of integration
+        for (int i = 0; i < nodes.size() - 1; i++) {
+            EastNorth n0 = ((Node) nodes.toArray()[i]).getEastNorth();
+            EastNorth n1 = ((Node) nodes.toArray()[i+1]).getEastNorth();
+
+            BigDecimal x0 = new BigDecimal(n0.east());
+            BigDecimal y0 = new BigDecimal(n0.north());
+            BigDecimal x1 = new BigDecimal(n1.east());
+            BigDecimal y1 = new BigDecimal(n1.north());
+
+            BigDecimal k = x0.multiply(y1, MathContext.DECIMAL128).subtract(y0.multiply(x1, MathContext.DECIMAL128));
+
+            area = area.add(k, MathContext.DECIMAL128);
+            east = east.add(k.multiply(x0.add(x1, MathContext.DECIMAL128), MathContext.DECIMAL128));
+            north = north.add(k.multiply(y0.add(y1, MathContext.DECIMAL128), MathContext.DECIMAL128));
+
+        }
+
+        BigDecimal d = new BigDecimal("2");
+        area  = area.divide(d, MathContext.DECIMAL128);
+        d = new BigDecimal("6");
+        north = north.divide(d.multiply(area, MathContext.DECIMAL128), MathContext.DECIMAL128);
+        east = east.divide(d.multiply(area, MathContext.DECIMAL128), MathContext.DECIMAL128);
+
+        EastNorth center = new EastNorth(east.doubleValue(), north.doubleValue());
+
+        return center;
+    }
+
+    private double calcAngle(double xc, double yc, double x, double y) {
+        // calculate the angle from xc|yc to x|y
+        if (xc == x && yc == y)
+            return 0; // actually invalid, but we won't have this case in this context
+        double yd = Math.abs(y - yc);
+        if (yd == 0 && xc < x)
+            return 0;
+        if (yd == 0 && xc > x)
+            return Math.PI;
+        double xd = Math.abs(x - xc);
+        double a = Math.atan2(xd, yd);
+        if (y > yc) {
+            a = Math.PI - a;
+        }
+        if (x < xc) {
+            a = -a;
+        }
+        a = 1.5*Math.PI + a;
+        if (a < 0) {
+            a += 2*Math.PI;
+        }
+        if (a >= 2*Math.PI) {
+            a -= 2*Math.PI;
+        }
+        return a;
+    }
+
+    private void fixAngleAndRadius(Collection<Command> cmds, Way way, EastNorth center, double r, int direction, boolean ignoreAngle) {
+        // run through all nodes and set radius and angles
+
+        double step = direction * 2 * Math.PI / (way.nodes.size() - 1);
+        double a = 0;
+        for (int i = 0; i < way.nodes.size() - 1; i++) {
+            Node n = ((Node)way.nodes.toArray()[i]);
+
+            if (i == 0 || ignoreAngle) {
+                a = calcAngle(center.east(), center.north(), n.getEastNorth().east(), n.getEastNorth().north());
+            }
+            Node p = newNode(center, r, a);
+            if (p.getCoor().isOutSideWorld()) {
+                JOptionPane.showMessageDialog(Main.parent, tr("Cannot move objects outside of the world."));
+                return;
+            }
+            cmds.add(new MoveCommand(n, p.getEastNorth().east() - n.getEastNorth().east(), p.getEastNorth().north() - n.getEastNorth().north()));
+
+            a += step;
+        }
+
+    }
+
+    private Collection<Node> findNodes(Collection<Node> nodes, EastNorth nc, double ma, double mb) {
+
+        Collection<Node> result = new LinkedList<Node>();
+        double min = ma;
+        double max = mb;
+        if (min > max) {
+            min = mb;
+            max = ma;
+        }
+
+        for (Node n : nodes) {
+            double a = calcAngle(nc.east(), nc.north(), n.getEastNorth().east(), n.getEastNorth().north());
+            if (a >= min && a <= max) {
+                result.add(n);
+            }
+        }
+
+        return result;
+    }
+
+    private Node newNode(EastNorth center, double r, double a) {
+        double x = center.east() + r * Math.cos(a);
+        double y = center.north() + r * Math.sin(a);
+        Node p = new Node(Main.proj.eastNorth2latlon(new EastNorth(x, y)));
+
+        return p;
+    }
+
+    private void makeCircle(Collection<Node> nodes, Way way, Collection<Command> cmds) {
+
+        // sanity checks
+        if (way == null && nodes == null) return;
+        if (way != null) {
+            // if the way has more than 2 nodes, it should be closed
+            if ((way.nodes.size() > 2) && !way.nodes.get(0).equals(way.nodes.get(way.nodes.size()-1))) return;
+            // use nodes of the way for the process
+            if (nodes == null) {
+                nodes = new LinkedList<Node>();
+                nodes.addAll(way.nodes);
+            }
+        }
+        if (nodes.size() < 2) return; // we need at least 2 nodes
+
+
+        // now make a circle
+        if (nodes.size() == 2) {
+            // 2 nodes or 1 segment for diameter
+
+            Node n1 = ((Node)nodes.toArray()[0]);
+            Node n2 = ((Node)nodes.toArray()[1]);
+
+            // calculate the center
+            EastNorth center = new EastNorth(0.5 * (n1.getEastNorth().east() + n2.getEastNorth().east()), 0.5 * (n1.getEastNorth().north() + n2.getEastNorth().north()));
+
+            // calculate the radius (r)
+            double r = Math.sqrt(Math.pow(center.east() - n1.getEastNorth().east(), 2) + Math.pow(center.north() - n1.getEastNorth().north(), 2));
+
+            // find direction
+            double a1 = calcAngle(center.east(), center.north(), n1.getEastNorth().east(), n1.getEastNorth().north());
+            double a2 = calcAngle(center.east(), center.north(), n2.getEastNorth().east(), n2.getEastNorth().north());
+            int direction = -1;
+            if (a1 < a2) { direction = -direction; }
+
+            double step = direction * 2 * Math.PI / NodeCount;
+
+            Way newWay;
+            if (way == null) {
+                newWay = new Way();
+            } else {
+                newWay = new Way(way);
+                newWay.nodes.clear();
+            }
+
+            // no need for gizmos here - create step by step
+
+            // add first node
+            newWay.nodes.add(((Node)nodes.toArray()[0]));
+            double a = a1;
+
+            // add first bunch of nodes
+            while (newWay.nodes.size() < NodeCount / 2 ) {
+                Node n = newNode(center, r, a + step);
+                if (n.getCoor().isOutSideWorld()) {
+                    JOptionPane.showMessageDialog(Main.parent, tr("Cannot add a node outside of the world."));
+                    return;
+                }
+                newWay.nodes.add(n);
+                cmds.add(new AddCommand(n));
+
+                a += step;
+            }
+
+            // add second node the hard way
+            newWay.nodes.add(((Node)nodes.toArray()[1]));
+            a += step;
+
+            // add second bunch of nodes
+            while (newWay.nodes.size() < NodeCount) {
+                Node n = newNode(center, r, a + step);
+                if (n.getCoor().isOutSideWorld()) {
+                    JOptionPane.showMessageDialog(Main.parent, tr("Cannot add a node outside of the world."));
+                    return;
+                }
+                newWay.nodes.add(n);
+                cmds.add(new AddCommand(n));
+
+                a += step;
+            }
+
+            // close the circle
+            newWay.nodes.add(newWay.nodes.get(0));
+
+            fixAngleAndRadius(cmds, newWay, center, r, direction, false); // not needed actually
+
+            if (way == null) {
+                cmds.add(new AddCommand(newWay));
+            } else {
+                cmds.add(new ChangeCommand(way, newWay));
+            }
+
+        } else {
+            // more than 2 nodes or closed way
+
+            // calculate center and direction
+            EastNorth center = calcCenter(nodes);
+            double area = 0;
+            for (int i=0; i<nodes.size(); i++) {
+                EastNorth n0 = ((Node) nodes.toArray()[i]).getEastNorth();
+                EastNorth n1 = ((Node) nodes.toArray()[(i + 1) % nodes.size()]).getEastNorth();
+                area  += n0.east() * n1.north() - n1.east() * n0.north();
+            }
+            int direction = -1;
+            if (area > 0) { direction = -direction; }
+
+            // calculate radius
+            double r = 0;
+            for (Node n : nodes) {
+                r += Math.sqrt(Math.pow(center.east() - n.getEastNorth().east(), 2) + Math.pow(center.north() - n.getEastNorth().north(), 2));
+            }
+            r /= nodes.size();
+
+            // set start angle and step size
+            double step = direction * 2 * Math.PI / NodeCount;
+            double a = 0;
+            if (direction < 0) {
+                a = 2 * Math.PI;
+            }
+
+            if (!ctrl && nodes.size() < NodeCount) {
+                // pressing CTRL will not add new nodes to the circle
+                // add nodes if needed
+
+                Way newWay;
+                if (way == null) {
+                    newWay = new Way();
+                } else {
+                    newWay = new Way(way);
+                    newWay.nodes.clear();
+                }
+
+                while (newWay.nodes.size() < NodeCount) {
+                    Collection<Node> oldNodes = findNodes(nodes, center, a, a + step);
+
+                    if (oldNodes.isEmpty()) {
+                        // no existing nodes in sector, create a new one
+                        Node n = newNode(center, r, a + 0.5 * step);
+                        if (n.getCoor().isOutSideWorld()) {
+                            JOptionPane.showMessageDialog(Main.parent, tr("Cannot add a node outside of the world."));
+                            return;
+                        }
+                        newWay.nodes.add(n);
+                        cmds.add(new AddCommand(n));
+                    } else {
+                        for (Node en : oldNodes) {
+                            if (!newWay.nodes.contains(en)) {
+                                newWay.nodes.add(en);
+                            }
+                        }
+                    }
+
+                    a += step;
+                }
+
+                newWay.nodes.add(newWay.nodes.get(0)); // close the circle
+
+                fixAngleAndRadius(cmds, newWay, center, r, direction, alt);
+                // pressing ALT will not change the angles
+
+                if (way == null) {
+                    cmds.add(new AddCommand(newWay));
+                } else {
+                    cmds.add(new ChangeCommand(way, newWay));
+                }
+
+            } else {
+                // just fix circle
+                if (way != null) {
+                    fixAngleAndRadius(cmds, way, center, r, direction, alt);
+                    // pressing ALT will not change the angles
+                }
+            }
+        }
+
+        return;
+    }
+
+    public void paint(Graphics g, MapView mv) {
+        if (mode == Mode.circle) {
+            // Draw a simple circle-placeholder
+            if (selectedNode == null) return;
+
+            // find the coordinates
+            double x1 = selectedNode.getEastNorth().east();
+            double y1 = selectedNode.getEastNorth().north();
+
+            double x2 = mv.getEastNorth(mousePos.x, mousePos.y).east();
+            double y2 = mv.getEastNorth(mousePos.x, mousePos.y).north();
+
+            double xc = 0.5 * (x1 + x2);
+            double yc = 0.5 * (y1 + y2);
+
+            double a = calcAngle(xc, yc, x1, y1);
+            double r = Math.sqrt(Math.pow(xc - x1, 2) + Math.pow(yc - y1, 2));
+
+            // show length of diameter in status
+            distance = Main.proj.eastNorth2latlon(new EastNorth(x2, y2)).greatCircleDistance(Main.proj.eastNorth2latlon(new EastNorth(x1, y1)));
+            Main.map.statusLine.setDist(distance);
+            updateStatusLine();
+
+            // draw a circle
+            Graphics2D g2 = (Graphics2D)g;
+            g2.setColor(selectedColor);
+            g2.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+            GeneralPath b = new GeneralPath();
+
+            for (int i = 0; i <= NodeCount; i++) {
+                double a1 = a + 2 * Math.PI * (1.0 - i / (double)NodeCount);
+
+                double x = xc + r * Math.cos(a1);
+                double y = yc + r * Math.sin(a1);
+                Point p = mv.getPoint(new EastNorth(x, y));
+                if (i == 0) {
+                    b.moveTo(p.x, p.y);
+                } else {
+                    b.lineTo(p.x, p.y);
+                }
+
+            }
+            g2.draw(b);
+
+            // draw the center point
+            Point p = mv.getPoint(new EastNorth(xc, yc));
+            g2.drawArc(p.x, p.y, 2, 2, 0, 360);
+
+            g2.setStroke(new BasicStroke(1));
+        } else {
+            List<OsmPrimitive> sel = new ArrayList<OsmPrimitive>(Main.ds.getSelected());
+            if (sel.size() == 1 && sel.get(0) instanceof Way) {
+                Way selWay  = (Way) sel.get(0);
+
+                if ((selWay.nodes.size() > 2) && selWay.nodes.get(0).equals(selWay.nodes.get(selWay.nodes.size()-1))) {
+                    // more than 2 nodes
+
+                    EastNorth center = calcCenter(selWay.nodes);
+                    Point p = mv.getPoint(center);
+
+                    // draw a point
+                    Graphics2D g2 = (Graphics2D)g;
+                    g2.setColor(selectedColor);
+                    g2.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+                    g2.drawArc(p.x, p.y, 2, 2, 0, 360);
+
+                    g2.setStroke(new BasicStroke(1));
+
+                }
+            }
+        }
+    }
+
+    @Override public String getModeHelpText() {
+        if (mode == Mode.select)
+            return tr("Release the mouse button to select the objects in the rectangle.");
+        else if (mode == Mode.circle)
+            return tr("Make a circle of the desired size, then release the mouse button.");
+        else if (mode == Mode.rotate)
+            return tr("Release the mouse button to stop rotating.");
+        else {
+            List<OsmPrimitive> sel = new ArrayList<OsmPrimitive>(Main.ds.getSelectedNodesAndWays());
+            if (sel.size() == 1 && sel.get(0) instanceof Way) {
+                Way selWay  = (Way) sel.get(0);
+
+                if (selWay.nodes.size() == 2)
+                    return tr("Double-click on the selected way to create a circle.");
+                else if ((selWay.nodes.size() > 2) && selWay.nodes.get(0).equals(selWay.nodes.get(selWay.nodes.size()-1)))
+                    return tr("Double-click on the selected way to align nodes in a circle.");
+
+            } else if (sel.size() == 1 && sel.get(0) instanceof Node)
+                return tr("Click on the selected node and move the mouse to create a circle.");
+        }
+        return tr("Select a way or node to make a circle.");
+    }
+
+    @Override public boolean layerIsSupported(Layer l) {
+        return l instanceof OsmDataLayer;
+    }
+}
Index: src/org/openstreetmap/josm/gui/MapFrame.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapFrame.java	(revision 1755)
+++ src/org/openstreetmap/josm/gui/MapFrame.java	(working copy)
@@ -18,6 +18,7 @@
 import org.openstreetmap.josm.actions.mapmode.DeleteAction;
 import org.openstreetmap.josm.actions.mapmode.DrawAction;
 import org.openstreetmap.josm.actions.mapmode.ExtrudeAction;
+import org.openstreetmap.josm.actions.mapmode.CircleAction;
 import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.actions.mapmode.SelectAction;
 import org.openstreetmap.josm.actions.mapmode.ZoomAction;
@@ -90,6 +91,7 @@
         addMapMode(new IconToggleButton(new SelectAction(this)));
         addMapMode(new IconToggleButton(new DrawAction(this)));
         addMapMode(new IconToggleButton(new ExtrudeAction(this)));
+        addMapMode(new IconToggleButton(new CircleAction(this)));
         addMapMode(new IconToggleButton(new ZoomAction(this)));
         addMapMode(new IconToggleButton(new DeleteAction(this)));
 
@@ -132,14 +134,17 @@
      * Delegates the call to all Destroyables within this component (e.g. MapModes)
      */
     public void destroy() {
-        for (ToggleDialog t : allDialogs)
+        for (ToggleDialog t : allDialogs) {
             t.close();
+        }
         for (int i = 0; i < toolBarActions.getComponentCount(); ++i)
-            if (toolBarActions.getComponent(i) instanceof Destroyable)
+            if (toolBarActions.getComponent(i) instanceof Destroyable) {
                 ((Destroyable)toolBarActions).destroy();
+            }
         for (int i = 0; i < toolBarToggle.getComponentCount(); ++i)
-            if (toolBarToggle.getComponent(i) instanceof Destroyable)
+            if (toolBarToggle.getComponent(i) instanceof Destroyable) {
                 ((Destroyable)toolBarToggle).destroy();
+            }
 
         // remove menu entries
         Main.main.menu.viewMenu.setVisible(false);
@@ -194,8 +199,9 @@
     @Override public void setVisible(boolean aFlag) {
         boolean old = isVisible();
         super.setVisible(aFlag);
-        if (old != aFlag)
+        if (old != aFlag) {
             firePropertyChange("visible", old, aFlag);
+        }
     }
 
 
@@ -208,8 +214,9 @@
     public void selectMapMode(MapMode mapMode) {
         if (mapMode == this.mapMode)
             return;
-        if (this.mapMode != null)
+        if (this.mapMode != null) {
             this.mapMode.exitMode();
+        }
         this.mapMode = mapMode;
         mapMode.enterMode();
     }
@@ -229,13 +236,15 @@
         jb.add(toolBarToggle);
         if(Main.pref.getBoolean("sidetoolbar.visible", true))
         {
-            if(Main.pref.getBoolean("sidetoolbar.scrollable", true))
+            if(Main.pref.getBoolean("sidetoolbar.scrollable", true)) {
                 panel.add(new ScrollViewport(jb, ScrollViewport.VERTICAL_DIRECTION),
-                BorderLayout.WEST);
-            else
+                        BorderLayout.WEST);
+            } else {
                 panel.add(jb, BorderLayout.WEST);
+            }
         }
-        if (statusLine != null && Main.pref.getBoolean("statusline.visible", true))
+        if (statusLine != null && Main.pref.getBoolean("statusline.visible", true)) {
             panel.add(statusLine, BorderLayout.SOUTH);
+        }
     }
 }
