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