001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.event.ActionEvent;
008import java.awt.event.KeyEvent;
009import java.util.Collection;
010
011import org.openstreetmap.josm.command.MoveCommand;
012import org.openstreetmap.josm.data.UndoRedoHandler;
013import org.openstreetmap.josm.data.coor.LatLon;
014import org.openstreetmap.josm.data.osm.Node;
015import org.openstreetmap.josm.data.osm.OsmPrimitive;
016import org.openstreetmap.josm.data.osm.OsmUtils;
017import org.openstreetmap.josm.gui.MainApplication;
018import org.openstreetmap.josm.gui.dialogs.LatLonDialog;
019import org.openstreetmap.josm.tools.Shortcut;
020
021/**
022 * This action displays a dialog with the coordinates of a node where the user can change them,
023 * and when ok is pressed, the node is relocated to the specified position.
024 */
025public final class MoveNodeAction extends JosmAction {
026
027    /**
028     * Constructs a new {@code MoveNodeAction}.
029     */
030    public MoveNodeAction() {
031        super(tr("Move Node..."), "movenode", tr("Edit latitude and longitude of a node."),
032                Shortcut.registerShortcut("movenode", tr("Edit: {0}", tr("Move Node...")),
033                        KeyEvent.VK_M, Shortcut.NONE), true);
034        setHelpId(ht("/Action/MoveNode"));
035    }
036
037    @Override
038    public void actionPerformed(ActionEvent e) {
039        Collection<Node> selNodes = getLayerManager().getEditDataSet().getSelectedNodes();
040        if (!isEnabled() || selNodes.size() != 1)
041            return;
042
043        LatLonDialog dialog = new LatLonDialog(MainApplication.getMainFrame(), tr("Move Node..."), ht("/Action/MoveNode"));
044        Node n = (Node) selNodes.toArray()[0];
045        dialog.setCoordinates(n.getCoor());
046        dialog.showDialog();
047        if (dialog.getValue() != 1)
048            return;
049
050        LatLon coordinates = dialog.getCoordinates();
051        if (coordinates == null)
052            return;
053
054        // move the node
055        UndoRedoHandler.getInstance().add(new MoveCommand(n, coordinates));
056        if (n.getCoor().distance(coordinates) > 1) {
057            // see #13538: Avoid rounding error near 180 longitude which moves nodes too far
058            if (coordinates.lon() >= 180.0) {
059                coordinates = new LatLon(coordinates.lat(), Math.nextDown(180.0));
060            } else if (coordinates.lon() <= -180.0) {
061                coordinates = new LatLon(coordinates.lat(), Math.nextUp(-180.0));
062            }
063            UndoRedoHandler.getInstance().undo(1);
064            UndoRedoHandler.getInstance().add(new MoveCommand(n, coordinates));
065        }
066        MainApplication.getMap().mapView.repaint();
067    }
068
069    @Override
070    protected void updateEnabledState() {
071        updateEnabledStateOnCurrentSelection();
072    }
073
074    @Override
075    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
076        setEnabled(OsmUtils.isOsmCollectionEditable(selection) && selection.size() == 1 && selection.toArray()[0] instanceof Node);
077    }
078}