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}