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