Changeset 3594 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2010-10-09T00:37:35+02:00 (14 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
r3593 r3594 12 12 import java.awt.event.KeyEvent; 13 13 import java.awt.event.MouseEvent; 14 import java. util.ArrayList;14 import java.awt.geom.Point2D; 15 15 import java.util.Collection; 16 16 import java.util.Collections; 17 import java.util.HashSet;18 17 import java.util.Iterator; 19 18 import java.util.LinkedList; 20 import java.util.Set;21 import java.util.TreeSet;22 19 23 20 import javax.swing.JOptionPane; … … 47 44 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 48 45 import org.openstreetmap.josm.tools.ImageProvider; 46 import org.openstreetmap.josm.tools.Pair; 49 47 import org.openstreetmap.josm.tools.PlatformHookOsx; 50 48 import org.openstreetmap.josm.tools.Shortcut; … … 65 63 enum Mode { move, rotate, select } 66 64 private Mode mode = null; 65 private SelectionManager selectionManager; 66 67 private boolean cancelDrawMode = false; 68 private boolean didMouseDrag = false; 69 70 /** 71 * The component this SelectAction is associated with. 72 */ 73 private final MapView mv; 74 75 /** 76 * The old cursor before the user pressed the mouse button. 77 */ 78 private Cursor oldCursor; 79 80 /** 81 * The position of the mouse before the user moves a node. 82 */ 83 private Point mousePos; 84 85 /** 86 * The time of the user mouse down event. 87 */ 67 88 private long mouseDownTime = 0; 68 private boolean didMove = false;69 private boolean cancelDrawMode = false;70 private Node virtualNode = null;71 private Collection<WaySegment> virtualWays = new ArrayList<WaySegment>();72 73 /**74 * The old cursor before the user pressed the mouse button.75 */76 private Cursor oldCursor;77 /**78 * The position of the mouse before the user moves a node.79 */80 private Point mousePos;81 private SelectionManager selectionManager;82 89 83 90 /** … … 93 100 private int initialMoveThreshold; 94 101 private boolean initialMoveThresholdExceeded = false; 102 95 103 /** 96 104 * Create a new SelectAction … … 102 110 mapFrame, 103 111 getCursor("normal", "selection", Cursor.DEFAULT_CURSOR)); 112 mv = mapFrame.mapView; 104 113 putValue("help", "Action/Move/Move"); 105 selectionManager = new SelectionManager(this, false, m apFrame.mapView);114 selectionManager = new SelectionManager(this, false, mv); 106 115 initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200); 107 116 initialMoveThreshold = Main.pref.getInteger("edit.initial-move-threshold",5); … … 118 127 private void setCursor(Cursor c) { 119 128 if (oldCursor == null) { 120 oldCursor = Main.map.mapView.getCursor();121 Main.map.mapView.setCursor(c);129 oldCursor = mv.getCursor(); 130 mv.setCursor(c); 122 131 } 123 132 } … … 125 134 private void restoreCursor() { 126 135 if (oldCursor != null) { 127 Main.map.mapView.setCursor(oldCursor);136 mv.setCursor(oldCursor); 128 137 oldCursor = null; 129 138 } … … 132 141 @Override public void enterMode() { 133 142 super.enterMode(); 134 Main.map.mapView.addMouseListener(this);135 Main.map.mapView.addMouseMotionListener(this);136 Main.map.mapView.setVirtualNodesEnabled(143 mv.addMouseListener(this); 144 mv.addMouseMotionListener(this); 145 mv.setVirtualNodesEnabled( 137 146 Main.pref.getInteger("mappaint.node.virtual-size", 8) != 0); 138 147 } … … 140 149 @Override public void exitMode() { 141 150 super.exitMode(); 142 selectionManager.unregister( Main.map.mapView);143 Main.map.mapView.removeMouseListener(this);144 Main.map.mapView.removeMouseMotionListener(this);145 Main.map.mapView.setVirtualNodesEnabled(false);151 selectionManager.unregister(mv); 152 mv.removeMouseListener(this); 153 mv.removeMouseMotionListener(this); 154 mv.setVirtualNodesEnabled(false); 146 155 } 147 156 … … 152 161 */ 153 162 @Override public void mouseDragged(MouseEvent e) { 154 if(! Main.map.mapView.isActiveLayerVisible())163 if(!mv.isActiveLayerVisible()) 155 164 return; 156 165 … … 167 176 if (mode == Mode.move) { 168 177 setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 169 }170 171 if (mousePos == null) {172 mousePos = e.getPoint();173 return;174 178 } 175 179 … … 182 186 } 183 187 184 EastNorth mouseEN = Main.map.mapView.getEastNorth(e.getX(), e.getY());185 EastNorth mouseStartEN = Main.map.mapView.getEastNorth(mousePos.x, mousePos.y);188 EastNorth mouseEN = mv.getEastNorth(e.getX(), e.getY()); 189 EastNorth mouseStartEN = mv.getEastNorth(mousePos.x, mousePos.y); 186 190 double dx = mouseEN.east() - mouseStartEN.east(); 187 191 double dy = mouseEN.north() - mouseStartEN.north(); … … 255 259 } 256 260 257 Main.map.mapView.repaint();261 mv.repaint(); 258 262 mousePos = e.getPoint(); 259 263 260 didMo ve= true;264 didMouseDrag = true; 261 265 } 262 266 … … 269 273 } 270 274 271 private Collection<OsmPrimitive> getNearestCollectionVirtual(Point p) { 272 int snapDistance = Main.pref.getInteger("mappaint.node.virtual-snap-distance", 8); 273 int virtualSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70); 274 snapDistance *= snapDistance; 275 276 MapView c = Main.map.mapView; 277 Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected(); 278 279 // take nearest node 280 OsmPrimitive osm = c.getNearestNode(p, OsmPrimitive.isSelectablePredicate); 281 282 if (osm != null) { 283 for (Node n : c.getNearestNodes(p, OsmPrimitive.isSelectablePredicate)) { 284 if (sel.contains(n)) { 285 // take nearest selected node 286 osm = n; 287 break; 288 } 289 } 290 } else { 291 Node virtualWayNode = null; 275 private Node virtualNode = null; 276 private Collection<WaySegment> virtualWays = new LinkedList<WaySegment>(); 277 278 /** 279 * Calculate a virtual node if there is enough visual space to draw a crosshair 280 * node and the middle of a way segment is clicked. If the user drags the 281 * crosshair node, it will be added to all ways in <code>virtualWays</code>. 282 * 283 * @param e contains the point clicked 284 * @return whether <code>virtualNode</code> and <code>virtualWays</code> were setup. 285 */ 286 private boolean setupVirtual(MouseEvent e) { 287 if (Main.pref.getInteger("mappaint.node.virtual-size", 8) > 0) { 288 int virtualSnapDistSq = Main.pref.getInteger("mappaint.node.virtual-snap-distance", 8); 289 int virtualSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70); 290 virtualSnapDistSq *= virtualSnapDistSq; 291 292 Collection<WaySegment> selVirtualWays = new LinkedList<WaySegment>(); 293 Pair<Node, Node> vnp = null, wnp = new Pair<Node, Node>(null, null); 294 Point p = e.getPoint(); 292 295 Way w = null; 293 296 294 Collection<WaySegment> virtualWaysInSel = new ArrayList<WaySegment>(); 295 Collection<WaySegment> wss = c.getNearestWaySegments(p, OsmPrimitive.isSelectablePredicate); 296 for(WaySegment nearestWS : wss) { 297 if (nearestWS == null) { 298 continue; 299 } 300 301 w = nearestWS.way; 302 if (osm == null && sel.contains(w)) { 303 // take nearest selected way 304 osm = w; 305 } 306 307 if (Main.pref.getInteger("mappaint.node.virtual-size", 8) > 0) { 308 Point p1 = c.getPoint(w.getNode(nearestWS.lowerIndex)); 309 Point p2 = c.getPoint(w.getNode(nearestWS.lowerIndex+1)); 310 if(SimplePaintVisitor.isLargeSegment(p1, p2, virtualSpace)) 297 for(WaySegment ws : mv.getNearestWaySegments(p, OsmPrimitive.isSelectablePredicate)) { 298 w = ws.way; 299 300 Point2D p1 = mv.getPoint2D(wnp.a = w.getNode(ws.lowerIndex)); 301 Point2D p2 = mv.getPoint2D(wnp.b = w.getNode(ws.lowerIndex+1)); 302 if(SimplePaintVisitor.isLargeSegment(p1, p2, virtualSpace)) 303 { 304 Point2D pc = new Point2D.Double((p1.getX()+p2.getX())/2, (p1.getY()+p2.getY())/2); 305 if (p.distanceSq(pc) < virtualSnapDistSq) 311 306 { 312 Point pc = new Point((p1.x+p2.x)/2, (p1.y+p2.y)/2); 313 if (p.distanceSq(pc) < snapDistance) 314 { 315 // Check that only segments on top of each other get added to the 316 // virtual ways list. Otherwise ways that coincidentally have their 317 // virtual node at the same spot will be joined which is likely unwanted 318 if(virtualWayNode != null) { 319 if(!w.getNode(nearestWS.lowerIndex+1).equals(virtualWayNode) 320 && !w.getNode(nearestWS.lowerIndex).equals(virtualWayNode)) { 321 continue; 322 } 323 } else { 324 virtualWayNode = w.getNode(nearestWS.lowerIndex+1); 325 } 326 327 (!sel.contains(w) ? virtualWays : virtualWaysInSel).add(nearestWS); 328 if(virtualNode == null) { 329 virtualNode = new Node(Main.map.mapView.getLatLon(pc.x, pc.y)); 330 } 307 // Check that only segments on top of each other get added to the 308 // virtual ways list. Otherwise ways that coincidentally have their 309 // virtual node at the same spot will be joined which is likely unwanted 310 Pair.sort(wnp); 311 if (vnp == null) { 312 vnp = new Pair<Node, Node>(wnp.a, wnp.b); 313 virtualNode = new Node(mv.getLatLon(pc.getX(), pc.getY())); 331 314 } 332 } 333 } 334 } 335 336 if (virtualNode != null) { 337 // insert virtualNode into all segments if nothing was selected, 338 // else only into the (previously) selected segments 339 virtualWays = virtualWaysInSel.isEmpty() ? virtualWays : virtualWaysInSel; 340 } 341 342 if (osm == null && !wss.isEmpty()) { 343 // take nearest way 344 osm = wss.iterator().next().way; 345 } 346 } 347 348 if (osm == null) 349 return Collections.emptySet(); 350 return Collections.singleton(osm); 315 if (vnp.equals(wnp)) { 316 (w.isSelected() ? selVirtualWays : virtualWays).add(ws); 317 } 318 } 319 } 320 } 321 322 if (!selVirtualWays.isEmpty()) { 323 virtualWays = selVirtualWays; 324 } 325 } 326 327 return !virtualWays.isEmpty(); 328 } 329 330 private Collection<OsmPrimitive> cycleList = Collections.emptyList(); 331 private boolean cyclePrims = false; 332 private OsmPrimitive cycleStart = null; 333 334 /** 335 * 336 * @param osm nearest primitive found by simple method 337 * @param e 338 * @return 339 */ 340 private Collection<OsmPrimitive> cycleSetup(Collection<OsmPrimitive> single, MouseEvent e) { 341 OsmPrimitive osm = null; 342 343 if (single == null) { 344 single = MapView.asColl( 345 mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, false)); 346 } 347 348 if (!single.isEmpty()) { 349 boolean waitForMouseUp = Main.pref.getBoolean("mappaint.select.waits-for-mouse-up", false); 350 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 351 boolean alt = ((e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0 352 || Main.pref.getBoolean("selectaction.cycles.multiple.matches", false)); 353 354 Point p = e.getPoint(); 355 osm = single.iterator().next(); 356 357 if (!alt) { 358 cycleList = MapView.asColl(osm); 359 if (waitForMouseUp) { 360 // find a selected nearest node or way, not the true nearest.. 361 osm = mv.getNearestNodeOrWay(p, OsmPrimitive.isSelectablePredicate, true); 362 } 363 } else { 364 if (osm instanceof Node) { 365 cycleList = new LinkedList<OsmPrimitive>(mv.getNearestNodes(p, OsmPrimitive.isSelectablePredicate)); 366 } else if (osm instanceof Way) { 367 cycleList = new LinkedList<OsmPrimitive>(mv.getNearestWays(p, OsmPrimitive.isSelectablePredicate)); 368 } 369 370 if (!waitForMouseUp && cycleList.size()>1) { 371 cyclePrims = false; 372 373 OsmPrimitive old = osm; 374 for (OsmPrimitive o : cycleList) { 375 if (o.isSelected()) { 376 cyclePrims = true; 377 osm = o; 378 break; 379 } 380 } 381 382 // for cycle groups of 2, we can toggle to the true nearest 383 // primitive on mouse presses, if it is not selected and if ctrl is not used 384 // else, if rotation is possible, defer sel change to mouse release 385 if (cycleList.size()==2) { 386 if (!(old.equals(osm) || ctrl)) { 387 cyclePrims = false; 388 osm = old; 389 } 390 } 391 } 392 } 393 } 394 395 return MapView.asColl(osm); 351 396 } 352 397 … … 361 406 */ 362 407 @Override public void mousePressed(MouseEvent e) { 363 if(!Main.map.mapView.isActiveLayerVisible()) 408 debug("mousePressed: e.getPoint()=" + e.getPoint()); 409 410 // return early 411 if(!mv.isActiveLayerVisible() 412 || !(Boolean)this.getValue("active") 413 || e.getButton() != MouseEvent.BUTTON1) 364 414 return; 365 415 366 416 // request focus in order to enable the expected keyboard shortcuts 367 Main.map.mapView.requestFocus(); 368 369 cancelDrawMode = false; 370 if (! (Boolean)this.getValue("active")) return; 371 if (e.getButton() != MouseEvent.BUTTON1) 372 return; 417 mv.requestFocus(); 418 373 419 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 374 420 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; … … 376 422 // We don't want to change to draw tool if the user tries to (de)select 377 423 // stuff but accidentally clicks in an empty area when selection is empty 378 if(shift || ctrl) { 379 cancelDrawMode = true; 380 } 381 424 cancelDrawMode = (shift || ctrl); 425 didMouseDrag = false; 426 initialMoveThresholdExceeded = false; 382 427 mouseDownTime = System.currentTimeMillis(); 383 didMove = false; 384 initialMoveThresholdExceeded = false; 385 386 Collection<OsmPrimitive> osmColl = getNearestCollectionVirtual(e.getPoint()); 387 388 if (ctrl && shift) { 428 mousePos = e.getPoint(); 429 430 Collection<OsmPrimitive> c = MapView.asColl( 431 mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, false)); 432 433 if (shift && ctrl) { 434 mode = Mode.rotate; 435 389 436 if (getCurrentDataSet().getSelected().isEmpty()) { 390 selectPrims(osmColl, true, false, false, false); 391 } 392 mode = Mode.rotate; 437 getCurrentDataSet().setSelected(c); 438 } 439 440 // Mode.select redraws when selectPrims is called 441 // Mode.move redraws when mouseDragged is called 442 // Mode.rotate redraws here 393 443 setCursor(ImageProvider.getCursor("rotate", null)); 394 } else if (!osmColl.isEmpty()) { 395 // Don't replace the selection now if the user clicked on a 396 // selected object (this would break moving of selected groups). 397 // We'll do that later in mouseReleased if the user didn't try to 398 // move. 399 selectPrims(osmColl, 400 shift || getCurrentDataSet().getSelected().containsAll(osmColl), 401 ctrl, false, false); 444 mv.repaint(); 445 } else if (!c.isEmpty()) { 402 446 mode = Mode.move; 447 448 if (!cancelDrawMode && c.iterator().next() instanceof Way) { 449 setupVirtual(e); 450 } 451 452 selectPrims(cycleSetup(c, e), e, false, false); 403 453 } else { 404 454 mode = Mode.select; 405 oldCursor = Main.map.mapView.getCursor(); 406 selectionManager.register(Main.map.mapView); 455 456 oldCursor = mv.getCursor(); 457 selectionManager.register(mv); 407 458 selectionManager.mousePressed(e); 408 459 } 409 460 410 if(mode != Mode.move || shift || ctrl) {411 virtualNode = null;412 virtualWays.clear();413 }414 415 461 updateStatusLine(); 416 // Mode.select redraws when selectPrims is called 417 // Mode.move redraws when mouseDragged is called 418 // Mode.rotate redraws here 419 if(mode == Mode.rotate) { 420 Main.map.mapView.repaint(); 421 } 422 423 mousePos = e.getPoint(); 424 } 425 426 /** 427 * Restore the old mouse cursor. 428 */ 429 @Override public void mouseReleased(MouseEvent e) { 430 if(!Main.map.mapView.isActiveLayerVisible()) 462 } 463 464 @Override 465 public void mouseReleased(MouseEvent e) { 466 debug("mouseReleased: e.getPoint()=" + e.getPoint()); 467 468 if(!mv.isActiveLayerVisible()) 431 469 return; 432 470 471 restoreCursor(); 433 472 if (mode == Mode.select) { 434 selectionManager.unregister( Main.map.mapView);473 selectionManager.unregister(mv); 435 474 436 475 // Select Draw Tool if no selection has been made … … 440 479 } 441 480 } 442 restoreCursor();443 481 444 482 if (mode == Mode.move) { 445 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 446 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 447 boolean alt = (e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0 448 || Main.pref.getBoolean("selectaction.cycles.multiple.matches", false); 449 450 virtualWays.clear(); 451 virtualNode = null; 452 453 if (!didMove) { 454 Collection<OsmPrimitive> c = Main.map.mapView.getNearestCollection(e.getPoint(), OsmPrimitive.isSelectablePredicate); 455 if (!c.isEmpty() && alt) { 456 if (c.iterator().next() instanceof Node) { 457 // consider all nearest nodes 458 c = new ArrayList<OsmPrimitive>(Main.map.mapView.getNearestNodes(e.getPoint(), OsmPrimitive.isSelectablePredicate)); 459 } else { 460 // consider all nearest primitives (should be only ways at this point..) 461 c = Main.map.mapView.getAllNearest(e.getPoint(), OsmPrimitive.isSelectablePredicate); 462 } 463 } 464 selectPrims(c, shift, ctrl, true, false); 465 466 // If the user double-clicked a node, change to draw mode 467 c = getCurrentDataSet().getSelected(); 468 if(e.getClickCount() >=2 && c.size() == 1 && c.iterator().next() instanceof Node) { 469 // We need to do it like this as otherwise drawAction will see a double 470 // click and switch back to SelectMode 471 Main.worker.execute(new Runnable(){ 472 public void run() { 473 Main.map.selectDrawTool(true); 474 } 475 }); 476 return; 483 if (!didMouseDrag) { 484 // only built in move mode 485 virtualWays.clear(); 486 virtualNode = null; 487 488 // do nothing if the click was to short to be recognized as a drag, 489 // but the release position is farther than 10px away from the press position 490 if (mousePos.distanceSq(e.getPoint())<100) { 491 selectPrims(cyclePrims(cycleList, e), e, true, false); 492 493 // If the user double-clicked a node, change to draw mode 494 Collection<OsmPrimitive> c = getCurrentDataSet().getSelected(); 495 if(e.getClickCount() >=2 && c.size() == 1 && c.iterator().next() instanceof Node) { 496 // We need to do it like this as otherwise drawAction will see a double 497 // click and switch back to SelectMode 498 Main.worker.execute(new Runnable(){ 499 public void run() { 500 Main.map.selectDrawTool(true); 501 } 502 }); 503 return; 504 } 477 505 } 478 506 } else { 479 Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected(); 480 Collection<OsmPrimitive> s = new TreeSet<OsmPrimitive>(); 481 int max = Main.pref.getInteger("warn.move.maxelements", 20); 482 for (OsmPrimitive osm : selection) 483 { 484 if(osm instanceof Node) { 485 s.add(osm); 486 } else if(osm instanceof Way) 487 { 488 s.add(osm); 489 s.addAll(((Way)osm).getNodes()); 490 } 491 if(s.size() > max) 492 { 493 ExtendedDialog ed = new ExtendedDialog( 494 Main.parent, 495 tr("Move elements"), 496 new String[] {tr("Move them"), tr("Undo move")}); 497 ed.setButtonIcons(new String[] {"reorder.png", "cancel.png"}); 498 ed.setContent(tr("You moved more than {0} elements. " 499 + "Moving a large number of elements is often an error.\n" 500 + "Really move them?", max)); 501 ed.setCancelButton(2); 502 ed.toggleEnable("movedManyElements"); 503 ed.showDialog(); 504 505 if(ed.getValue() != 1) 506 { 507 Main.main.undoRedo.undo(); 508 } 507 int max = Main.pref.getInteger("warn.move.maxelements", 20), limit = max; 508 for (OsmPrimitive osm : getCurrentDataSet().getSelected()) { 509 if (osm instanceof Way) { 510 limit -= ((Way)osm).getNodes().size(); 511 } 512 if ((limit -= 1) < 0) { 509 513 break; 510 514 } 511 515 } 512 if (ctrl) { 513 Collection<Node> affectedNodes = OsmPrimitive.getFilteredSet(selection, Node.class); 514 Collection<Node> nn = Main.map.mapView.getNearestNodes(e.getPoint(), affectedNodes, OsmPrimitive.isSelectablePredicate); 515 if (nn != null) { 516 Node targetNode = nn.iterator().next(); 517 Set<Node> nodesToMerge = new HashSet<Node>(affectedNodes); 518 nodesToMerge.add(targetNode); 519 if (!nodesToMerge.isEmpty()) { 520 Command cmd = MergeNodesAction.mergeNodes(Main.main.getEditLayer(),nodesToMerge, targetNode); 521 if(cmd != null) { 522 Main.main.undoRedo.add(cmd); 523 } 524 } 525 } 516 if (limit < 0) { 517 ExtendedDialog ed = new ExtendedDialog( 518 Main.parent, 519 tr("Move elements"), 520 new String[] {tr("Move them"), tr("Undo move")}); 521 ed.setButtonIcons(new String[] {"reorder.png", "cancel.png"}); 522 ed.setContent(tr("You moved more than {0} elements. " 523 + "Moving a large number of elements is often an error.\n" 524 + "Really move them?", max)); 525 ed.setCancelButton(2); 526 ed.toggleEnable("movedManyElements"); 527 ed.showDialog(); 528 529 if(ed.getValue() != 1) { 530 Main.main.undoRedo.undo(); 531 } 532 } else { 533 mergePrims(getCurrentDataSet().getSelectedNodes(), e); 526 534 } 527 535 getCurrentDataSet().fireSelectionChanged(); … … 535 543 } 536 544 537 public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) { 538 selectPrims(selectionManager.getObjectsInRectangle(r, alt), shift, ctrl, true, true); 539 } 540 541 private boolean selMorePrims = false; 542 private OsmPrimitive selCycleStart = null; 543 544 public void selectPrims(Collection<OsmPrimitive> selectionList, boolean shift, 545 boolean ctrl, boolean released, boolean area) { 545 public void selectionEnded(Rectangle r, MouseEvent e) { 546 boolean alt = (e.getModifiersEx() & (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK)) != 0; 547 selectPrims(selectionManager.getObjectsInRectangle(r, alt), e, true, true); 548 } 549 550 /** 551 * Modifies current selection state and returns the next element in a 552 * selection cycle given by <code>prims</code>. 553 * @param prims the primitives that form the selection cycle 554 * @param shift whether shift is pressed 555 * @param ctrl whether ctrl is pressed 556 * @return the next element of cycle list <code>prims</code>. 557 */ 558 private Collection<OsmPrimitive> cyclePrims(Collection<OsmPrimitive> prims, MouseEvent e) { 559 OsmPrimitive nxt = null; 560 561 debug("cyclePrims(): entry....."); 562 for (OsmPrimitive osm : prims) { 563 debug("cyclePrims(): prims id=" + osm.getId()); 564 } 565 566 if (prims.size() > 1) { 567 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 568 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 569 570 DataSet ds = getCurrentDataSet(); 571 OsmPrimitive first = prims.iterator().next(), foundInDS = null; 572 nxt = first; 573 574 for (Iterator<OsmPrimitive> i = prims.iterator(); i.hasNext(); ) { 575 if (cyclePrims && shift) { 576 if (!(nxt = i.next()).isSelected()) { 577 debug("cyclePrims(): taking " + nxt.getId()); 578 break; // take first primitive in prims list not in sel 579 } 580 } else { 581 if ((nxt = i.next()).isSelected()) { 582 foundInDS = nxt; 583 if (cyclePrims || ctrl) { 584 ds.clearSelection(foundInDS); 585 nxt = i.hasNext() ? i.next() : first; 586 } 587 debug("selectPrims(): taking " + nxt.getId()); 588 break; // take next primitive in prims list 589 } 590 } 591 } 592 593 if (ctrl) { 594 // a member of prims was found in the current dataset selection 595 if (foundInDS != null) { 596 // mouse was moved to a different selection group w/ a previous sel 597 if (!prims.contains(cycleStart)) { 598 ds.clearSelection(prims); 599 cycleStart = foundInDS; 600 debug("selectPrims(): cycleStart set to foundInDS=" + cycleStart.getId()); 601 } else if (cycleStart.equals(nxt)) { 602 // loop detected, insert deselect step 603 ds.addSelected(nxt); 604 debug("selectPrims(): cycleStart hit"); 605 } 606 } else { 607 // setup for iterating a sel group again or a new, different one.. 608 nxt = (prims.contains(cycleStart)) ? cycleStart : first; 609 cycleStart = nxt; 610 debug("selectPrims(): cycleStart set to nxt=" + cycleStart.getId()); 611 } 612 } else { 613 cycleStart = null; 614 } 615 616 debug("cyclePrims(): truncated prims list to id=" + nxt.getId()); 617 } 618 619 // pass on prims, if it had less than 2 elements 620 return (nxt != null) ? MapView.asColl(nxt) : prims; 621 } 622 623 private void mergePrims(Collection<Node> affectedNodes, MouseEvent e) { 624 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 625 626 if (ctrl && !affectedNodes.isEmpty()) { 627 Collection<Node> target = mv.getNearestNodes(e.getPoint(), affectedNodes, OsmPrimitive.isSelectablePredicate); 628 if (!target.isEmpty()) { 629 Collection<Node> nodesToMerge = new LinkedList<Node>(affectedNodes); 630 nodesToMerge.add(target.iterator().next()); 631 632 Command cmd = MergeNodesAction.mergeNodes(Main.main.getEditLayer(), nodesToMerge, target.iterator().next()); 633 if(cmd != null) { 634 Main.main.undoRedo.add(cmd); 635 } 636 } 637 } 638 } 639 640 private void selectPrims(Collection<OsmPrimitive> prims, MouseEvent e, boolean released, boolean area) { 641 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 642 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 546 643 DataSet ds = getCurrentDataSet(); 547 548 // decides on mousePressed whether549 // to cycle on mouseReleased (selList already selected)550 // or not (selList is a new selection)551 selMorePrims = (released || area) ? selMorePrims : ds.getSelected().containsAll(selectionList);552 644 553 645 // not allowed together: do not change dataset selection, return early … … 555 647 return; 556 648 557 // toggle through possible objects on mouse release 558 if (released && !area) { 559 if (selectionList.size() > 1) { 560 Collection<OsmPrimitive> coll = ds.getSelected(); 561 562 OsmPrimitive first, foundInDS, node, nxt; 563 first = nxt = selectionList.iterator().next(); 564 foundInDS = node = null; 565 566 for (Iterator<OsmPrimitive> i = selectionList.iterator(); i.hasNext(); ) { 567 if (selMorePrims && shift) { 568 if (!coll.contains(nxt = i.next())) { 569 break; // take first primitive not in dsSel or last if all contained 570 } 571 } else { 572 if (coll.contains(nxt = i.next())) { 573 foundInDS = nxt; 574 if (selMorePrims || ctrl) { 575 ds.clearSelection(nxt); 576 nxt = i.hasNext() ? i.next() : first; 577 } 578 break; // take next primitive of selList 579 } else if (nxt instanceof Node && node == null) { 580 node = nxt; 581 } 582 } 583 } 584 585 if (ctrl) { 586 if (foundInDS != null) { 587 // a member of selList was foundInDS 588 if (!selectionList.contains(selCycleStart)) { 589 selCycleStart = foundInDS; 590 } 591 // check if selCycleStart == prim (equals next(foundInDS)) 592 if (selCycleStart.equals(nxt)) { 593 ds.addSelected(nxt); // cycle complete, prim toggled below 594 selCycleStart = null; // check: might do w/out ?? 595 } 596 } else { 597 // no member of selList was foundInDS (sets were disjunct), setup for new cycle 598 selCycleStart = nxt = (node != null) ? node : first; 599 } 600 } 601 602 selectionList = new ArrayList<OsmPrimitive>(1); // do not modify the passed object.. 603 selectionList.add(nxt); 604 } 649 if (!released) { 650 // Don't replace the selection if the user clicked on a 651 // selected object (it breaks moving of selected groups). 652 // Do it later, on mouse release. 653 shift |= getCurrentDataSet().getSelected().containsAll(prims); 605 654 } 606 655 … … 610 659 // out of the selection. 611 660 if (area) { 612 ds.clearSelection( selectionList);661 ds.clearSelection(prims); 613 662 } else { 614 ds.toggleSelected(selectionList); 615 } 663 ds.toggleSelected(prims); 664 } 665 } else if (shift) { 666 // add prims to an existing selection 667 ds.addSelected(prims); 616 668 } else { 617 // plain clicks with no modifiers 618 if (!shift) { 619 ds.setSelected(selectionList); 620 } else { 621 // add things to an 622 // existing selection. 623 ds.addSelected(selectionList); 624 } 669 // clear selection, then select the prims clicked 670 ds.setSelected(prims); 625 671 } 626 672 } … … 640 686 return l instanceof OsmDataLayer; 641 687 } 688 689 private static void debug(String s) { 690 //System.err.println("SelectAction:" + s); 691 } 642 692 } -
trunk/src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java
r3443 r3594 6 6 import java.awt.Rectangle; 7 7 import java.awt.event.KeyEvent; 8 import java.awt.event.MouseEvent; 8 9 9 10 import org.openstreetmap.josm.Main; … … 50 51 * Zoom to the rectangle on the map. 51 52 */ 52 public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) {53 public void selectionEnded(Rectangle r, MouseEvent e) { 53 54 if (r.width >= 3 && r.height >= 3 && Main.isDisplayingMapView()) { 54 55 MapView mv = Main.map.mapView; -
trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/SimplePaintVisitor.java
r3565 r3594 13 13 import java.awt.Stroke; 14 14 import java.awt.geom.GeneralPath; 15 import java.awt.geom.Point2D; 15 16 import java.util.Collection; 16 17 import java.util.Iterator; … … 26 27 import org.openstreetmap.josm.data.osm.Way; 27 28 import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor; 28 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;29 29 import org.openstreetmap.josm.gui.NavigatableComponent; 30 30 … … 266 266 267 267 final int size = max((ds.isSelected(n) ? selectedNodeSize : 0), 268 269 270 268 (n.isTagged() ? taggedNodeSize : 0), 269 (n.isConnectionNode() ? connectionNodeSize : 0), 270 unselectedNodeSize); 271 271 272 272 final boolean fill = (ds.isSelected(n) && fillSelectedNode) || 273 274 275 273 (n.isTagged() && fillTaggedNode) || 274 (n.isConnectionNode() && fillConnectionNode) || 275 fillUnselectedNode; 276 276 277 277 drawNode(n, color, size, fill); … … 279 279 } 280 280 281 public static boolean isLargeSegment(Point p1, Pointp2, int space)281 public static boolean isLargeSegment(Point2D p1, Point2D p2, int space) 282 282 { 283 int xd = p1.x-p2.x; if(xd < 0) { 284 xd = -xd; 285 } 286 int yd = p1.y-p2.y; if(yd < 0) { 287 yd = -yd; 288 } 283 double xd = Math.abs(p1.getX()-p2.getX()); 284 double yd = Math.abs(p1.getY()-p2.getY()); 289 285 return (xd+yd > space); 290 286 } -
trunk/src/org/openstreetmap/josm/gui/MapStatus.java
r3438 r3594 289 289 */ 290 290 private final void statusBarElementUpdate(MouseState ms) { 291 final OsmPrimitive osmNearest = mv.getNearest (ms.mousePos, OsmPrimitive.isUsablePredicate);291 final OsmPrimitive osmNearest = mv.getNearestNodeOrWay(ms.mousePos, OsmPrimitive.isUsablePredicate); 292 292 if (osmNearest != null) { 293 293 nameText.setText(osmNearest.getDisplayName(DefaultNameFormatter.getInstance())); -
trunk/src/org/openstreetmap/josm/gui/MapView.java
r3478 r3594 36 36 import org.openstreetmap.josm.actions.mapmode.MapMode; 37 37 import org.openstreetmap.josm.data.Bounds; 38 import org.openstreetmap.josm.data.SelectionChangedListener;39 38 import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent; 40 39 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; 40 import org.openstreetmap.josm.data.SelectionChangedListener; 41 41 import org.openstreetmap.josm.data.coor.LatLon; 42 42 import org.openstreetmap.josm.data.osm.DataSet; -
trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
r3490 r3594 6 6 import java.awt.Point; 7 7 import java.awt.Rectangle; 8 import java.awt.geom.Point2D; 8 9 import java.util.ArrayList; 9 10 import java.util.Collection; … … 16 17 import java.util.Locale; 17 18 import java.util.Map; 19 import java.util.Set; 18 20 import java.util.Stack; 19 21 import java.util.TreeMap; … … 85 87 } 86 88 87 public static final int snapDistance = Main.pref.getInteger("node.snap-distance", 10);88 public static final int snapDistanceSq = sqr(snapDistance);89 90 private static int sqr(int a) { return a*a;}91 89 /** 92 90 * The scale factor in x or y-units per pixel. This means, if scale = 10, … … 194 192 } 195 193 194 public LatLon getLatLon(double x, double y) { 195 return getLatLon((int)x, (int)y); 196 } 197 196 198 /** 197 199 * @param r … … 228 230 * to the own top/left. 229 231 */ 230 public Point getPoint(EastNorth p) {232 public Point2D getPoint2D(EastNorth p) { 231 233 if (null == p) 232 234 return new Point(); 233 235 double x = (p.east()-center.east())/scale + getWidth()/2; 234 236 double y = (center.north()-p.north())/scale + getHeight()/2; 235 return new Point ((int)x,(int)y);236 } 237 238 public Point getPoint(LatLon latlon) {237 return new Point2D.Double(x, y); 238 } 239 240 public Point2D getPoint2D(LatLon latlon) { 239 241 if (latlon == null) 240 242 return new Point(); 241 243 else if (latlon instanceof CachedLatLon) 242 return getPoint (((CachedLatLon)latlon).getEastNorth());244 return getPoint2D(((CachedLatLon)latlon).getEastNorth()); 243 245 else 244 return getPoint(getProjection().latlon2eastNorth(latlon)); 245 } 246 return getPoint2D(getProjection().latlon2eastNorth(latlon)); 247 } 248 public Point2D getPoint2D(Node n) { 249 return getPoint2D(n.getEastNorth()); 250 } 251 252 // looses precision, may overflow (depends on p and current scale) 253 //@Deprecated 254 public Point getPoint(EastNorth p) { 255 Point2D d = getPoint2D(p); 256 return new Point((int) d.getX(), (int) d.getY()); 257 } 258 259 // looses precision, may overflow (depends on p and current scale) 260 //@Deprecated 261 public Point getPoint(LatLon latlon) { 262 Point2D d = getPoint2D(latlon); 263 return new Point((int) d.getX(), (int) d.getY()); 264 } 265 266 // looses precision, may overflow (depends on p and current scale) 267 //@Deprecated 246 268 public Point getPoint(Node n) { 247 return getPoint(n.getEastNorth()); 269 Point2D d = getPoint2D(n); 270 return new Point((int) d.getX(), (int) d.getY()); 248 271 } 249 272 … … 429 452 } 430 453 431 private BBox get SnapDistanceBBox(Point p) {454 private BBox getBBox(Point p, int snapDistance) { 432 455 return new BBox(getLatLon(p.x - snapDistance, p.y - snapDistance), 433 456 getLatLon(p.x + snapDistance, p.y + snapDistance)); 434 457 } 435 458 436 @Deprecated 437 public final Node getNearestNode(Point p) { 438 return getNearestNode(p, OsmPrimitive.isUsablePredicate); 439 } 440 441 /** 442 * Return the nearest node to the screen point given. 443 * If more then one node within snapDistance pixel is found, 444 * the nearest node is returned. 445 * @param p the screen point 446 * @param predicate this parameter imposes a condition on the returned object, e.g. 447 * give the nearest node that is tagged. 448 */ 449 public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate) { 459 /** 460 * The *result* does not depend on the current map selection state, 461 * neither does the result *order*. 462 * It solely depends on the distance to point p. 463 * 464 * @return a sorted map with the keys representing the distance of 465 * their associated nodes to point p. 466 */ 467 private Map<Double, List<Node>> getNearestNodesImpl(Point p, 468 Predicate<OsmPrimitive> predicate) { 469 TreeMap<Double, List<Node>> nearestMap = new TreeMap<Double, List<Node>>(); 450 470 DataSet ds = getCurrentDataSet(); 451 if (ds == null) 452 return null; 453 454 double minDistanceSq = snapDistanceSq; 455 Node minPrimitive = null; 456 for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) { 457 if (! predicate.evaluate(n)) 458 continue; 459 Point sp = getPoint(n); 460 double dist = p.distanceSq(sp); 461 if (dist < minDistanceSq) { 462 minDistanceSq = dist; 463 minPrimitive = n; 464 } 465 // when multiple nodes on one point, prefer new or selected nodes 466 else if (dist == minDistanceSq && minPrimitive != null 467 && ((n.isNew() && ds.isSelected(n)) 468 || (!ds.isSelected(minPrimitive) && (ds.isSelected(n) || n.isNew())))) { 469 minPrimitive = n; 470 } 471 } 472 return minPrimitive; 473 } 474 475 /** 476 * @return all way segments within 10px of p, sorted by their 477 * perpendicular distance. 478 * 479 * @param p the point for which to search the nearest segment. 480 * @param predicate the returned objects have to fulfill certain properties. 481 */ 482 public final List<WaySegment> getNearestWaySegments(Point p, Predicate<OsmPrimitive> predicate) { 483 TreeMap<Double, List<WaySegment>> nearest = new TreeMap<Double, List<WaySegment>>(); 484 DataSet ds = getCurrentDataSet(); 485 if (ds == null) 486 return null; 487 488 for (Way w : ds.searchWays(getSnapDistanceBBox(p))) { 489 if (!predicate.evaluate(w)) 490 continue; 491 Node lastN = null; 492 int i = -2; 493 for (Node n : w.getNodes()) { 494 i++; 495 if (n.isDeleted() || n.isIncomplete()) {//FIXME: This shouldn't happen, raise exception? 496 continue; 497 } 498 if (lastN == null) { 499 lastN = n; 500 continue; 501 } 502 503 Point A = getPoint(lastN); 504 Point B = getPoint(n); 505 double c = A.distanceSq(B); 506 double a = p.distanceSq(B); 507 double b = p.distanceSq(A); 508 double perDist = a - (a - b + c) * (a - b + c) / 4 / c; // perpendicular distance squared 509 if (perDist < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) { 510 if (ds.isSelected(w)) { 511 perDist -= 0.00001; 471 472 if (ds != null) { 473 double dist, snapDistanceSq = Main.pref.getInteger("mappaint.node.snap-distance", 10); 474 snapDistanceSq *= snapDistanceSq; 475 476 for (Node n : ds.searchNodes(getBBox(p, Main.pref.getInteger("mappaint.node.snap-distance", 10)))) { 477 if (predicate.evaluate(n) 478 && (dist = getPoint2D(n).distanceSq(p)) < snapDistanceSq) 479 { 480 List<Node> nlist; 481 if (nearestMap.containsKey(dist)) { 482 nlist = nearestMap.get(dist); 483 } else { 484 nlist = new LinkedList<Node>(); 485 nearestMap.put(dist, nlist); 512 486 } 513 List<WaySegment> l; 514 if (nearest.containsKey(perDist)) { 515 l = nearest.get(perDist); 516 } else { 517 l = new LinkedList<WaySegment>(); 518 nearest.put(perDist, l); 519 } 520 l.add(new WaySegment(w, i)); 521 } 522 523 lastN = n; 524 } 525 } 526 ArrayList<WaySegment> nearestList = new ArrayList<WaySegment>(); 527 for (List<WaySegment> wss : nearest.values()) { 528 nearestList.addAll(wss); 529 } 530 return nearestList; 531 } 532 533 /** 534 * @return the nearest way segment to the screen point given that is not 535 * in ignore. 536 * 537 * @param p the point for which to search the nearest segment. 538 * @param ignore a collection of segments which are not to be returned. 539 * @param predicate the returned object has to fulfill certain properties. 540 * May be null. 541 */ 542 public final WaySegment getNearestWaySegment 543 (Point p, Collection<WaySegment> ignore, Predicate<OsmPrimitive> predicate) { 544 List<WaySegment> nearest = getNearestWaySegments(p, predicate); 545 if(nearest == null) 546 return null; 547 if (ignore != null) { 548 nearest.removeAll(ignore); 549 } 550 return nearest.isEmpty() ? null : nearest.get(0); 551 } 552 553 /** 554 * @return the nearest way segment to the screen point given. 555 */ 556 public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate) { 557 return getNearestWaySegment(p, null, predicate); 558 } 559 560 @Deprecated 561 public final Way getNearestWay(Point p) { 562 return getNearestWay(p, OsmPrimitive.isUsablePredicate); 563 } 564 565 /** 566 * @return the nearest way to the screen point given. 567 */ 568 public final Way getNearestWay(Point p, Predicate<OsmPrimitive> predicate) { 569 WaySegment nearestWaySeg = getNearestWaySegment(p, predicate); 570 return nearestWaySeg == null ? null : nearestWaySeg.way; 571 } 572 573 /** 574 * Return the object, that is nearest to the given screen point. 575 * 576 * First, a node will be searched. If a node within 10 pixel is found, the 577 * nearest node is returned. 578 * 579 * If no node is found, search for near ways. 580 * 581 * If nothing is found, return <code>null</code>. 582 * 583 * @param p The point on screen. 584 * @param predicate the returned object has to fulfill certain properties. 585 * @return The primitive that is nearest to the point p. 586 */ 587 public OsmPrimitive getNearest(Point p, Predicate<OsmPrimitive> predicate) { 588 OsmPrimitive osm = getNearestNode(p, predicate); 589 if (osm == null) 590 { 591 osm = getNearestWay(p, predicate); 592 } 593 return osm; 594 } 595 596 /** 597 * Returns a singleton of the nearest object, or else an empty collection. 598 */ 599 public Collection<OsmPrimitive> getNearestCollection(Point p, Predicate<OsmPrimitive> predicate) { 600 OsmPrimitive osm = getNearest(p, predicate); 601 if (osm == null) 602 return Collections.emptySet(); 603 return Collections.singleton(osm); 604 } 605 606 /** 607 * @return A list of all objects that are nearest to 608 * the mouse. 609 * 610 * @return A collection of all items or <code>null</code> 611 * if no item under or near the point. The returned 612 * list is never empty. 613 */ 614 public Collection<OsmPrimitive> getAllNearest(Point p, Predicate<OsmPrimitive> predicate) { 615 Collection<OsmPrimitive> nearest = new HashSet<OsmPrimitive>(); 616 DataSet ds = getCurrentDataSet(); 617 if (ds == null) 618 return null; 619 for (Way w : ds.searchWays(getSnapDistanceBBox(p))) { 620 if (!predicate.evaluate(w)) 621 continue; 622 Node lastN = null; 623 for (Node n : w.getNodes()) { 624 if (!predicate.evaluate(n)) 625 continue; 626 if (lastN == null) { 627 lastN = n; 628 continue; 629 } 630 Point A = getPoint(lastN); 631 Point B = getPoint(n); 632 double c = A.distanceSq(B); 633 double a = p.distanceSq(B); 634 double b = p.distanceSq(A); 635 double perDist = a - (a - b + c) * (a - b + c) / 4 / c; // perpendicular distance squared 636 if (perDist < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) { 637 nearest.add(w); 638 break; 639 } 640 lastN = n; 641 } 642 } 643 for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) { 644 if (n.isUsable() 645 && getPoint(n).distanceSq(p) < snapDistanceSq) { 646 nearest.add(n); 647 } 648 } 649 return nearest.isEmpty() ? null : nearest; 650 } 651 652 /** 653 * @return A list of all nodes that are nearest to 654 * the mouse. 655 * 656 * @return A collection of all nodes or <code>null</code> 657 * if no node under or near the point. The returned 658 * list is never empty. 659 */ 660 public Collection<Node> getNearestNodes(Point p, Predicate<OsmPrimitive> predicate) { 661 Collection<Node> nearest = new HashSet<Node>(); 662 DataSet ds = getCurrentDataSet(); 663 if (ds == null) 664 return null; 665 666 for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) { 667 if (!predicate.evaluate(n)) 668 continue; 669 if (getPoint(n).distanceSq(p) < snapDistanceSq) { 670 nearest.add(n); 671 } 672 } 673 return nearest.isEmpty() ? null : nearest; 674 } 675 676 /** 677 * @return the nearest nodes to the screen point given that is not 678 * in ignore. 487 nlist.add(n); 488 } 489 } 490 } 491 492 return nearestMap; 493 } 494 495 /** 496 * The *result* does not depend on the current map selection state, 497 * neither does the result *order*. 498 * It solely depends on the distance to point p. 499 * 500 * @return All nodes nearest to point p that are in a belt from 501 * dist(nearest) to dist(nearest)+4px around p and 502 * that are not in ignore. 679 503 * 680 504 * @param p the point for which to search the nearest segment. 681 505 * @param ignore a collection of nodes which are not to be returned. 682 506 * @param predicate the returned objects have to fulfill certain properties. 683 * May be null. 684 */ 685 public final Collection<Node> getNearestNodes(Point p, Collection<Node> ignore, Predicate<OsmPrimitive> predicate) { 686 Collection<Node> nearest = getNearestNodes(p, predicate); 687 if (nearest == null) return null; 507 */ 508 public final List<Node> getNearestNodes(Point p, 509 Collection<Node> ignore, Predicate<OsmPrimitive> predicate) { 510 List<Node> nearestList = Collections.emptyList(); 511 512 if (ignore == null) { 513 ignore = Collections.emptySet(); 514 } 515 516 Map<Double, List<Node>> nlists = getNearestNodesImpl(p, predicate); 517 if (!nlists.isEmpty()) { 518 Double minDistSq = null; 519 List<Node> nlist; 520 for (Double distSq : nlists.keySet()) { 521 nlist = nlists.get(distSq); 522 523 // filter nodes to be ignored before determining minDistSq.. 524 nlist.removeAll(ignore); 525 if (minDistSq == null) { 526 if (!nlist.isEmpty()) { 527 minDistSq = distSq; 528 nearestList = new ArrayList<Node>(); 529 nearestList.addAll(nlist); 530 } 531 } else { 532 if (distSq-minDistSq < (4)*(4)) { 533 nearestList.addAll(nlist); 534 } 535 } 536 } 537 } 538 539 return nearestList; 540 } 541 542 /** 543 * The *result* does not depend on the current map selection state, 544 * neither does the result *order*. 545 * It solely depends on the distance to point p. 546 * 547 * @return All nodes nearest to point p that are in a belt from 548 * dist(nearest) to dist(nearest)+4px around p. 549 * @see #getNearestNodes(Point, Collection, Predicate) 550 * 551 * @param p the point for which to search the nearest segment. 552 * @param predicate the returned objects have to fulfill certain properties. 553 */ 554 public final List<Node> getNearestNodes(Point p, Predicate<OsmPrimitive> predicate) { 555 return getNearestNodes(p, null, predicate); 556 } 557 558 /** 559 * The *result* depends on the current map selection state. 560 * 561 * If more than one node within node.snap-distance pixels is found, 562 * the nearest node selected is returned. 563 * 564 * If no such node is found, the nearest new/id=0 node within 565 * about the same distance as the true nearest node is returned. 566 * 567 * If no such node is found either, the true nearest 568 * node to p is returned. 569 * 570 * Finally, if a node is not found at all, return null. 571 * 572 * @return A node within snap-distance to point p, 573 * that is chosen by the algorithm described. 574 * 575 * @param p the screen point 576 * @param predicate this parameter imposes a condition on the returned object, e.g. 577 * give the nearest node that is tagged. 578 */ 579 public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate) { 580 Node n = null; 581 582 Map<Double, List<Node>> nlists = getNearestNodesImpl(p, predicate); 583 if (!nlists.isEmpty()) { 584 Node ntsel = null, ntnew = null; 585 double minDistSq = nlists.keySet().iterator().next(); 586 587 for (Double distSq : nlists.keySet()) { 588 for (Node nd : nlists.get(distSq)) { 589 // find the nearest selected node 590 if (ntsel == null && nd.isSelected()) { 591 ntsel = nd; 592 } 593 // find the nearest newest node that is within about the same 594 // distance as the true nearest node 595 if (ntnew == null && nd.isNew() && (distSq-minDistSq < 1)) { 596 ntnew = nd; 597 } 598 } 599 } 600 601 // take nearest selected, nearest new or true nearest node to p, in that order 602 n = (ntsel != null) ? ntsel : ((ntnew != null) ? ntnew 603 : nlists.values().iterator().next().get(0)); 604 } 605 606 return n; 607 } 608 609 @Deprecated 610 public final Node getNearestNode(Point p) { 611 return getNearestNode(p, OsmPrimitive.isUsablePredicate); 612 } 613 614 /** 615 * The *result* does not depend on the current map selection state, 616 * neither does the result *order*. 617 * It solely depends on the distance to point p. 618 * 619 * @return a sorted map with the keys representing the perpendicular 620 * distance of their associated way segments to point p. 621 */ 622 private Map<Double, List<WaySegment>> getNearestWaySegmentsImpl(Point p, 623 Predicate<OsmPrimitive> predicate) { 624 Map<Double, List<WaySegment>> nearestMap = new TreeMap<Double, List<WaySegment>>(); 625 DataSet ds = getCurrentDataSet(); 626 627 if (ds != null) { 628 double snapDistanceSq = Main.pref.getInteger("mappaint.segment.snap-distance", 10); 629 snapDistanceSq *= snapDistanceSq; 630 631 for (Way w : ds.searchWays(getBBox(p, Main.pref.getInteger("mappaint.segment.snap-distance", 10)))) { 632 if (!predicate.evaluate(w)) { 633 continue; 634 } 635 Node lastN = null; 636 int i = -2; 637 for (Node n : w.getNodes()) { 638 i++; 639 if (n.isDeleted() || n.isIncomplete()) { //FIXME: This shouldn't happen, raise exception? 640 continue; 641 } 642 if (lastN == null) { 643 lastN = n; 644 continue; 645 } 646 647 Point2D A = getPoint2D(lastN); 648 Point2D B = getPoint2D(n); 649 double c = A.distanceSq(B); 650 double a = p.distanceSq(B); 651 double b = p.distanceSq(A); 652 653 /* perpendicular distance squared 654 * loose some precision to account for possible deviations in the calculation above 655 * e.g. if identical (A and B) come about reversed in another way, values may differ 656 * -- zero out least significant 32 dual digits of mantissa.. 657 */ 658 double perDistSq = Double.longBitsToDouble( 659 Double.doubleToLongBits( a - (a - b + c) * (a - b + c) / 4 / c ) 660 >> 32 << 32); // resolution in numbers with large exponent not needed here.. 661 662 if (perDistSq < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) { 663 //System.err.println(Double.toHexString(perDistSq)); 664 665 List<WaySegment> wslist; 666 if (nearestMap.containsKey(perDistSq)) { 667 wslist = nearestMap.get(perDistSq); 668 } else { 669 wslist = new LinkedList<WaySegment>(); 670 nearestMap.put(perDistSq, wslist); 671 } 672 wslist.add(new WaySegment(w, i)); 673 } 674 675 lastN = n; 676 } 677 } 678 } 679 680 return nearestMap; 681 } 682 683 /** 684 * The result *order* depends on the current map selection state. 685 * Segments within 10px of p are searched and sorted by their distance to @param p, 686 * then, within groups of equally distant segments, prefer those that are selected. 687 * 688 * @return all segments within 10px of p that are not in ignore, 689 * sorted by their perpendicular distance. 690 * 691 * @param p the point for which to search the nearest segments. 692 * @param ignore a collection of segments which are not to be returned. 693 * @param predicate the returned objects have to fulfill certain properties. 694 */ 695 public final List<WaySegment> getNearestWaySegments(Point p, 696 Collection<WaySegment> ignore, Predicate<OsmPrimitive> predicate) { 697 List<WaySegment> nearestList = new ArrayList<WaySegment>(); 698 List<WaySegment> unselected = new LinkedList<WaySegment>(); 699 700 for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) { 701 // put selected waysegs within each distance group first 702 // makes the order of nearestList dependent on current selection state 703 for (WaySegment ws : wss) { 704 (ws.way.isSelected() ? nearestList : unselected).add(ws); 705 } 706 nearestList.addAll(unselected); 707 unselected.clear(); 708 } 688 709 if (ignore != null) { 689 nearest.removeAll(ignore); 690 } 691 return nearest.isEmpty() ? null : nearest; 710 nearestList.removeAll(ignore); 711 } 712 713 return nearestList; 714 } 715 716 /** 717 * The result *order* depends on the current map selection state. 718 * 719 * @return all segments within 10px of p, sorted by their perpendicular distance. 720 * @see #getNearestWaySegments(Point, Collection, Predicate) 721 * 722 * @param p the point for which to search the nearest segments. 723 * @param predicate the returned objects have to fulfill certain properties. 724 */ 725 public final List<WaySegment> getNearestWaySegments(Point p, Predicate<OsmPrimitive> predicate) { 726 return getNearestWaySegments(p, null, predicate); 727 } 728 729 /** 730 * The *result* depends on the current map selection state. 731 * 732 * @return The nearest way segment to point p, 733 * prefer a nearest, selected way segment, if found. 734 * @see #getNearestWaySegments(Point, Collection, Predicate) 735 * 736 * @param p the point for which to search the nearest segment. 737 * @param predicate the returned object has to fulfill certain properties. 738 */ 739 public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate) { 740 WaySegment wayseg = null, ntsel = null; 741 742 for (List<WaySegment> wslist : getNearestWaySegmentsImpl(p, predicate).values()) { 743 if (wayseg != null && ntsel != null) { 744 break; 745 } 746 for (WaySegment ws : wslist) { 747 if (wayseg == null) { 748 wayseg = ws; 749 } 750 if (ntsel == null && ws.way.isSelected()) { 751 ntsel = ws; 752 } 753 } 754 } 755 756 return (ntsel != null) ? ntsel : wayseg; 757 } 758 759 /** 760 * The *result* does not depend on the current map selection state, 761 * neither does the result *order*. 762 * It solely depends on the perpendicular distance to point p. 763 * 764 * @return all nearest ways to the screen point given that are not in ignore. 765 * @see #getNearestWaySegments(Point, Collection, Predicate) 766 * 767 * @param p the point for which to search the nearest ways. 768 * @param ignore a collection of ways which are not to be returned. 769 * @param predicate the returned object has to fulfill certain properties. 770 */ 771 public final List<Way> getNearestWays(Point p, 772 Collection<Way> ignore, Predicate<OsmPrimitive> predicate) { 773 List<Way> nearestList = new ArrayList<Way>(); 774 Set<Way> wset = new HashSet<Way>(); 775 776 for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) { 777 for (WaySegment ws : wss) { 778 if (wset.add(ws.way)) { 779 nearestList.add(ws.way); 780 } 781 } 782 } 783 if (ignore != null) { 784 nearestList.removeAll(ignore); 785 } 786 787 return nearestList; 788 } 789 790 /** 791 * The *result* does not depend on the current map selection state, 792 * neither does the result *order*. 793 * It solely depends on the perpendicular distance to point p. 794 * 795 * @return all nearest ways to the screen point given. 796 * @see #getNearestWays(Point, Collection, Predicate) 797 * 798 * @param p the point for which to search the nearest ways. 799 * @param predicate the returned object has to fulfill certain properties. 800 */ 801 public final List<Way> getNearestWays(Point p, Predicate<OsmPrimitive> predicate) { 802 return getNearestWays(p, null, predicate); 803 } 804 805 /** 806 * The *result* depends on the current map selection state. 807 * 808 * @return The nearest way to point p, 809 * prefer a selected way if there are multiple nearest. 810 * @see #getNearestWaySegment(Point, Collection, Predicate) 811 * 812 * @param p the point for which to search the nearest segment. 813 * @param predicate the returned object has to fulfill certain properties. 814 */ 815 public final Way getNearestWay(Point p, Predicate<OsmPrimitive> predicate) { 816 WaySegment nearestWaySeg = getNearestWaySegment(p, predicate); 817 return (nearestWaySeg == null) ? null : nearestWaySeg.way; 818 } 819 820 @Deprecated 821 public final Way getNearestWay(Point p) { 822 return getNearestWay(p, OsmPrimitive.isUsablePredicate); 823 } 824 825 /** 826 * The *result* does not depend on the current map selection state, 827 * neither does the result *order*. 828 * It solely depends on the distance to point p. 829 * 830 * First, nodes will be searched. If there are nodes within BBox found, 831 * return a collection of those nodes only. 832 * 833 * If no nodes are found, search for nearest ways. If there are ways 834 * within BBox found, return a collection of those ways only. 835 * 836 * If nothing is found, return an empty collection. 837 * 838 * @return Primitives nearest to the given screen point that are not in ignore. 839 * @see #getNearestNodes(Point, Collection, Predicate) 840 * @see #getNearestWays(Point, Collection, Predicate) 841 * 842 * @param p The point on screen. 843 * @param ignore a collection of ways which are not to be returned. 844 * @param predicate the returned object has to fulfill certain properties. 845 */ 846 public final List<OsmPrimitive> getNearestNodesOrWays(Point p, 847 Collection<OsmPrimitive> ignore, Predicate<OsmPrimitive> predicate) { 848 List<OsmPrimitive> nearestList = Collections.emptyList(); 849 OsmPrimitive osm = getNearestNodeOrWay(p, predicate, false); 850 851 if (osm != null) { 852 if (osm instanceof Node) { 853 nearestList = new ArrayList<OsmPrimitive>(getNearestNodes(p, predicate)); 854 } else if (osm instanceof Way) { 855 nearestList = new ArrayList<OsmPrimitive>(getNearestWays(p, predicate)); 856 } 857 if (ignore != null) { 858 nearestList.removeAll(ignore); 859 } 860 } 861 862 return nearestList; 863 } 864 865 /** 866 * The *result* does not depend on the current map selection state, 867 * neither does the result *order*. 868 * It solely depends on the distance to point p. 869 * 870 * @return Primitives nearest to the given screen point. 871 * @see #getNearests(Point, Collection, Predicate) 872 * 873 * @param p The point on screen. 874 * @param predicate the returned object has to fulfill certain properties. 875 */ 876 public final List<OsmPrimitive> getNearestNodesOrWays(Point p, Predicate<OsmPrimitive> predicate) { 877 return getNearestNodesOrWays(p, null, predicate); 878 } 879 880 /** 881 * This is used as a helper routine to {@link #getNearestNodeOrWay(Point, Predicate, boolean)} 882 * 883 * @return true, if the node fulfills certain properties wrt p and use_sel 884 * 885 * @param osm node to check 886 * @param p point clicked 887 * @param use_sel whether to prefer a selected node 888 */ 889 private boolean isPrecedenceNode(Node osm, Point p, boolean use_selected) { 890 boolean ret = false; 891 892 if (osm != null) { 893 ret |= !(p.distanceSq(getPoint2D(osm)) > (4)*(4)); 894 ret |= osm.isTagged(); 895 if (use_selected) { 896 ret |= osm.isSelected(); 897 } 898 } 899 900 return ret; 901 } 902 903 /** 904 * The *result* depends on the current map selection state IF use_selected is true. 905 * 906 * IF use_selected is true, use {@link #getNearestNode(Point, Predicate)} to find 907 * the nearest, selected node. If not found, try {@link #getNearestWaySegment(Point, Predicate)} 908 * to find the nearest selected way. 909 * 910 * IF use_selected is false, or if no selected primitive was found, do the following. 911 * 912 * If the nearest node found is within 4px of p, simply take it. 913 * Else, find the nearest way segment. Then, if p is closer to its 914 * middle than to the node, take the way segment, else take the node. 915 * 916 * Finally, if no nearest primitive is found at all, return null. 917 * 918 * @return A primitive within snap-distance to point p, 919 * that is chosen by the algorithm described. 920 * @see getNearestNode(Point, Predicate) 921 * @see getNearestNodesImpl(Point, Predicate) 922 * @see getNearestWay(Point, Predicate) 923 * 924 * @param p The point on screen. 925 * @param predicate the returned object has to fulfill certain properties. 926 * @param use_selected whether to prefer primitives that are currently selected. 927 */ 928 public final OsmPrimitive getNearestNodeOrWay(Point p, Predicate<OsmPrimitive> predicate, boolean use_selected) { 929 OsmPrimitive osm = null; 930 WaySegment ws = null; 931 932 // find a nearest, selected primitive 933 if (use_selected) { 934 osm = getNearestNode(p, predicate); 935 use_selected = isPrecedenceNode((Node)osm, p, use_selected); 936 937 if (!use_selected) { 938 ws = getNearestWaySegment(p, predicate); 939 if (ws != null) { 940 if (ws.way.isSelected() || osm == null) { 941 // either no nearest nodes found or none were selected 942 use_selected = true; 943 osm = ws.way; 944 } // else { unselected node and wayseg found, do the stuff below } 945 } else { 946 // no nearest wayseg found, osm is null or the nearest node 947 use_selected = true; 948 } 949 } 950 } 951 952 // no nearest, selected primitive was found or caller does not care about current selection 953 if (!use_selected) { 954 if (osm == null) { 955 // get the true nearest node (if unselected found before, reuse it) 956 for (List<Node> nlist : getNearestNodesImpl(p, predicate).values()) { 957 osm = nlist.get(0); 958 break; 959 } 960 } 961 962 // there is no nearest node OR it does not fulfill criteria to simply be chosen 963 if (osm == null || !isPrecedenceNode((Node)osm, p, use_selected)) { 964 if (ws == null) { 965 // get the true nearest wayseg (if unselected found before, reuse it) 966 for (WaySegment wseg : getNearestWaySegments(p, predicate)) { 967 ws = wseg; 968 break; 969 } 970 } 971 if (ws != null) { 972 if (osm == null) { 973 // a nearest node was not found 974 osm = ws.way; 975 } else { 976 int maxWaySegLenSq = 3*Main.pref.getInteger("mappaint.node.snap-distance", 10); 977 maxWaySegLenSq *= maxWaySegLenSq; 978 979 Point2D wp1 = getPoint2D(ws.way.getNode(ws.lowerIndex)); 980 Point2D wp2 = getPoint2D(ws.way.getNode(ws.lowerIndex+1)); 981 982 // is wayseg shorter than maxWaySegLenSq and 983 // is p closer to the middle of wayseg than to the nearest node? 984 if (wp1.distanceSq(wp2) < maxWaySegLenSq && 985 p.distanceSq(project(0.5, wp1, wp2)) < p.distanceSq(getPoint2D((Node)osm))) { 986 osm = ws.way; 987 } 988 } 989 } 990 } 991 } 992 993 return osm; 994 } 995 996 /** 997 * Convenience method to {@link #getNearestNodeOrWay(Point, Predicate, boolean)}. 998 * 999 * @return The nearest primitive to point p. 1000 */ 1001 public final OsmPrimitive getNearestNodeOrWay(Point p, Predicate<OsmPrimitive> predicate) { 1002 return getNearestNodeOrWay(p, predicate, false); 1003 } 1004 1005 @Deprecated 1006 public final OsmPrimitive getNearest(Point p, Predicate<OsmPrimitive> predicate) { 1007 return getNearestNodeOrWay(p, predicate, false); 1008 } 1009 1010 @Deprecated 1011 public final Collection<OsmPrimitive> getNearestCollection(Point p, Predicate<OsmPrimitive> predicate) { 1012 return asColl(getNearest(p, predicate)); 1013 } 1014 1015 /** 1016 * @return o as collection of o's type. 1017 */ 1018 public final static <T> Collection<T> asColl(T o) { 1019 if (o == null) 1020 return Collections.emptySet(); 1021 return Collections.singleton(o); 1022 } 1023 1024 public final static double perDist(Point2D pt, Point2D a, Point2D b) { 1025 if (pt != null && a != null && b != null) { 1026 double pd = ( 1027 (a.getX()-pt.getX())*(b.getX()-a.getX()) - 1028 (a.getY()-pt.getY())*(b.getY()-a.getY()) ); 1029 return Math.abs(pd) / a.distance(b); 1030 } 1031 return 0d; 1032 } 1033 1034 /** 1035 * 1036 * @param pt point to project onto (ab) 1037 * @param a root of vector 1038 * @param b vector 1039 * @return point of intersection of line given by (ab) 1040 * with its orthogonal line running through pt 1041 */ 1042 public final static Point2D project(Point2D pt, Point2D a, Point2D b) { 1043 if (pt != null && a != null && b != null) { 1044 double r = (( 1045 (pt.getX()-a.getX())*(b.getX()-a.getX()) + 1046 (pt.getY()-a.getY())*(b.getY()-a.getY()) ) 1047 / a.distanceSq(b)); 1048 return project(r, a, b); 1049 } 1050 return null; 1051 } 1052 1053 /** 1054 * if r = 0 returns a, if r=1 returns b, 1055 * if r = 0.5 returns center between a and b, etc.. 1056 * 1057 * @param r scale value 1058 * @param a root of vector 1059 * @param b vector 1060 * @return new point at a + r*(ab) 1061 */ 1062 public final static Point2D project(double r, Point2D a, Point2D b) { 1063 Point2D ret = null; 1064 1065 if (a != null && b != null) { 1066 ret = new Point2D.Double(a.getX() + r*(b.getX()-a.getX()), 1067 a.getY() + r*(b.getY()-a.getY())); 1068 } 1069 return ret; 1070 } 1071 1072 /** 1073 * The *result* does not depend on the current map selection state, 1074 * neither does the result *order*. 1075 * It solely depends on the distance to point p. 1076 * 1077 * @return a list of all objects that are nearest to point p and 1078 * not in ignore or an empty list if nothing was found. 1079 * 1080 * @param p The point on screen. 1081 * @param ignore a collection of ways which are not to be returned. 1082 * @param predicate the returned object has to fulfill certain properties. 1083 */ 1084 public final List<OsmPrimitive> getAllNearest(Point p, 1085 Collection<OsmPrimitive> ignore, Predicate<OsmPrimitive> predicate) { 1086 List<OsmPrimitive> nearestList = new ArrayList<OsmPrimitive>(); 1087 Set<Way> wset = new HashSet<Way>(); 1088 1089 for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) { 1090 for (WaySegment ws : wss) { 1091 if (wset.add(ws.way)) { 1092 nearestList.add(ws.way); 1093 } 1094 } 1095 } 1096 for (List<Node> nlist : getNearestNodesImpl(p, predicate).values()) { 1097 nearestList.addAll(nlist); 1098 } 1099 if (ignore != null) { 1100 nearestList.removeAll(ignore); 1101 } 1102 1103 return nearestList; 1104 } 1105 1106 /** 1107 * The *result* does not depend on the current map selection state, 1108 * neither does the result *order*. 1109 * It solely depends on the distance to point p. 1110 * 1111 * @return a list of all objects that are nearest to point p 1112 * or an empty list if nothing was found. 1113 * @see #getAllNearest(Point, Collection, Predicate) 1114 * 1115 * @param p The point on screen. 1116 * @param predicate the returned object has to fulfill certain properties. 1117 */ 1118 public final List<OsmPrimitive> getAllNearest(Point p, Predicate<OsmPrimitive> predicate) { 1119 return getAllNearest(p, null, predicate); 692 1120 } 693 1121 -
trunk/src/org/openstreetmap/josm/gui/SelectionManager.java
r3198 r3594 61 61 * @see InputEvent#getModifiersEx() 62 62 */ 63 public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl);63 public void selectionEnded(Rectangle r, MouseEvent e); 64 64 /** 65 65 * Called to register the selection manager for "active" property. … … 189 189 mousePos = null; 190 190 191 boolean shift = (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0;192 boolean alt = (e.getModifiersEx() & (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK)) != 0;193 boolean ctrl = (e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0;194 191 if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == 0) { 195 selectionEndedListener.selectionEnded(r, alt, shift, ctrl);192 selectionEndedListener.selectionEnded(r, e); 196 193 } 197 194 } … … 277 274 278 275 if (clicked) { 279 OsmPrimitive osm = nc.getNearest (center, OsmPrimitive.isSelectablePredicate);276 OsmPrimitive osm = nc.getNearestNodeOrWay(center, OsmPrimitive.isSelectablePredicate); 280 277 if (osm != null) { 281 278 selection.add(osm); -
trunk/src/org/openstreetmap/josm/gui/preferences/PrefJPanel.java
r3247 r3594 195 195 //shortcutTable.setFillsViewportHeight(true); Java 1.6 196 196 shortcutTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); 197 shortcutTable.setAutoCreateRowSorter(true); 197 198 listScrollPane.setViewportView(shortcutTable); 198 199
Note:
See TracChangeset
for help on using the changeset viewer.