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