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