source: josm/trunk/src/org/openstreetmap/josm/gui/SelectionManager.java@ 6340

Last change on this file since 6340 was 6084, checked in by bastiK, 11 years ago

see #8902 - add missing @Override annotations (patch by shinigami)

  • Property svn:eol-style set to native
File size: 12.7 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui;
3
4import java.awt.Component;
5import java.awt.Point;
6import java.awt.Polygon;
7import java.awt.Rectangle;
8import java.awt.event.InputEvent;
9import java.awt.event.MouseEvent;
10import java.awt.event.MouseListener;
11import java.awt.event.MouseMotionListener;
12import java.beans.PropertyChangeEvent;
13import java.beans.PropertyChangeListener;
14import java.util.Collection;
15import java.util.LinkedList;
16
17import org.openstreetmap.josm.data.osm.Node;
18import org.openstreetmap.josm.data.osm.OsmPrimitive;
19import org.openstreetmap.josm.data.osm.Way;
20
21/**
22 * Manages the selection of a rectangle. Listening to left and right mouse button
23 * presses and to mouse motions and draw the rectangle accordingly.
24 *
25 * Left mouse button selects a rectangle from the press until release. Pressing
26 * right mouse button while left is still pressed enable the rectangle to move
27 * around. Releasing the left button fires an action event to the listener given
28 * at constructor, except if the right is still pressed, which just remove the
29 * selection rectangle and does nothing.
30 *
31 * The point where the left mouse button was pressed and the current mouse
32 * position are two opposite corners of the selection rectangle.
33 *
34 * It is possible to specify an aspect ratio (width per height) which the
35 * selection rectangle always must have. In this case, the selection rectangle
36 * will be the largest window with this aspect ratio, where the position the left
37 * mouse button was pressed and the corner of the current mouse position are at
38 * opposite sites (the mouse position corner is the corner nearest to the mouse
39 * cursor).
40 *
41 * When the left mouse button was released, an ActionEvent is send to the
42 * ActionListener given at constructor. The source of this event is this manager.
43 *
44 * @author imi
45 */
46public class SelectionManager implements MouseListener, MouseMotionListener, PropertyChangeListener {
47
48 /**
49 * This is the interface that an user of SelectionManager has to implement
50 * to get informed when a selection closes.
51 * @author imi
52 */
53 public interface SelectionEnded {
54 /**
55 * Called, when the left mouse button was released.
56 * @param r The rectangle that is currently the selection.
57 * @param e The mouse event.
58 * @see InputEvent#getModifiersEx()
59 */
60 public void selectionEnded(Rectangle r, MouseEvent e);
61 /**
62 * Called to register the selection manager for "active" property.
63 * @param listener The listener to register
64 */
65 public void addPropertyChangeListener(PropertyChangeListener listener);
66 /**
67 * Called to remove the selection manager from the listener list
68 * for "active" property.
69 * @param listener The listener to register
70 */
71 public void removePropertyChangeListener(PropertyChangeListener listener);
72 }
73 /**
74 * The listener that receives the events after left mouse button is released.
75 */
76 private final SelectionEnded selectionEndedListener;
77 /**
78 * Position of the map when the mouse button was pressed.
79 * If this is not <code>null</code>, a rectangle is drawn on screen.
80 */
81 private Point mousePosStart;
82 /**
83 * Position of the map when the selection rectangle was last drawn.
84 */
85 private Point mousePos;
86 /**
87 * The Component, the selection rectangle is drawn onto.
88 */
89 private final NavigatableComponent nc;
90 /**
91 * Whether the selection rectangle must obtain the aspect ratio of the
92 * drawComponent.
93 */
94 private boolean aspectRatio;
95
96 private boolean lassoMode;
97 private Polygon lasso = new Polygon();
98
99 /**
100 * Create a new SelectionManager.
101 *
102 * @param selectionEndedListener The action listener that receives the event when
103 * the left button is released.
104 * @param aspectRatio If true, the selection window must obtain the aspect
105 * ratio of the drawComponent.
106 * @param navComp The component, the rectangle is drawn onto.
107 */
108 public SelectionManager(SelectionEnded selectionEndedListener, boolean aspectRatio, NavigatableComponent navComp) {
109 this.selectionEndedListener = selectionEndedListener;
110 this.aspectRatio = aspectRatio;
111 this.nc = navComp;
112 }
113
114 /**
115 * Register itself at the given event source.
116 * @param eventSource The emitter of the mouse events.
117 * @param lassoMode {@code true} to enable lasso mode, {@code false} to disable it.
118 */
119 public void register(NavigatableComponent eventSource, boolean lassoMode) {
120 this.lassoMode = lassoMode;
121 eventSource.addMouseListener(this);
122 eventSource.addMouseMotionListener(this);
123 selectionEndedListener.addPropertyChangeListener(this);
124 eventSource.addPropertyChangeListener("scale", new PropertyChangeListener(){
125 @Override
126 public void propertyChange(PropertyChangeEvent evt) {
127 if (mousePosStart != null) {
128 paintRect();
129 mousePos = mousePosStart = null;
130 }
131 }
132 });
133 }
134 /**
135 * Unregister itself from the given event source. If a selection rectangle is
136 * shown, hide it first.
137 *
138 * @param eventSource The emitter of the mouse events.
139 */
140 public void unregister(Component eventSource) {
141 eventSource.removeMouseListener(this);
142 eventSource.removeMouseMotionListener(this);
143 selectionEndedListener.removePropertyChangeListener(this);
144 }
145
146 /**
147 * If the correct button, from the "drawing rectangle" mode
148 */
149 @Override
150 public void mousePressed(MouseEvent e) {
151 if (e.getButton() == MouseEvent.BUTTON1) {
152 mousePosStart = mousePos = e.getPoint();
153
154 lasso.reset();
155 lasso.addPoint(mousePosStart.x, mousePosStart.y);
156 }
157 }
158
159 /**
160 * If the correct button is hold, draw the rectangle.
161 */
162 @Override
163 public void mouseDragged(MouseEvent e) {
164 int buttonPressed = e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK);
165
166 if (buttonPressed != 0) {
167 if (mousePosStart == null) {
168 mousePosStart = mousePos = e.getPoint();
169 }
170 if (!lassoMode) {
171 paintRect();
172 }
173 }
174
175 if (buttonPressed == MouseEvent.BUTTON1_DOWN_MASK) {
176 mousePos = e.getPoint();
177 if (lassoMode) {
178 paintLasso();
179 } else {
180 paintRect();
181 }
182 } else if (buttonPressed == (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) {
183 mousePosStart.x += e.getX()-mousePos.x;
184 mousePosStart.y += e.getY()-mousePos.y;
185 mousePos = e.getPoint();
186 paintRect();
187 }
188 }
189
190 /**
191 * Check the state of the keys and buttons and set the selection accordingly.
192 */
193 @Override
194 public void mouseReleased(MouseEvent e) {
195 if (e.getButton() != MouseEvent.BUTTON1)
196 return;
197 if (mousePos == null || mousePosStart == null)
198 return; // injected release from outside
199 // disable the selection rect
200 Rectangle r;
201 if (!lassoMode) {
202 nc.requestClearRect();
203 r = getSelectionRectangle();
204
205 lasso = rectToPolygon(r);
206 } else {
207 nc.requestClearPoly();
208 lasso.addPoint(mousePos.x, mousePos.y);
209 r = lasso.getBounds();
210 }
211 mousePosStart = null;
212 mousePos = null;
213
214 if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == 0) {
215 selectionEndedListener.selectionEnded(r, e);
216 }
217 }
218
219 /**
220 * Draws a selection rectangle on screen.
221 */
222 private void paintRect() {
223 if (mousePos == null || mousePosStart == null || mousePos == mousePosStart)
224 return;
225 nc.requestPaintRect(getSelectionRectangle());
226 }
227
228 private void paintLasso() {
229 if (mousePos == null || mousePosStart == null || mousePos == mousePosStart) {
230 return;
231 }
232 lasso.addPoint(mousePos.x, mousePos.y);
233 nc.requestPaintPoly(lasso);
234 }
235
236 /**
237 * Calculate and return the current selection rectangle
238 * @return A rectangle that spans from mousePos to mouseStartPos
239 */
240 private Rectangle getSelectionRectangle() {
241 int x = mousePosStart.x;
242 int y = mousePosStart.y;
243 int w = mousePos.x - mousePosStart.x;
244 int h = mousePos.y - mousePosStart.y;
245 if (w < 0) {
246 x += w;
247 w = -w;
248 }
249 if (h < 0) {
250 y += h;
251 h = -h;
252 }
253
254 if (aspectRatio) {
255 /* Keep the aspect ratio by growing the rectangle; the
256 * rectangle is always under the cursor. */
257 double aspectRatio = (double)nc.getWidth()/nc.getHeight();
258 if ((double)w/h < aspectRatio) {
259 int neww = (int)(h*aspectRatio);
260 if (mousePos.x < mousePosStart.x) {
261 x += w - neww;
262 }
263 w = neww;
264 } else {
265 int newh = (int)(w/aspectRatio);
266 if (mousePos.y < mousePosStart.y) {
267 y += h - newh;
268 }
269 h = newh;
270 }
271 }
272
273 return new Rectangle(x,y,w,h);
274 }
275
276 /**
277 * If the action goes inactive, remove the selection rectangle from screen
278 */
279 @Override
280 public void propertyChange(PropertyChangeEvent evt) {
281 if (evt.getPropertyName().equals("active") && !(Boolean)evt.getNewValue() && mousePosStart != null) {
282 paintRect();
283 mousePosStart = null;
284 mousePos = null;
285 }
286 }
287
288 /**
289 * Return a list of all objects in the selection, respecting the different
290 * modifier.
291 *
292 * @param alt Whether the alt key was pressed, which means select all
293 * objects that are touched, instead those which are completely covered.
294 * @return The collection of selected objects.
295 */
296 public Collection<OsmPrimitive> getSelectedObjects(boolean alt) {
297
298 Collection<OsmPrimitive> selection = new LinkedList<OsmPrimitive>();
299
300 // whether user only clicked, not dragged.
301 boolean clicked = false;
302 Rectangle bounding = lasso.getBounds();
303 if (bounding.height <= 2 && bounding.width <= 2) {
304 clicked = true;
305 }
306
307 if (clicked) {
308 Point center = new Point(lasso.xpoints[0], lasso.ypoints[0]);
309 OsmPrimitive osm = nc.getNearestNodeOrWay(center, OsmPrimitive.isSelectablePredicate, false);
310 if (osm != null) {
311 selection.add(osm);
312 }
313 } else {
314 // nodes
315 for (Node n : nc.getCurrentDataSet().getNodes()) {
316 if (n.isSelectable() && lasso.contains(nc.getPoint2D(n))) {
317 selection.add(n);
318 }
319 }
320
321 // ways
322 for (Way w : nc.getCurrentDataSet().getWays()) {
323 if (!w.isSelectable() || w.getNodesCount() == 0) {
324 continue;
325 }
326 if (alt) {
327 for (Node n : w.getNodes()) {
328 if (!n.isIncomplete() && lasso.contains(nc.getPoint2D(n))) {
329 selection.add(w);
330 break;
331 }
332 }
333 } else {
334 boolean allIn = true;
335 for (Node n : w.getNodes()) {
336 if (!n.isIncomplete() && !lasso.contains(nc.getPoint(n))) {
337 allIn = false;
338 break;
339 }
340 }
341 if (allIn) {
342 selection.add(w);
343 }
344 }
345 }
346 }
347 return selection;
348 }
349
350 private Polygon rectToPolygon(Rectangle r) {
351 Polygon poly = new Polygon();
352
353 poly.addPoint(r.x, r.y);
354 poly.addPoint(r.x, r.y + r.height);
355 poly.addPoint(r.x + r.width, r.y + r.height);
356 poly.addPoint(r.x + r.width, r.y);
357
358 return poly;
359 }
360
361 /**
362 * Enables or disables the lasso mode.
363 * @param lassoMode {@code true} to enable lasso mode, {@code false} to disable it.
364 */
365 public void setLassoMode(boolean lassoMode) {
366 this.lassoMode = lassoMode;
367 }
368
369 @Override
370 public void mouseClicked(MouseEvent e) {}
371 @Override
372 public void mouseEntered(MouseEvent e) {}
373 @Override
374 public void mouseExited(MouseEvent e) {}
375 @Override
376 public void mouseMoved(MouseEvent e) {}
377}
Note: See TracBrowser for help on using the repository browser.