[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[3] | 2 | package org.openstreetmap.josm.gui;
|
---|
| 3 |
|
---|
[1939] | 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
[3] | 6 | import java.awt.Cursor;
|
---|
[102] | 7 | import java.awt.Point;
|
---|
[101] | 8 | import java.awt.event.ActionEvent;
|
---|
| 9 | import java.awt.event.KeyEvent;
|
---|
[3] | 10 | import java.awt.event.MouseAdapter;
|
---|
| 11 | import java.awt.event.MouseEvent;
|
---|
| 12 | import java.awt.event.MouseMotionListener;
|
---|
| 13 | import java.awt.event.MouseWheelEvent;
|
---|
| 14 | import java.awt.event.MouseWheelListener;
|
---|
| 15 |
|
---|
[101] | 16 | import javax.swing.AbstractAction;
|
---|
[5472] | 17 | import javax.swing.ActionMap;
|
---|
| 18 | import javax.swing.InputMap;
|
---|
[101] | 19 | import javax.swing.JComponent;
|
---|
[237] | 20 | import javax.swing.JPanel;
|
---|
[5472] | 21 | import javax.swing.KeyStroke;
|
---|
[101] | 22 |
|
---|
[1939] | 23 | import org.openstreetmap.josm.Main;
|
---|
[7543] | 24 | import org.openstreetmap.josm.actions.mapmode.SelectAction;
|
---|
[71] | 25 | import org.openstreetmap.josm.data.coor.EastNorth;
|
---|
[5472] | 26 | import org.openstreetmap.josm.tools.Destroyable;
|
---|
[1939] | 27 | import org.openstreetmap.josm.tools.Shortcut;
|
---|
[3] | 28 |
|
---|
| 29 | /**
|
---|
| 30 | * Enables moving of the map by holding down the right mouse button and drag
|
---|
| 31 | * the mouse. Also, enables zooming by the mouse wheel.
|
---|
| 32 | *
|
---|
| 33 | * @author imi
|
---|
| 34 | */
|
---|
[5472] | 35 | public class MapMover extends MouseAdapter implements MouseMotionListener, MouseWheelListener, Destroyable {
|
---|
[3] | 36 |
|
---|
[1169] | 37 | private final class ZoomerAction extends AbstractAction {
|
---|
| 38 | private final String action;
|
---|
[8510] | 39 |
|
---|
[8836] | 40 | ZoomerAction(String action) {
|
---|
[1169] | 41 | this.action = action;
|
---|
[101] | 42 | }
|
---|
[8510] | 43 |
|
---|
[6084] | 44 | @Override
|
---|
[1169] | 45 | public void actionPerformed(ActionEvent e) {
|
---|
[6990] | 46 | if (".".equals(action) || ",".equals(action)) {
|
---|
[1169] | 47 | Point mouse = nc.getMousePosition();
|
---|
| 48 | if (mouse == null)
|
---|
[8510] | 49 | mouse = new Point((int) nc.getBounds().getCenterX(), (int) nc.getBounds().getCenterY());
|
---|
[7509] | 50 | MouseWheelEvent we = new MouseWheelEvent(nc, e.getID(), e.getWhen(), e.getModifiers(), mouse.x, mouse.y, 0, false,
|
---|
[6990] | 51 | MouseWheelEvent.WHEEL_UNIT_SCROLL, 1, ",".equals(action) ? -1 : 1);
|
---|
[1169] | 52 | mouseWheelMoved(we);
|
---|
| 53 | } else {
|
---|
| 54 | EastNorth center = nc.getCenter();
|
---|
| 55 | EastNorth newcenter = nc.getEastNorth(nc.getWidth()/2+nc.getWidth()/5, nc.getHeight()/2+nc.getHeight()/5);
|
---|
[7012] | 56 | switch(action) {
|
---|
| 57 | case "left":
|
---|
[1722] | 58 | nc.zoomTo(new EastNorth(2*center.east()-newcenter.east(), center.north()));
|
---|
[7012] | 59 | break;
|
---|
| 60 | case "right":
|
---|
[1722] | 61 | nc.zoomTo(new EastNorth(newcenter.east(), center.north()));
|
---|
[7012] | 62 | break;
|
---|
| 63 | case "up":
|
---|
[1722] | 64 | nc.zoomTo(new EastNorth(center.east(), 2*center.north()-newcenter.north()));
|
---|
[7012] | 65 | break;
|
---|
| 66 | case "down":
|
---|
[1722] | 67 | nc.zoomTo(new EastNorth(center.east(), newcenter.north()));
|
---|
[7012] | 68 | break;
|
---|
| 69 | }
|
---|
[1169] | 70 | }
|
---|
| 71 | }
|
---|
[101] | 72 | }
|
---|
| 73 |
|
---|
[1169] | 74 | /**
|
---|
| 75 | * The point in the map that was the under the mouse point
|
---|
| 76 | * when moving around started.
|
---|
| 77 | */
|
---|
| 78 | private EastNorth mousePosMove;
|
---|
| 79 | /**
|
---|
| 80 | * The map to move around.
|
---|
| 81 | */
|
---|
| 82 | private final NavigatableComponent nc;
|
---|
[5472] | 83 | private final JPanel contentPane;
|
---|
[3] | 84 |
|
---|
[8840] | 85 | private boolean movementInPlace;
|
---|
[101] | 86 |
|
---|
[1169] | 87 | /**
|
---|
[7543] | 88 | * Constructs a new {@code MapMover}.
|
---|
| 89 | * @param navComp the navigatable component
|
---|
| 90 | * @param contentPane the content pane
|
---|
[1169] | 91 | */
|
---|
| 92 | public MapMover(NavigatableComponent navComp, JPanel contentPane) {
|
---|
| 93 | this.nc = navComp;
|
---|
[5472] | 94 | this.contentPane = contentPane;
|
---|
[1169] | 95 | nc.addMouseListener(this);
|
---|
| 96 | nc.addMouseMotionListener(this);
|
---|
| 97 | nc.addMouseWheelListener(this);
|
---|
[101] | 98 |
|
---|
[1169] | 99 | if (contentPane != null) {
|
---|
[8540] | 100 | // CHECKSTYLE.OFF: LineLength
|
---|
[1169] | 101 | contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
---|
[4982] | 102 | Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.CTRL).getKeyStroke(),
|
---|
[1169] | 103 | "MapMover.Zoomer.right");
|
---|
| 104 | contentPane.getActionMap().put("MapMover.Zoomer.right", new ZoomerAction("right"));
|
---|
[1023] | 105 |
|
---|
[1169] | 106 | contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
---|
[4982] | 107 | Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.CTRL).getKeyStroke(),
|
---|
[1169] | 108 | "MapMover.Zoomer.left");
|
---|
| 109 | contentPane.getActionMap().put("MapMover.Zoomer.left", new ZoomerAction("left"));
|
---|
[1023] | 110 |
|
---|
[1169] | 111 | contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
---|
[4982] | 112 | Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.CTRL).getKeyStroke(),
|
---|
[1169] | 113 | "MapMover.Zoomer.up");
|
---|
| 114 | contentPane.getActionMap().put("MapMover.Zoomer.up", new ZoomerAction("up"));
|
---|
[1023] | 115 |
|
---|
[1169] | 116 | contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
---|
[4982] | 117 | Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.CTRL).getKeyStroke(),
|
---|
[1169] | 118 | "MapMover.Zoomer.down");
|
---|
| 119 | contentPane.getActionMap().put("MapMover.Zoomer.down", new ZoomerAction("down"));
|
---|
[8540] | 120 | // CHECKSTYLE.ON: LineLength
|
---|
[1023] | 121 |
|
---|
[7606] | 122 | // see #10592 - Disable these alternate shortcuts on OS X because of conflict with system shortcut
|
---|
| 123 | if (!Main.isPlatformOsx()) {
|
---|
| 124 | contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
---|
[8509] | 125 | Shortcut.registerShortcut("view:zoominalternate",
|
---|
| 126 | tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.CTRL).getKeyStroke(),
|
---|
[7606] | 127 | "MapMover.Zoomer.in");
|
---|
| 128 | contentPane.getActionMap().put("MapMover.Zoomer.in", new ZoomerAction(","));
|
---|
[1023] | 129 |
|
---|
[7606] | 130 | contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
---|
[8509] | 131 | Shortcut.registerShortcut("view:zoomoutalternate",
|
---|
| 132 | tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.CTRL).getKeyStroke(),
|
---|
[7606] | 133 | "MapMover.Zoomer.out");
|
---|
| 134 | contentPane.getActionMap().put("MapMover.Zoomer.out", new ZoomerAction("."));
|
---|
| 135 | }
|
---|
[1169] | 136 | }
|
---|
| 137 | }
|
---|
[101] | 138 |
|
---|
[1169] | 139 | /**
|
---|
[7543] | 140 | * If the right (and only the right) mouse button is pressed, move the map.
|
---|
[1169] | 141 | */
|
---|
[6084] | 142 | @Override
|
---|
[1169] | 143 | public void mouseDragged(MouseEvent e) {
|
---|
| 144 | int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
|
---|
[6989] | 145 | int macMouseMask = MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK;
|
---|
[7543] | 146 | boolean stdMovement = (e.getModifiersEx() & (MouseEvent.BUTTON3_DOWN_MASK | offMask)) == MouseEvent.BUTTON3_DOWN_MASK;
|
---|
| 147 | boolean macMovement = Main.isPlatformOsx() && e.getModifiersEx() == macMouseMask;
|
---|
| 148 | boolean allowedMode = !Main.map.mapModeSelect.equals(Main.map.mapMode)
|
---|
[7590] | 149 | || SelectAction.Mode.SELECT.equals(Main.map.mapModeSelect.getMode());
|
---|
[7543] | 150 | if (stdMovement || (macMovement && allowedMode)) {
|
---|
[1169] | 151 | if (mousePosMove == null)
|
---|
| 152 | startMovement(e);
|
---|
| 153 | EastNorth center = nc.getCenter();
|
---|
| 154 | EastNorth mouseCenter = nc.getEastNorth(e.getX(), e.getY());
|
---|
[1722] | 155 | nc.zoomTo(new EastNorth(
|
---|
[1169] | 156 | mousePosMove.east() + center.east() - mouseCenter.east(),
|
---|
[1722] | 157 | mousePosMove.north() + center.north() - mouseCenter.north()));
|
---|
[6957] | 158 | } else {
|
---|
[1169] | 159 | endMovement();
|
---|
[6957] | 160 | }
|
---|
[1169] | 161 | }
|
---|
[3] | 162 |
|
---|
[1169] | 163 | /**
|
---|
| 164 | * Start the movement, if it was the 3rd button (right button).
|
---|
| 165 | */
|
---|
[6957] | 166 | @Override
|
---|
| 167 | public void mousePressed(MouseEvent e) {
|
---|
[1169] | 168 | int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
|
---|
[1939] | 169 | int macMouseMask = MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK;
|
---|
[7509] | 170 | if (e.getButton() == MouseEvent.BUTTON3 && (e.getModifiersEx() & offMask) == 0 ||
|
---|
[6989] | 171 | Main.isPlatformOsx() && e.getModifiersEx() == macMouseMask) {
|
---|
[1169] | 172 | startMovement(e);
|
---|
[1939] | 173 | }
|
---|
[1169] | 174 | }
|
---|
[3] | 175 |
|
---|
[1169] | 176 | /**
|
---|
| 177 | * Change the cursor back to it's pre-move cursor.
|
---|
| 178 | */
|
---|
[6957] | 179 | @Override
|
---|
| 180 | public void mouseReleased(MouseEvent e) {
|
---|
[6989] | 181 | if (e.getButton() == MouseEvent.BUTTON3 || Main.isPlatformOsx() && e.getButton() == MouseEvent.BUTTON1) {
|
---|
[1169] | 182 | endMovement();
|
---|
[1939] | 183 | }
|
---|
[1169] | 184 | }
|
---|
[3] | 185 |
|
---|
[1169] | 186 | /**
|
---|
| 187 | * Start movement by setting a new cursor and remember the current mouse
|
---|
| 188 | * position.
|
---|
| 189 | * @param e The mouse event that leat to the movement from.
|
---|
| 190 | */
|
---|
| 191 | private void startMovement(MouseEvent e) {
|
---|
| 192 | if (movementInPlace)
|
---|
| 193 | return;
|
---|
| 194 | movementInPlace = true;
|
---|
| 195 | mousePosMove = nc.getEastNorth(e.getX(), e.getY());
|
---|
[3919] | 196 | nc.setNewCursor(Cursor.MOVE_CURSOR, this);
|
---|
[1169] | 197 | }
|
---|
[101] | 198 |
|
---|
[1169] | 199 | /**
|
---|
| 200 | * End the movement. Setting back the cursor and clear the movement variables
|
---|
| 201 | */
|
---|
| 202 | private void endMovement() {
|
---|
| 203 | if (!movementInPlace)
|
---|
| 204 | return;
|
---|
| 205 | movementInPlace = false;
|
---|
[3919] | 206 | nc.resetCursor(this);
|
---|
[1169] | 207 | mousePosMove = null;
|
---|
| 208 | }
|
---|
[3] | 209 |
|
---|
[1169] | 210 | /**
|
---|
| 211 | * Zoom the map by 1/5th of current zoom per wheel-delta.
|
---|
| 212 | * @param e The wheel event.
|
---|
| 213 | */
|
---|
[6084] | 214 | @Override
|
---|
[1169] | 215 | public void mouseWheelMoved(MouseWheelEvent e) {
|
---|
[4494] | 216 | nc.zoomToFactor(e.getX(), e.getY(), Math.pow(Math.sqrt(2), e.getWheelRotation()));
|
---|
[1169] | 217 | }
|
---|
[3] | 218 |
|
---|
[1169] | 219 | /**
|
---|
[6957] | 220 | * Emulates dragging on Mac OSX.
|
---|
[1169] | 221 | */
|
---|
[6084] | 222 | @Override
|
---|
[1939] | 223 | public void mouseMoved(MouseEvent e) {
|
---|
| 224 | if (!movementInPlace)
|
---|
| 225 | return;
|
---|
| 226 | // Mac OSX simulates with ctrl + mouse 1 the second mouse button hence no dragging events get fired.
|
---|
| 227 | // Is only the selected mouse button pressed?
|
---|
[6957] | 228 | if (Main.isPlatformOsx()) {
|
---|
[1939] | 229 | if (e.getModifiersEx() == MouseEvent.CTRL_DOWN_MASK) {
|
---|
| 230 | if (mousePosMove == null) {
|
---|
| 231 | startMovement(e);
|
---|
| 232 | }
|
---|
| 233 | EastNorth center = nc.getCenter();
|
---|
| 234 | EastNorth mouseCenter = nc.getEastNorth(e.getX(), e.getY());
|
---|
| 235 | nc.zoomTo(new EastNorth(mousePosMove.east() + center.east() - mouseCenter.east(), mousePosMove.north()
|
---|
| 236 | + center.north() - mouseCenter.north()));
|
---|
| 237 | } else {
|
---|
| 238 | endMovement();
|
---|
| 239 | }
|
---|
| 240 | }
|
---|
| 241 | }
|
---|
| 242 |
|
---|
[5472] | 243 | @Override
|
---|
| 244 | public void destroy() {
|
---|
| 245 | if (this.contentPane != null) {
|
---|
| 246 | InputMap inputMap = contentPane.getInputMap();
|
---|
| 247 | KeyStroke[] inputKeys = inputMap.keys();
|
---|
| 248 | if (inputKeys != null) {
|
---|
| 249 | for (KeyStroke key : inputKeys) {
|
---|
| 250 | Object binding = inputMap.get(key);
|
---|
[8510] | 251 | if (binding instanceof String && ((String) binding).startsWith("MapMover.")) {
|
---|
[5472] | 252 | inputMap.remove(key);
|
---|
| 253 | }
|
---|
| 254 | }
|
---|
| 255 | }
|
---|
| 256 | ActionMap actionMap = contentPane.getActionMap();
|
---|
| 257 | Object[] actionsKeys = actionMap.keys();
|
---|
| 258 | if (actionsKeys != null) {
|
---|
| 259 | for (Object key : actionsKeys) {
|
---|
[8510] | 260 | if (key instanceof String && ((String) key).startsWith("MapMover.")) {
|
---|
[5472] | 261 | actionMap.remove(key);
|
---|
| 262 | }
|
---|
| 263 | }
|
---|
| 264 | }
|
---|
| 265 | }
|
---|
| 266 | }
|
---|
[3] | 267 | }
|
---|