001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm.gui; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.Cursor; 007 import java.awt.Point; 008 import java.awt.event.ActionEvent; 009 import java.awt.event.KeyEvent; 010 import java.awt.event.MouseAdapter; 011 import java.awt.event.MouseEvent; 012 import java.awt.event.MouseMotionListener; 013 import java.awt.event.MouseWheelEvent; 014 import java.awt.event.MouseWheelListener; 015 016 import javax.swing.AbstractAction; 017 import javax.swing.ActionMap; 018 import javax.swing.InputMap; 019 import javax.swing.JComponent; 020 import javax.swing.JPanel; 021 import javax.swing.KeyStroke; 022 023 import org.openstreetmap.josm.Main; 024 import org.openstreetmap.josm.data.coor.EastNorth; 025 import org.openstreetmap.josm.tools.Destroyable; 026 import org.openstreetmap.josm.tools.PlatformHookOsx; 027 import org.openstreetmap.josm.tools.Shortcut; 028 029 /** 030 * Enables moving of the map by holding down the right mouse button and drag 031 * the mouse. Also, enables zooming by the mouse wheel. 032 * 033 * @author imi 034 */ 035 public class MapMover extends MouseAdapter implements MouseMotionListener, MouseWheelListener, Destroyable { 036 037 private final class ZoomerAction extends AbstractAction { 038 private final String action; 039 public ZoomerAction(String action) { 040 this.action = action; 041 } 042 public void actionPerformed(ActionEvent e) { 043 if (action.equals(".") || action.equals(",")) { 044 Point mouse = nc.getMousePosition(); 045 if (mouse == null) 046 mouse = new Point((int)nc.getBounds().getCenterX(), (int)nc.getBounds().getCenterY()); 047 MouseWheelEvent we = new MouseWheelEvent(nc, e.getID(), e.getWhen(), e.getModifiers(), mouse.x, mouse.y, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1, action.equals(",") ? -1 : 1); 048 mouseWheelMoved(we); 049 } else { 050 EastNorth center = nc.getCenter(); 051 EastNorth newcenter = nc.getEastNorth(nc.getWidth()/2+nc.getWidth()/5, nc.getHeight()/2+nc.getHeight()/5); 052 if (action.equals("left")) 053 nc.zoomTo(new EastNorth(2*center.east()-newcenter.east(), center.north())); 054 else if (action.equals("right")) 055 nc.zoomTo(new EastNorth(newcenter.east(), center.north())); 056 else if (action.equals("up")) 057 nc.zoomTo(new EastNorth(center.east(), 2*center.north()-newcenter.north())); 058 else if (action.equals("down")) 059 nc.zoomTo(new EastNorth(center.east(), newcenter.north())); 060 } 061 } 062 } 063 064 /** 065 * The point in the map that was the under the mouse point 066 * when moving around started. 067 */ 068 private EastNorth mousePosMove; 069 /** 070 * The map to move around. 071 */ 072 private final NavigatableComponent nc; 073 private final JPanel contentPane; 074 075 private boolean movementInPlace = false; 076 077 /** 078 * Create a new MapMover 079 */ 080 public MapMover(NavigatableComponent navComp, JPanel contentPane) { 081 this.nc = navComp; 082 this.contentPane = contentPane; 083 nc.addMouseListener(this); 084 nc.addMouseMotionListener(this); 085 nc.addMouseWheelListener(this); 086 087 if (contentPane != null) { 088 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 089 Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.CTRL).getKeyStroke(), 090 "MapMover.Zoomer.right"); 091 contentPane.getActionMap().put("MapMover.Zoomer.right", new ZoomerAction("right")); 092 093 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 094 Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.CTRL).getKeyStroke(), 095 "MapMover.Zoomer.left"); 096 contentPane.getActionMap().put("MapMover.Zoomer.left", new ZoomerAction("left")); 097 098 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 099 Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.CTRL).getKeyStroke(), 100 "MapMover.Zoomer.up"); 101 contentPane.getActionMap().put("MapMover.Zoomer.up", new ZoomerAction("up")); 102 103 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 104 Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.CTRL).getKeyStroke(), 105 "MapMover.Zoomer.down"); 106 contentPane.getActionMap().put("MapMover.Zoomer.down", new ZoomerAction("down")); 107 108 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 109 Shortcut.registerShortcut("view:zoominalternate", tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.CTRL).getKeyStroke(), 110 "MapMover.Zoomer.in"); 111 contentPane.getActionMap().put("MapMover.Zoomer.in", new ZoomerAction(",")); 112 113 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 114 Shortcut.registerShortcut("view:zoomoutalternate", tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.CTRL).getKeyStroke(), 115 "MapMover.Zoomer.out"); 116 contentPane.getActionMap().put("MapMover.Zoomer.out", new ZoomerAction(".")); 117 } 118 } 119 120 /** 121 * If the right (and only the right) mouse button is pressed, move the map 122 */ 123 public void mouseDragged(MouseEvent e) { 124 int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK; 125 if ((e.getModifiersEx() & (MouseEvent.BUTTON3_DOWN_MASK | offMask)) == MouseEvent.BUTTON3_DOWN_MASK) { 126 if (mousePosMove == null) 127 startMovement(e); 128 EastNorth center = nc.getCenter(); 129 EastNorth mouseCenter = nc.getEastNorth(e.getX(), e.getY()); 130 nc.zoomTo(new EastNorth( 131 mousePosMove.east() + center.east() - mouseCenter.east(), 132 mousePosMove.north() + center.north() - mouseCenter.north())); 133 } else 134 endMovement(); 135 } 136 137 /** 138 * Start the movement, if it was the 3rd button (right button). 139 */ 140 @Override public void mousePressed(MouseEvent e) { 141 int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK; 142 int macMouseMask = MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK; 143 if (e.getButton() == MouseEvent.BUTTON3 && (e.getModifiersEx() & offMask) == 0) { 144 startMovement(e); 145 } else if (isPlatformOsx() && e.getModifiersEx() == macMouseMask) { 146 startMovement(e); 147 } 148 } 149 150 /** 151 * Change the cursor back to it's pre-move cursor. 152 */ 153 @Override public void mouseReleased(MouseEvent e) { 154 if (e.getButton() == MouseEvent.BUTTON3) { 155 endMovement(); 156 } else if (isPlatformOsx() && e.getButton() == MouseEvent.BUTTON1) { 157 endMovement(); 158 } 159 } 160 161 /** 162 * Start movement by setting a new cursor and remember the current mouse 163 * position. 164 * @param e The mouse event that leat to the movement from. 165 */ 166 private void startMovement(MouseEvent e) { 167 if (movementInPlace) 168 return; 169 movementInPlace = true; 170 mousePosMove = nc.getEastNorth(e.getX(), e.getY()); 171 nc.setNewCursor(Cursor.MOVE_CURSOR, this); 172 } 173 174 /** 175 * End the movement. Setting back the cursor and clear the movement variables 176 */ 177 private void endMovement() { 178 if (!movementInPlace) 179 return; 180 movementInPlace = false; 181 nc.resetCursor(this); 182 mousePosMove = null; 183 } 184 185 /** 186 * Zoom the map by 1/5th of current zoom per wheel-delta. 187 * @param e The wheel event. 188 */ 189 public void mouseWheelMoved(MouseWheelEvent e) { 190 nc.zoomToFactor(e.getX(), e.getY(), Math.pow(Math.sqrt(2), e.getWheelRotation())); 191 } 192 193 /** 194 * Emulates dragging on Mac OSX 195 */ 196 public void mouseMoved(MouseEvent e) { 197 if (!movementInPlace) 198 return; 199 // Mac OSX simulates with ctrl + mouse 1 the second mouse button hence no dragging events get fired. 200 // Is only the selected mouse button pressed? 201 if (isPlatformOsx()) { 202 if (e.getModifiersEx() == MouseEvent.CTRL_DOWN_MASK) { 203 if (mousePosMove == null) { 204 startMovement(e); 205 } 206 EastNorth center = nc.getCenter(); 207 EastNorth mouseCenter = nc.getEastNorth(e.getX(), e.getY()); 208 nc.zoomTo(new EastNorth(mousePosMove.east() + center.east() - mouseCenter.east(), mousePosMove.north() 209 + center.north() - mouseCenter.north())); 210 } else { 211 endMovement(); 212 } 213 } 214 } 215 216 /** 217 * Replies true if we are currently running on OSX 218 * 219 * @return true if we are currently running on OSX 220 */ 221 public static boolean isPlatformOsx() { 222 return Main.platform != null && Main.platform instanceof PlatformHookOsx; 223 } 224 225 @Override 226 public void destroy() { 227 if (this.contentPane != null) { 228 InputMap inputMap = contentPane.getInputMap(); 229 KeyStroke[] inputKeys = inputMap.keys(); 230 if (inputKeys != null) { 231 for (KeyStroke key : inputKeys) { 232 Object binding = inputMap.get(key); 233 if (binding instanceof String && ((String)binding).startsWith("MapMover.")) { 234 inputMap.remove(key); 235 } 236 } 237 } 238 ActionMap actionMap = contentPane.getActionMap(); 239 Object[] actionsKeys = actionMap.keys(); 240 if (actionsKeys != null) { 241 for (Object key : actionsKeys) { 242 if (key instanceof String && ((String)key).startsWith("MapMover.")) { 243 actionMap.remove(key); 244 } 245 } 246 } 247 } 248 } 249 }