source: josm/trunk/src/org/openstreetmap/josm/gui/MapMover.java@ 9870

Last change on this file since 9870 was 9824, checked in by bastiK, 8 years ago

applied #12539 - reverse zoom with mouse wheel (patch by kolesar)

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