source: josm/trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapController.java@ 17334

Last change on this file since 17334 was 17334, checked in by Don-vip, 4 years ago

see #20129 - rename file to match new class name

  • Property svn:eol-style set to native
File size: 11.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.bbox;
3
4import java.awt.Point;
5import java.awt.event.ActionEvent;
6import java.awt.event.InputEvent;
7import java.awt.event.KeyEvent;
8import java.awt.event.MouseAdapter;
9import java.awt.event.MouseEvent;
10import java.util.Timer;
11import java.util.TimerTask;
12
13import javax.swing.AbstractAction;
14import javax.swing.ActionMap;
15import javax.swing.InputMap;
16import javax.swing.JComponent;
17import javax.swing.JPanel;
18import javax.swing.KeyStroke;
19
20import org.openstreetmap.josm.tools.PlatformManager;
21
22/**
23 * This class controls the user input by listening to mouse and key events.
24 * Currently implemented is: - zooming in and out with scrollwheel - zooming in
25 * and centering by double clicking - selecting an area by clicking and dragging
26 * the mouse
27 *
28 * @author Tim Haussmann
29 */
30public class SlippyMapController extends MouseAdapter {
31
32 /** A Timer for smoothly moving the map area */
33 private static final Timer TIMER = new Timer(true);
34
35 /** Does the moving */
36 private MoveTask moveTask = new MoveTask();
37
38 /** How often to do the moving (milliseconds) */
39 private static final long timerInterval = 20;
40
41 /** The maximum speed (pixels per timer interval) */
42 private static final double MAX_SPEED = 20;
43
44 /** The speed increase per timer interval when a cursor button is clicked */
45 private static final double ACCELERATION = 0.10;
46
47 private static final int MAC_MOUSE_BUTTON3_MASK = MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK;
48
49 private static final String[] N = {
50 ",", ".", "up", "right", "down", "left"};
51 private static final int[] K = {
52 KeyEvent.VK_COMMA, KeyEvent.VK_PERIOD, KeyEvent.VK_UP, KeyEvent.VK_RIGHT, KeyEvent.VK_DOWN, KeyEvent.VK_LEFT};
53
54 // start and end point of selection rectangle
55 private Point iStartSelectionPoint;
56 private Point iEndSelectionPoint;
57
58 private final SlippyMapBBoxChooser iSlippyMapChooser;
59
60 private boolean isSelecting;
61
62 /**
63 * Constructs a new {@code SlippyMapController}.
64 * @param navComp navigable component
65 * @param contentPane content pane
66 */
67 public SlippyMapController(SlippyMapBBoxChooser navComp, JPanel contentPane) {
68 iSlippyMapChooser = navComp;
69 iSlippyMapChooser.addMouseListener(this);
70 iSlippyMapChooser.addMouseMotionListener(this);
71
72 if (contentPane != null) {
73 for (int i = 0; i < N.length; ++i) {
74 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
75 KeyStroke.getKeyStroke(K[i], KeyEvent.CTRL_DOWN_MASK), "MapMover.Zoomer." + N[i]);
76 }
77 }
78 isSelecting = false;
79
80 InputMap inputMap = navComp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
81 ActionMap actionMap = navComp.getActionMap();
82
83 // map moving
84 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "MOVE_RIGHT");
85 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "MOVE_LEFT");
86 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "MOVE_UP");
87 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "MOVE_DOWN");
88 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "STOP_MOVE_HORIZONTALLY");
89 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "STOP_MOVE_HORIZONTALLY");
90 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "STOP_MOVE_VERTICALLY");
91 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "STOP_MOVE_VERTICALLY");
92
93 // zooming. To avoid confusion about which modifier key to use,
94 // we just add all keys left of the space bar
95 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.CTRL_DOWN_MASK, false), "ZOOM_IN");
96 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.META_DOWN_MASK, false), "ZOOM_IN");
97 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK, false), "ZOOM_IN");
98 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0, false), "ZOOM_IN");
99 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0, false), "ZOOM_IN");
100 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0, false), "ZOOM_IN");
101 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.SHIFT_DOWN_MASK, false), "ZOOM_IN");
102 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.CTRL_DOWN_MASK, false), "ZOOM_OUT");
103 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.META_DOWN_MASK, false), "ZOOM_OUT");
104 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK, false), "ZOOM_OUT");
105 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0, false), "ZOOM_OUT");
106 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0, false), "ZOOM_OUT");
107
108 // action mapping
109 actionMap.put("MOVE_RIGHT", new MoveXAction(1));
110 actionMap.put("MOVE_LEFT", new MoveXAction(-1));
111 actionMap.put("MOVE_UP", new MoveYAction(-1));
112 actionMap.put("MOVE_DOWN", new MoveYAction(1));
113 actionMap.put("STOP_MOVE_HORIZONTALLY", new MoveXAction(0));
114 actionMap.put("STOP_MOVE_VERTICALLY", new MoveYAction(0));
115 actionMap.put("ZOOM_IN", new ZoomInAction());
116 actionMap.put("ZOOM_OUT", new ZoomOutAction());
117 }
118
119 /**
120 * Start drawing the selection rectangle if it was the 1st button (left button)
121 */
122 @Override
123 public void mousePressed(MouseEvent e) {
124 if (e.getButton() == MouseEvent.BUTTON1 && !(PlatformManager.isPlatformOsx() && e.getModifiersEx() == MAC_MOUSE_BUTTON3_MASK)) {
125 iStartSelectionPoint = e.getPoint();
126 iEndSelectionPoint = e.getPoint();
127 }
128 }
129
130 @Override
131 public void mouseDragged(MouseEvent e) {
132 if (iStartSelectionPoint != null && (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == MouseEvent.BUTTON1_DOWN_MASK
133 && !(PlatformManager.isPlatformOsx() && e.getModifiersEx() == MAC_MOUSE_BUTTON3_MASK)) {
134 iEndSelectionPoint = e.getPoint();
135 iSlippyMapChooser.setSelection(iStartSelectionPoint, iEndSelectionPoint);
136 isSelecting = true;
137 }
138 }
139
140 /**
141 * When dragging the map change the cursor back to it's pre-move cursor. If
142 * a double-click occurs center and zoom the map on the clicked location.
143 */
144 @Override
145 public void mouseReleased(MouseEvent e) {
146 if (e.getButton() == MouseEvent.BUTTON1) {
147
148 if (isSelecting && e.getClickCount() == 1) {
149 iSlippyMapChooser.setSelection(iStartSelectionPoint, e.getPoint());
150
151 // reset the selections start and end
152 iEndSelectionPoint = null;
153 iStartSelectionPoint = null;
154 isSelecting = false;
155
156 } else {
157 iSlippyMapChooser.handleAttribution(e.getPoint(), true);
158 }
159 }
160 }
161
162 @Override
163 public void mouseMoved(MouseEvent e) {
164 iSlippyMapChooser.handleMouseMoved(e.getPoint());
165 iSlippyMapChooser.handleAttribution(e.getPoint(), false);
166 }
167
168 private class MoveXAction extends AbstractAction {
169
170 private final int direction;
171
172 MoveXAction(int direction) {
173 this.direction = direction;
174 }
175
176 @Override
177 public void actionPerformed(ActionEvent e) {
178 moveTask.setDirectionX(direction);
179 }
180 }
181
182 private class MoveYAction extends AbstractAction {
183
184 private final int direction;
185
186 MoveYAction(int direction) {
187 this.direction = direction;
188 }
189
190 @Override
191 public void actionPerformed(ActionEvent e) {
192 moveTask.setDirectionY(direction);
193 }
194 }
195
196 /** Moves the map depending on which cursor keys are pressed (or not) */
197 private class MoveTask extends TimerTask {
198 /** The current x speed (pixels per timer interval) */
199 private double speedX = 1;
200
201 /** The current y speed (pixels per timer interval) */
202 private double speedY = 1;
203
204 /** The horizontal direction of movement, -1:left, 0:stop, 1:right */
205 private int directionX;
206
207 /** The vertical direction of movement, -1:up, 0:stop, 1:down */
208 private int directionY;
209
210 /**
211 * Indicated if <code>moveTask</code> is currently enabled (periodically
212 * executed via timer) or disabled
213 */
214 protected boolean scheduled;
215
216 protected void setDirectionX(int directionX) {
217 this.directionX = directionX;
218 updateScheduleStatus();
219 }
220
221 protected void setDirectionY(int directionY) {
222 this.directionY = directionY;
223 updateScheduleStatus();
224 }
225
226 private void updateScheduleStatus() {
227 boolean newMoveTaskState = !(directionX == 0 && directionY == 0);
228
229 if (newMoveTaskState != scheduled) {
230 scheduled = newMoveTaskState;
231 if (newMoveTaskState) {
232 TIMER.schedule(this, 0, timerInterval);
233 } else {
234 // We have to create a new instance because rescheduling a
235 // once canceled TimerTask is not possible
236 moveTask = new MoveTask();
237 cancel(); // Stop this TimerTask
238 }
239 }
240 }
241
242 @Override
243 public void run() {
244 // update the x speed
245 switch (directionX) {
246 case -1:
247 if (speedX > -1) {
248 speedX = -1;
249 }
250 if (speedX > -1 * MAX_SPEED) {
251 speedX -= ACCELERATION;
252 }
253 break;
254 case 0:
255 speedX = 0;
256 break;
257 case 1:
258 if (speedX < 1) {
259 speedX = 1;
260 }
261 if (speedX < MAX_SPEED) {
262 speedX += ACCELERATION;
263 }
264 break;
265 default:
266 throw new IllegalStateException(Integer.toString(directionX));
267 }
268
269 // update the y speed
270 switch (directionY) {
271 case -1:
272 if (speedY > -1) {
273 speedY = -1;
274 }
275 if (speedY > -1 * MAX_SPEED) {
276 speedY -= ACCELERATION;
277 }
278 break;
279 case 0:
280 speedY = 0;
281 break;
282 case 1:
283 if (speedY < 1) {
284 speedY = 1;
285 }
286 if (speedY < MAX_SPEED) {
287 speedY += ACCELERATION;
288 }
289 break;
290 default:
291 throw new IllegalStateException(Integer.toString(directionY));
292 }
293
294 // move the map
295 int moveX = (int) Math.floor(speedX);
296 int moveY = (int) Math.floor(speedY);
297 if (moveX != 0 || moveY != 0) {
298 iSlippyMapChooser.moveMap(moveX, moveY);
299 }
300 }
301 }
302
303 private class ZoomInAction extends AbstractAction {
304
305 @Override
306 public void actionPerformed(ActionEvent e) {
307 iSlippyMapChooser.zoomIn();
308 }
309 }
310
311 private class ZoomOutAction extends AbstractAction {
312
313 @Override
314 public void actionPerformed(ActionEvent e) {
315 iSlippyMapChooser.zoomOut();
316 }
317 }
318}
Note: See TracBrowser for help on using the repository browser.