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 javax.swing.JOptionPane;
012
013import org.openstreetmap.josm.Main;
014import org.openstreetmap.josm.command.Command;
015import org.openstreetmap.josm.command.MoveCommand;
016import org.openstreetmap.josm.data.coor.EastNorth;
017import org.openstreetmap.josm.data.osm.DataSet;
018import org.openstreetmap.josm.data.osm.Node;
019import org.openstreetmap.josm.data.osm.OsmPrimitive;
020import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
021import org.openstreetmap.josm.tools.Shortcut;
022
023/**
024 * Moves the selection
025 *
026 * @author Frederik Ramm
027 */
028public class MoveAction extends JosmAction {
029
030    public enum Direction { UP, LEFT, RIGHT, DOWN }
031
032    private final Direction myDirection;
033
034    // any better idea?
035    private static String calltosupermustbefirststatementinconstructortext(Direction dir) {
036        String directiontext;
037        if (dir == Direction.UP) {
038            directiontext = tr("up");
039        } else if (dir == Direction.DOWN) {
040            directiontext = tr("down");
041        } else if (dir == Direction.LEFT) {
042            directiontext = tr("left");
043        } else {
044            directiontext = tr("right");
045        }
046        return directiontext;
047    }
048
049    // any better idea?
050    private static Shortcut calltosupermustbefirststatementinconstructor(Direction dir) {
051        Shortcut sc;
052        // CHECKSTYLE.OFF: SingleSpaceSeparator
053        if (dir == Direction.UP) {
054            sc = Shortcut.registerShortcut("core:moveup",    tr("Move objects {0}", tr("up")),    KeyEvent.VK_UP,    Shortcut.SHIFT);
055        } else if (dir == Direction.DOWN) {
056            sc = Shortcut.registerShortcut("core:movedown",  tr("Move objects {0}", tr("down")),  KeyEvent.VK_DOWN,  Shortcut.SHIFT);
057        } else if (dir == Direction.LEFT) {
058            sc = Shortcut.registerShortcut("core:moveleft",  tr("Move objects {0}", tr("left")),  KeyEvent.VK_LEFT,  Shortcut.SHIFT);
059        } else { //dir == Direction.RIGHT
060            sc = Shortcut.registerShortcut("core:moveright", tr("Move objects {0}", tr("right")), KeyEvent.VK_RIGHT, Shortcut.SHIFT);
061        }
062        // CHECKSTYLE.ON: SingleSpaceSeparator
063        return sc;
064    }
065
066    /**
067     * Constructs a new {@code MoveAction}.
068     * @param dir direction
069     */
070    public MoveAction(Direction dir) {
071        super(tr("Move {0}", calltosupermustbefirststatementinconstructortext(dir)), null,
072                tr("Moves Objects {0}", calltosupermustbefirststatementinconstructortext(dir)),
073                calltosupermustbefirststatementinconstructor(dir), false);
074        myDirection = dir;
075        putValue("help", ht("/Action/Move"));
076        if (dir == Direction.UP) {
077            putValue("toolbar", "action/move/up");
078        } else if (dir == Direction.DOWN) {
079            putValue("toolbar", "action/move/down");
080        } else if (dir == Direction.LEFT) {
081            putValue("toolbar", "action/move/left");
082        } else { //dir == Direction.RIGHT
083            putValue("toolbar", "action/move/right");
084        }
085        Main.toolbar.register(this);
086    }
087
088    @Override
089    public void actionPerformed(ActionEvent event) {
090
091        if (!Main.isDisplayingMapView())
092            return;
093
094        // find out how many "real" units the objects have to be moved in order to
095        // achive an 1-pixel movement
096
097        EastNorth en1 = Main.map.mapView.getEastNorth(100, 100);
098        EastNorth en2 = Main.map.mapView.getEastNorth(101, 101);
099
100        double distx = en2.east() - en1.east();
101        double disty = en2.north() - en1.north();
102
103        switch (myDirection) {
104        case UP:
105            distx = 0;
106            disty = -disty;
107            break;
108        case DOWN:
109            distx = 0;
110            break;
111        case LEFT:
112            disty = 0;
113            distx = -distx;
114            break;
115        default:
116            disty = 0;
117        }
118
119        DataSet ds = getLayerManager().getEditDataSet();
120        Collection<OsmPrimitive> selection = ds.getSelected();
121        Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection);
122
123        Command c = !Main.main.undoRedo.commands.isEmpty()
124        ? Main.main.undoRedo.commands.getLast() : null;
125
126        ds.beginUpdate();
127        if (c instanceof MoveCommand && affectedNodes.equals(((MoveCommand) c).getParticipatingPrimitives())) {
128            ((MoveCommand) c).moveAgain(distx, disty);
129        } else {
130            c = new MoveCommand(selection, distx, disty);
131            Main.main.undoRedo.add(c);
132        }
133        ds.endUpdate();
134
135        for (Node n : affectedNodes) {
136            if (n.getCoor().isOutSideWorld()) {
137                // Revert move
138                ((MoveCommand) c).moveAgain(-distx, -disty);
139                JOptionPane.showMessageDialog(
140                        Main.parent,
141                        tr("Cannot move objects outside of the world."),
142                        tr("Warning"),
143                        JOptionPane.WARNING_MESSAGE
144                );
145                return;
146            }
147        }
148
149        Main.map.mapView.repaint();
150    }
151
152    @Override
153    protected void updateEnabledState() {
154        updateEnabledStateOnCurrentSelection();
155    }
156
157    @Override
158    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
159        setEnabled(selection != null && !selection.isEmpty());
160    }
161}