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 calltosupermustbefirststatementinconstructor_text(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    public MoveAction(Direction dir) {
064        super(tr("Move {0}", calltosupermustbefirststatementinconstructor_text(dir)), null,
065                tr("Moves Objects {0}", calltosupermustbefirststatementinconstructor_text(dir)),
066                calltosupermustbefirststatementinconstructor(dir), false);
067        myDirection = dir;
068        putValue("help", ht("/Action/Move"));
069        if        (dir == Direction.UP)   {
070            putValue("toolbar", "action/move/up");
071        } else if (dir == Direction.DOWN)  {
072            putValue("toolbar", "action/move/down");
073        } else if (dir == Direction.LEFT)  {
074            putValue("toolbar", "action/move/left");
075        } else { //dir == Direction.RIGHT
076            putValue("toolbar", "action/move/right");
077        }
078        Main.toolbar.register(this);
079    }
080
081    @Override
082    public void actionPerformed(ActionEvent event) {
083
084        if (!Main.isDisplayingMapView())
085            return;
086
087        // find out how many "real" units the objects have to be moved in order to
088        // achive an 1-pixel movement
089
090        EastNorth en1 = Main.map.mapView.getEastNorth(100, 100);
091        EastNorth en2 = Main.map.mapView.getEastNorth(101, 101);
092
093        double distx = en2.east() - en1.east();
094        double disty = en2.north() - en1.north();
095
096        switch (myDirection) {
097        case UP:
098            distx = 0;
099            disty = -disty;
100            break;
101        case DOWN:
102            distx = 0;
103            break;
104        case LEFT:
105            disty = 0;
106            distx = -distx;
107            break;
108        default:
109            disty = 0;
110        }
111
112        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
113        Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection);
114
115        Command c = !Main.main.undoRedo.commands.isEmpty()
116        ? Main.main.undoRedo.commands.getLast() : null;
117
118        getCurrentDataSet().beginUpdate();
119        if (c instanceof MoveCommand && affectedNodes.equals(((MoveCommand)c).getParticipatingPrimitives())) {
120            ((MoveCommand)c).moveAgain(distx, disty);
121        } else {
122            Main.main.undoRedo.add(
123                    c = new MoveCommand(selection, distx, disty));
124        }
125        getCurrentDataSet().endUpdate();
126
127        for (Node n : affectedNodes) {
128            if (n.getCoor().isOutSideWorld()) {
129                // Revert move
130                ((MoveCommand) c).moveAgain(-distx, -disty);
131                JOptionPane.showMessageDialog(
132                        Main.parent,
133                        tr("Cannot move objects outside of the world."),
134                        tr("Warning"),
135                        JOptionPane.WARNING_MESSAGE
136                );
137                return;
138            }
139        }
140
141        Main.map.mapView.repaint();
142    }
143
144    @Override
145    protected void updateEnabledState() {
146        if (getCurrentDataSet() == null) {
147            setEnabled(false);
148        } else {
149            updateEnabledState(getCurrentDataSet().getSelected());
150        }
151    }
152
153    @Override
154    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
155        setEnabled(selection != null && !selection.isEmpty());
156    }
157}