Changeset 5370 in josm
- Timestamp:
- 2012-07-28T09:59:43+02:00 (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
r5265 r5370 196 196 } 197 197 198 /** 199 * This is called whenever the keyboard modifier status changes 200 */ 201 public void eventDispatched(AWTEvent e) { 202 if(oldEvent == null) 203 return; 204 // We don't have a mouse event, so we pass the old mouse event but the 205 // new modifiers. 206 if(giveUserFeedback(oldEvent, ((InputEvent) e).getModifiers())) { 207 mv.repaint(); 208 } 209 } 210 211 /** 212 * handles adding highlights and updating the cursor for the given mouse event. 213 * Please note that the highlighting for merging while moving is handled via mouseDragged. 214 * @param MouseEvent which should be used as base for the feedback 215 * @return true if repaint is required 216 */ 217 private boolean giveUserFeedback(MouseEvent e) { 218 return giveUserFeedback(e, e.getModifiers()); 219 } 220 221 /** 222 * handles adding highlights and updating the cursor for the given mouse event. 223 * Please note that the highlighting for merging while moving is handled via mouseDragged. 224 * @param MouseEvent which should be used as base for the feedback 225 * @param define custom keyboard modifiers if the ones from MouseEvent are outdated or similar 226 * @return true if repaint is required 227 */ 228 private boolean giveUserFeedback(MouseEvent e, int modifiers) { 229 Collection<OsmPrimitive> c = MapView.asColl( 230 mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, true)); 231 232 updateKeyModifiers(modifiers); 233 determineMapMode(!c.isEmpty()); 234 235 HashSet<OsmPrimitive> newHighlights = new HashSet<OsmPrimitive>(); 236 237 virtualManager.clear(); 238 if(mode == Mode.move && virtualManager.setupVirtual(e.getPoint())) { 239 DataSet ds = getCurrentDataSet(); 240 if (ds != null && drawTargetHighlight) { 241 ds.setHighlightedVirtualNodes(virtualManager.virtualWays); 242 } 243 mv.setNewCursor(SelectActionCursor.virtual_node.cursor(), this); 244 // don't highlight anything else if a virtual node will be 245 return repaintIfRequired(newHighlights); 246 } 247 248 mv.setNewCursor(getCursor(c), this); 249 250 // return early if there can't be any highlights 251 if(!drawTargetHighlight || mode != Mode.move || c.isEmpty()) 252 return repaintIfRequired(newHighlights); 253 254 // CTRL toggles selection, but if while dragging CTRL means merge 255 final boolean isToggleMode = ctrl && !dragInProgress(); 256 for(OsmPrimitive x : c) { 257 // only highlight primitives that will change the selection 258 // when clicked. I.e. don't highlight selected elements unless 259 // we are in toggle mode. 260 if(isToggleMode || !x.isSelected()) { 261 newHighlights.add(x); 262 } 263 } 264 return repaintIfRequired(newHighlights); 265 } 266 198 267 /** 199 268 * works out which cursor should be displayed for most of SelectAction's … … 207 276 switch(mode) { 208 277 case move: 209 if(virtual Node != null) {278 if(virtualManager.hasVirtualNode()) { 210 279 c = "virtual_node"; 211 280 break; … … 294 363 return needsRepaint; 295 364 } 296 297 /** 298 * handles adding highlights and updating the cursor for the given mouse event. 299 * Please note that the highlighting for merging while moving is handled via mouseDragged. 300 * @param MouseEvent which should be used as base for the feedback 301 * @return true if repaint is required 302 */ 303 private boolean giveUserFeedback(MouseEvent e) { 304 return giveUserFeedback(e, e.getModifiers()); 305 } 306 307 /** 308 * handles adding highlights and updating the cursor for the given mouse event. 309 * Please note that the highlighting for merging while moving is handled via mouseDragged. 310 * @param MouseEvent which should be used as base for the feedback 311 * @param define custom keyboard modifiers if the ones from MouseEvent are outdated or similar 312 * @return true if repaint is required 313 */ 314 private boolean giveUserFeedback(MouseEvent e, int modifiers) { 365 366 /** 367 * Look, whether any object is selected. If not, select the nearest node. 368 * If there are no nodes in the dataset, do nothing. 369 * 370 * If the user did not press the left mouse button, do nothing. 371 * 372 * Also remember the starting position of the movement and change the mouse 373 * cursor to movement. 374 */ 375 @Override 376 public void mousePressed(MouseEvent e) { 377 // return early 378 if (!mv.isActiveLayerVisible() || !(Boolean) this.getValue("active") || (mouseDownButton = e.getButton()) != MouseEvent.BUTTON1) 379 return; 380 381 // left-button mouse click only is processed here 382 383 // request focus in order to enable the expected keyboard shortcuts 384 mv.requestFocus(); 385 386 // update which modifiers are pressed (shift, alt, ctrl) 387 updateKeyModifiers(e); 388 389 // We don't want to change to draw tool if the user tries to (de)select 390 // stuff but accidentally clicks in an empty area when selection is empty 391 cancelDrawMode = (shift || ctrl); 392 didMouseDrag = false; 393 initialMoveThresholdExceeded = false; 394 mouseDownTime = System.currentTimeMillis(); 395 lastMousePos = e.getPoint(); 396 397 // primitives under cursor are stored in c collection 315 398 Collection<OsmPrimitive> c = MapView.asColl( 316 399 mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, true)); 317 400 318 updateKeyModifiers(modifiers);319 401 determineMapMode(!c.isEmpty()); 320 321 HashSet<OsmPrimitive> newHighlights = new HashSet<OsmPrimitive>(); 322 323 virtualWays.clear(); 324 virtualNode = null; 325 if(mode == Mode.move && setupVirtual(e)) { 326 DataSet ds = getCurrentDataSet(); 327 if (ds != null && drawTargetHighlight) { 328 ds.setHighlightedVirtualNodes(virtualWays); 329 } 330 mv.setNewCursor(SelectActionCursor.virtual_node.cursor(), this); 331 // don't highlight anything else if a virtual node will be 332 return repaintIfRequired(newHighlights); 333 } 334 335 mv.setNewCursor(getCursor(c), this); 336 337 // return early if there can't be any highlights 338 if(!drawTargetHighlight || mode != Mode.move || c.isEmpty()) 339 return repaintIfRequired(newHighlights); 340 341 // CTRL toggles selection, but if while dragging CTRL means merge 342 final boolean isToggleMode = ctrl && !dragInProgress(); 343 for(OsmPrimitive x : c) { 344 // only highlight primitives that will change the selection 345 // when clicked. I.e. don't highlight selected elements unless 346 // we are in toggle mode. 347 if(isToggleMode || !x.isSelected()) { 348 newHighlights.add(x); 349 } 350 } 351 return repaintIfRequired(newHighlights); 352 } 353 354 /** 355 * This is called whenever the keyboard modifier status changes 356 */ 357 public void eventDispatched(AWTEvent e) { 358 if(oldEvent == null) 359 return; 360 // We don't have a mouse event, so we pass the old mouse event but the 361 // new modifiers. 362 if(giveUserFeedback(oldEvent, ((InputEvent) e).getModifiers())) { 402 403 switch(mode) { 404 case rotate: 405 case scale: 406 if (getCurrentDataSet().getSelected().isEmpty()) { 407 getCurrentDataSet().setSelected(c); 408 } 409 410 // Mode.select redraws when selectPrims is called 411 // Mode.move redraws when mouseDragged is called 412 // Mode.rotate redraws here 413 // Mode.scale redraws here 414 break; 415 case move: 416 if (!cancelDrawMode && c.iterator().next() instanceof Way) { 417 virtualManager.setupVirtual(e.getPoint()); 418 } 419 420 selectPrims(cycleManager.cycleSetup(c, e.getPoint()), false, false); 421 break; 422 case select: 423 default: 424 selectionManager.register(mv, lassoMode); 425 selectionManager.mousePressed(e); 426 break; 427 } 428 giveUserFeedback(e); 429 mv.repaint(); 430 updateStatusLine(); 431 } 432 433 @Override 434 public void mouseMoved(MouseEvent e) { 435 // Mac OSX simulates with ctrl + mouse 1 the second mouse button hence no dragging events get fired. 436 if ((Main.platform instanceof PlatformHookOsx) && (mode == Mode.rotate || mode == Mode.scale)) { 437 mouseDragged(e); 438 return; 439 } 440 oldEvent = e; 441 if(giveUserFeedback(e)) { 363 442 mv.repaint(); 364 443 } 365 444 } 366 445 367 446 /** 368 447 * If the left mouse button is pressed, move all currently selected … … 398 477 // highlight it and adjust the cursor accordingly. 399 478 final boolean canMerge = ctrl && !getCurrentDataSet().getSelectedNodes().isEmpty(); 400 final OsmPrimitive p = canMerge ? (OsmPrimitive)findNodeToMergeTo(e ) : null;479 final OsmPrimitive p = canMerge ? (OsmPrimitive)findNodeToMergeTo(e.getPoint()) : null; 401 480 boolean needsRepaint = removeHighlighting(); 402 481 if(p != null) { … … 440 519 return; 441 520 442 if (virtualWays.size() > 0) { 443 Collection<Command> virtualCmds = new LinkedList<Command>(); 444 virtualCmds.add(new AddCommand(virtualNode)); 445 for (WaySegment virtualWay : virtualWays) { 446 Way w = virtualWay.way; 447 Way wnew = new Way(w); 448 wnew.addNode(virtualWay.lowerIndex + 1, virtualNode); 449 virtualCmds.add(new ChangeCommand(w, wnew)); 450 } 451 virtualCmds.add(new MoveCommand(virtualNode, dx, dy)); 452 String text = trn("Add and move a virtual new node to way", 453 "Add and move a virtual new node to {0} ways", virtualWays.size(), 454 virtualWays.size()); 455 Main.main.undoRedo.add(new SequenceCommand(text, virtualCmds)); 456 getCurrentDataSet().setSelected(Collections.singleton((OsmPrimitive) virtualNode)); 457 virtualWays.clear(); 458 virtualNode = null; 521 if (virtualManager.hasVirtualWays()) { 522 virtualManager.processVirtualNodeMovements(dx, dy); 459 523 } else { 460 // Currently we support only transformations which do not affect relations. 461 // So don't add them in the first place to make handling easier 462 Collection<OsmPrimitive> selection = getCurrentDataSet().getSelectedNodesAndWays(); 463 Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection); 464 465 // for these transformations, having only one node makes no sense - quit silently 466 if (affectedNodes.size() < 2 && (mode == Mode.rotate || mode == Mode.scale)) 467 return; 468 469 Command c = !Main.main.undoRedo.commands.isEmpty() 470 ? Main.main.undoRedo.commands.getLast() : null; 471 if (c instanceof SequenceCommand) { 472 c = ((SequenceCommand) c).getLastCommand(); 473 } 474 475 if (mode == Mode.move) { 476 if (c instanceof MoveCommand && affectedNodes.equals(((MoveCommand) c).getParticipatingPrimitives())) { 477 ((MoveCommand) c).moveAgain(dx, dy); 478 } else { 479 Main.main.undoRedo.add( 480 c = new MoveCommand(selection, dx, dy)); 481 } 482 483 for (Node n : affectedNodes) { 484 if (n.getCoor().isOutSideWorld()) { 485 // Revert move 486 ((MoveCommand) c).moveAgain(-dx, -dy); 487 488 JOptionPane.showMessageDialog( 489 Main.parent, 490 tr("Cannot move objects outside of the world."), 491 tr("Warning"), 492 JOptionPane.WARNING_MESSAGE); 493 mv.setNewCursor(cursor, this); 494 return; 495 } 496 } 497 } else if (mode == Mode.rotate) { 498 if (c instanceof RotateCommand && affectedNodes.equals(((RotateCommand) c).getTransformedNodes())) { 499 ((RotateCommand) c).handleEvent(currentEN); 500 } else { 501 Main.main.undoRedo.add(new RotateCommand(selection, currentEN)); 502 } 503 } else if (mode == Mode.scale) { 504 if (c instanceof ScaleCommand && affectedNodes.equals(((ScaleCommand) c).getTransformedNodes())) { 505 ((ScaleCommand) c).handleEvent(currentEN); 506 } else { 507 Main.main.undoRedo.add(new ScaleCommand(selection, currentEN)); 508 } 509 } 524 if (!updateCommandWhileDragging(dx, dy, currentEN)) return; 510 525 } 511 526 … … 518 533 } 519 534 520 @Override 521 public void mouseMoved(MouseEvent e) { 522 // Mac OSX simulates with ctrl + mouse 1 the second mouse button hence no dragging events get fired. 523 if ((Main.platform instanceof PlatformHookOsx) && (mode == Mode.rotate || mode == Mode.scale)) { 524 mouseDragged(e); 525 return; 526 } 527 oldEvent = e; 528 if(giveUserFeedback(e)) { 529 mv.repaint(); 530 } 531 } 535 532 536 533 537 @Override … … 538 542 } 539 543 540 /** returns true whenever elements have been grabbed and moved (i.e. the initial 541 * thresholds have been exceeded) and is still in progress (i.e. mouse button 542 * still pressed) 543 */ 544 final private boolean dragInProgress() { 545 return didMouseDrag && startingDraggingPos != null; 546 } 547 548 private Node virtualNode = null; 549 private Collection<WaySegment> virtualWays = new LinkedList<WaySegment>(); 550 551 /** 552 * Calculate a virtual node if there is enough visual space to draw a crosshair 553 * node and the middle of a way segment is clicked. If the user drags the 554 * crosshair node, it will be added to all ways in <code>virtualWays</code>. 555 * 556 * @param e contains the point clicked 557 * @return whether <code>virtualNode</code> and <code>virtualWays</code> were setup. 558 */ 559 private boolean setupVirtual(MouseEvent e) { 560 if (Main.pref.getInteger("mappaint.node.virtual-size", 8) > 0) { 561 int virtualSnapDistSq = Main.pref.getInteger("mappaint.node.virtual-snap-distance", 8); 562 int virtualSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70); 563 virtualSnapDistSq *= virtualSnapDistSq; 564 565 Collection<WaySegment> selVirtualWays = new LinkedList<WaySegment>(); 566 Pair<Node, Node> vnp = null, wnp = new Pair<Node, Node>(null, null); 567 Point p = e.getPoint(); 568 Way w = null; 569 570 for (WaySegment ws : mv.getNearestWaySegments(p, OsmPrimitive.isSelectablePredicate)) { 571 w = ws.way; 572 573 Point2D p1 = mv.getPoint2D(wnp.a = w.getNode(ws.lowerIndex)); 574 Point2D p2 = mv.getPoint2D(wnp.b = w.getNode(ws.lowerIndex + 1)); 575 if (WireframeMapRenderer.isLargeSegment(p1, p2, virtualSpace)) { 576 Point2D pc = new Point2D.Double((p1.getX() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2); 577 if (p.distanceSq(pc) < virtualSnapDistSq) { 578 // Check that only segments on top of each other get added to the 579 // virtual ways list. Otherwise ways that coincidentally have their 580 // virtual node at the same spot will be joined which is likely unwanted 581 Pair.sort(wnp); 582 if (vnp == null) { 583 vnp = new Pair<Node, Node>(wnp.a, wnp.b); 584 virtualNode = new Node(mv.getLatLon(pc.getX(), pc.getY())); 585 } 586 if (vnp.equals(wnp)) { 587 (w.isSelected() ? selVirtualWays : virtualWays).add(ws); 588 } 589 } 590 } 591 } 592 593 if (!selVirtualWays.isEmpty()) { 594 virtualWays = selVirtualWays; 595 } 596 } 597 598 return !virtualWays.isEmpty(); 599 } 600 private Collection<OsmPrimitive> cycleList = Collections.emptyList(); 601 private boolean cyclePrims = false; 602 private OsmPrimitive cycleStart = null; 603 604 /** 605 * 606 * @param osm nearest primitive found by simple method 607 * @param e 608 * @return 609 */ 610 private Collection<OsmPrimitive> cycleSetup(Collection<OsmPrimitive> single, MouseEvent e) { 611 OsmPrimitive osm = null; 612 613 if (single != null && !single.isEmpty()) { 614 osm = single.iterator().next(); 615 616 Point p = e.getPoint(); 617 boolean waitForMouseUp = Main.pref.getBoolean("mappaint.select.waits-for-mouse-up", false); 618 updateKeyModifiers(e); 619 alt = alt || Main.pref.getBoolean("selectaction.cycles.multiple.matches", false); 620 621 if (!alt) { 622 cycleList = MapView.asColl(osm); 623 624 if (waitForMouseUp) { 625 // prefer a selected nearest node or way, if possible 626 osm = mv.getNearestNodeOrWay(p, OsmPrimitive.isSelectablePredicate, true); 627 } 628 } else { 629 cycleList = mv.getAllNearest(p, OsmPrimitive.isSelectablePredicate); 630 631 if (cycleList.size() > 1) { 632 cyclePrims = false; 633 634 OsmPrimitive old = osm; 635 for (OsmPrimitive o : cycleList) { 636 if (o.isSelected()) { 637 cyclePrims = true; 638 osm = o; 639 break; 640 } 641 } 642 643 // special case: for cycle groups of 2, we can toggle to the 644 // true nearest primitive on mousePressed right away 645 if (cycleList.size() == 2 && !waitForMouseUp) { 646 if (!(osm.equals(old) || osm.isNew() || ctrl)) { 647 cyclePrims = false; 648 osm = old; 649 } // else defer toggling to mouseRelease time in those cases: 650 /* 651 * osm == old -- the true nearest node is the selected one 652 * osm is a new node -- do not break unglue ways in ALT mode 653 * ctrl is pressed -- ctrl generally works on mouseReleased 654 */ 655 } 656 } 657 } 658 } 659 660 return MapView.asColl(osm); 661 } 662 663 /** 664 * sets the mapmode according to key modifiers and if there are any 665 * selectables nearby. Everything has to be pre-determined for this 666 * function; its main purpose is to centralize what the modifiers do. 667 * @param hasSelectionNearby 668 */ 669 private void determineMapMode(boolean hasSelectionNearby) { 670 if (shift && ctrl) { 671 mode = Mode.rotate; 672 } else if (alt && ctrl) { 673 mode = Mode.scale; 674 } else if (hasSelectionNearby || dragInProgress()) { 675 mode = Mode.move; 676 } else { 677 mode = Mode.select; 678 } 679 } 680 681 /** 682 * Look, whether any object is selected. If not, select the nearest node. 683 * If there are no nodes in the dataset, do nothing. 684 * 685 * If the user did not press the left mouse button, do nothing. 686 * 687 * Also remember the starting position of the movement and change the mouse 688 * cursor to movement. 689 */ 690 @Override 691 public void mousePressed(MouseEvent e) { 692 // return early 693 if (!mv.isActiveLayerVisible() || !(Boolean) this.getValue("active") || (mouseDownButton = e.getButton()) != MouseEvent.BUTTON1) 694 return; 695 696 // request focus in order to enable the expected keyboard shortcuts 697 mv.requestFocus(); 698 699 // update which modifiers are pressed (shift, alt, ctrl) 700 updateKeyModifiers(e); 701 702 // We don't want to change to draw tool if the user tries to (de)select 703 // stuff but accidentally clicks in an empty area when selection is empty 704 cancelDrawMode = (shift || ctrl); 705 didMouseDrag = false; 706 initialMoveThresholdExceeded = false; 707 mouseDownTime = System.currentTimeMillis(); 708 lastMousePos = e.getPoint(); 709 710 Collection<OsmPrimitive> c = MapView.asColl( 711 mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, true)); 712 713 determineMapMode(!c.isEmpty()); 714 switch(mode) { 715 case rotate: 716 case scale: 717 if (getCurrentDataSet().getSelected().isEmpty()) { 718 getCurrentDataSet().setSelected(c); 719 } 720 721 // Mode.select redraws when selectPrims is called 722 // Mode.move redraws when mouseDragged is called 723 // Mode.rotate redraws here 724 // Mode.scale redraws here 725 break; 726 case move: 727 if (!cancelDrawMode && c.iterator().next() instanceof Way) { 728 setupVirtual(e); 729 } 730 731 selectPrims(cycleSetup(c, e), e, false, false); 732 break; 733 case select: 734 default: 735 selectionManager.register(mv, lassoMode); 736 selectionManager.mousePressed(e); 737 break; 738 } 739 giveUserFeedback(e); 740 mv.repaint(); 741 updateStatusLine(); 742 } 743 544 744 545 @Override 745 546 public void mouseReleased(MouseEvent e) { … … 763 564 if (!didMouseDrag) { 764 565 // only built in move mode 765 virtualWays.clear(); 766 virtualNode = null; 767 566 virtualManager.clear(); 768 567 // do nothing if the click was to short too be recognized as a drag, 769 568 // but the release position is farther than 10px away from the press position 770 569 if (lastMousePos == null || lastMousePos.distanceSq(e.getPoint()) < 100) { 771 selectPrims(cyclePrims(cycleList, e), e, true, false); 570 updateKeyModifiers(e); 571 selectPrims(cycleManager.cyclePrims(), true, false); 772 572 773 573 // If the user double-clicked a node, change to draw mode … … 785 585 } 786 586 } else { 787 int max = Main.pref.getInteger("warn.move.maxelements", 20), limit = max; 788 for (OsmPrimitive osm : getCurrentDataSet().getSelected()) { 789 if (osm instanceof Way) { 790 limit -= ((Way) osm).getNodes().size(); 791 } 792 if ((limit -= 1) < 0) { 793 break; 794 } 795 } 796 if (limit < 0) { 797 ExtendedDialog ed = new ExtendedDialog( 798 Main.parent, 799 tr("Move elements"), 800 new String[]{tr("Move them"), tr("Undo move")}); 801 ed.setButtonIcons(new String[]{"reorder.png", "cancel.png"}); 802 ed.setContent(tr("You moved more than {0} elements. " + "Moving a large number of elements is often an error.\n" + "Really move them?", max)); 803 ed.setCancelButton(2); 804 ed.toggleEnable("movedManyElements"); 805 ed.showDialog(); 806 807 if (ed.getValue() != 1) { 808 Main.main.undoRedo.undo(); 809 } 810 } else { 811 mergePrims(e); 812 } 813 getCurrentDataSet().fireSelectionChanged(); 587 confirmOrUndoMovement(e); 814 588 } 815 589 } … … 829 603 } 830 604 605 @Override 831 606 public void selectionEnded(Rectangle r, MouseEvent e) { 832 607 updateKeyModifiers(e); 833 608 mv.repaint(); 834 selectPrims(selectionManager.getSelectedObjects(alt), e, true, true); 835 } 836 837 /** 838 * Modifies current selection state and returns the next element in a 839 * selection cycle given by <code>prims</code>. 840 * @param prims the primitives that form the selection cycle 841 * @param mouse event 842 * @return the next element of cycle list <code>prims</code>. 843 */ 844 private Collection<OsmPrimitive> cyclePrims(Collection<OsmPrimitive> prims, MouseEvent e) { 845 OsmPrimitive nxt = null; 846 847 if (prims.size() > 1) { 609 selectPrims(selectionManager.getSelectedObjects(alt), true, true); 610 } 611 612 /** 613 * sets the mapmode according to key modifiers and if there are any 614 * selectables nearby. Everything has to be pre-determined for this 615 * function; its main purpose is to centralize what the modifiers do. 616 * @param hasSelectionNearby 617 */ 618 private void determineMapMode(boolean hasSelectionNearby) { 619 if (shift && ctrl) { 620 mode = Mode.rotate; 621 } else if (alt && ctrl) { 622 mode = Mode.scale; 623 } else if (hasSelectionNearby || dragInProgress()) { 624 mode = Mode.move; 625 } else { 626 mode = Mode.select; 627 } 628 } 629 630 /** returns true whenever elements have been grabbed and moved (i.e. the initial 631 * thresholds have been exceeded) and is still in progress (i.e. mouse button 632 * still pressed) 633 */ 634 final private boolean dragInProgress() { 635 return didMouseDrag && startingDraggingPos != null; 636 } 637 638 639 /** 640 * Create or update data modfication command whle dragging mouse - implementation of 641 * continuous moving, scaling and rotation 642 * @param dx, @param dy - mouse displacement 643 * @param currentEN - 644 * @return 645 */ 646 private boolean updateCommandWhileDragging(double dx, double dy, EastNorth currentEN) { 647 // Currently we support only transformations which do not affect relations. 648 // So don't add them in the first place to make handling easier 649 Collection<OsmPrimitive> selection = getCurrentDataSet().getSelectedNodesAndWays(); 650 Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection); 651 // for these transformations, having only one node makes no sense - quit silently 652 if (affectedNodes.size() < 2 && (mode == Mode.rotate || mode == Mode.scale)) { 653 return false; 654 } 655 Command c = !Main.main.undoRedo.commands.isEmpty() 656 ? Main.main.undoRedo.commands.getLast() : null; 657 if (c instanceof SequenceCommand) { 658 c = ((SequenceCommand) c).getLastCommand(); 659 } 660 if (mode == Mode.move) { 661 getCurrentDataSet().beginUpdate(); 662 if (c instanceof MoveCommand && affectedNodes.equals(((MoveCommand) c).getParticipatingPrimitives())) { 663 ((MoveCommand) c).moveAgain(dx, dy); 664 } else { 665 Main.main.undoRedo.add( 666 c = new MoveCommand(selection, dx, dy)); 667 } 668 getCurrentDataSet().endUpdate(); 669 for (Node n : affectedNodes) { 670 if (n.getCoor().isOutSideWorld()) { 671 // Revert move 672 ((MoveCommand) c).moveAgain(-dx, -dy); 673 JOptionPane.showMessageDialog( 674 Main.parent, 675 tr("Cannot move objects outside of the world."), 676 tr("Warning"), 677 JOptionPane.WARNING_MESSAGE); 678 mv.setNewCursor(cursor, this); 679 return false; 680 } 681 } 682 } else if (mode == Mode.rotate) { 683 getCurrentDataSet().beginUpdate(); 684 if (c instanceof RotateCommand && affectedNodes.equals(((RotateCommand) c).getTransformedNodes())) { 685 ((RotateCommand) c).handleEvent(currentEN); 686 } else { 687 Main.main.undoRedo.add(new RotateCommand(selection, currentEN)); 688 } 689 getCurrentDataSet().endUpdate(); 690 } else if (mode == Mode.scale) { 691 getCurrentDataSet().beginUpdate(); 692 if (c instanceof ScaleCommand && affectedNodes.equals(((ScaleCommand) c).getTransformedNodes())) { 693 ((ScaleCommand) c).handleEvent(currentEN); 694 } else { 695 Main.main.undoRedo.add(new ScaleCommand(selection, currentEN)); 696 } 697 getCurrentDataSet().endUpdate(); 698 } 699 return true; 700 } 701 702 private void confirmOrUndoMovement(MouseEvent e) { 703 int max = Main.pref.getInteger("warn.move.maxelements", 20), limit = max; 704 for (OsmPrimitive osm : getCurrentDataSet().getSelected()) { 705 if (osm instanceof Way) { 706 limit -= ((Way) osm).getNodes().size(); 707 } 708 if ((limit -= 1) < 0) { 709 break; 710 } 711 } 712 if (limit < 0) { 713 ExtendedDialog ed = new ExtendedDialog( 714 Main.parent, 715 tr("Move elements"), 716 new String[]{tr("Move them"), tr("Undo move")}); 717 ed.setButtonIcons(new String[]{"reorder.png", "cancel.png"}); 718 ed.setContent(tr("You moved more than {0} elements. " + "Moving a large number of elements is often an error.\n" + "Really move them?", max)); 719 ed.setCancelButton(2); 720 ed.toggleEnable("movedManyElements"); 721 ed.showDialog(); 722 723 if (ed.getValue() != 1) { 724 Main.main.undoRedo.undo(); 725 } 726 } else { 727 // if small number of elements were moved, 848 728 updateKeyModifiers(e); 849 850 DataSet ds = getCurrentDataSet(); 851 OsmPrimitive first = prims.iterator().next(), foundInDS = null; 852 nxt = first; 853 854 for (Iterator<OsmPrimitive> i = prims.iterator(); i.hasNext();) { 855 if (cyclePrims && shift) { 856 if (!(nxt = i.next()).isSelected()) { 857 break; // take first primitive in prims list not in sel 858 } 859 } else { 860 if ((nxt = i.next()).isSelected()) { 861 foundInDS = nxt; 862 if (cyclePrims || ctrl) { 863 ds.clearSelection(foundInDS); 864 nxt = i.hasNext() ? i.next() : first; 865 } 866 break; // take next primitive in prims list 867 } 868 } 869 } 870 871 if (ctrl) { 872 // a member of prims was found in the current dataset selection 873 if (foundInDS != null) { 874 // mouse was moved to a different selection group w/ a previous sel 875 if (!prims.contains(cycleStart)) { 876 ds.clearSelection(prims); 877 cycleStart = foundInDS; 878 } else if (cycleStart.equals(nxt)) { 879 // loop detected, insert deselect step 880 ds.addSelected(nxt); 881 } 882 } else { 883 // setup for iterating a sel group again or a new, different one.. 884 nxt = (prims.contains(cycleStart)) ? cycleStart : first; 885 cycleStart = nxt; 886 } 887 } else { 888 cycleStart = null; 889 } 890 } 891 892 // pass on prims, if it had less than 2 elements 893 return (nxt != null) ? MapView.asColl(nxt) : prims; 894 } 895 729 if (ctrl) mergePrims(e.getPoint()); 730 } 731 getCurrentDataSet().fireSelectionChanged(); 732 } 733 896 734 /** Merges the selected nodes to the one closest to the given mouse position iff the control 897 735 * key is pressed. If there is no such node, no action will be done and no error will be 898 736 * reported. If there is, it will execute the merge and add it to the undo buffer. */ 899 final private void mergePrims(MouseEvent e) { 900 updateKeyModifiers(e); 737 final private void mergePrims(Point p) { 901 738 Collection<Node> selNodes = getCurrentDataSet().getSelectedNodes(); 902 if ( !ctrl ||selNodes.isEmpty())903 return; 904 905 Node target = findNodeToMergeTo( e);739 if (selNodes.isEmpty()) 740 return; 741 742 Node target = findNodeToMergeTo(p); 906 743 if (target == null) 907 744 return; … … 914 751 /** tries to find a node to merge to when in move-merge mode for the current mouse 915 752 * position. Either returns the node or null, if no suitable one is nearby. */ 916 final private Node findNodeToMergeTo( MouseEvent e) {917 Collection<Node> target = mv.getNearestNodes( e.getPoint(),753 final private Node findNodeToMergeTo(Point p) { 754 Collection<Node> target = mv.getNearestNodes(p, 918 755 getCurrentDataSet().getSelectedNodes(), 919 756 OsmPrimitive.isSelectablePredicate); … … 921 758 } 922 759 923 private void selectPrims(Collection<OsmPrimitive> prims, MouseEvent e, boolean released, boolean area) { 924 updateKeyModifiers(e); 760 private void selectPrims(Collection<OsmPrimitive> prims, boolean released, boolean area) { 925 761 DataSet ds = getCurrentDataSet(); 926 762 … … 929 765 // anything if about to drag the virtual node (i.e. !released) but continue if the 930 766 // cursor is only released above a virtual node by accident (i.e. released). See #7018 931 if ((shift && ctrl) || (ctrl && !released) || ( !virtualWays.isEmpty() && !released))767 if ((shift && ctrl) || (ctrl && !released) || (virtualManager.hasVirtualWays() && !released)) 932 768 return; 933 769 … … 982 818 this.lassoMode = lassoMode; 983 819 } 820 821 CycleManager cycleManager = new CycleManager(); 822 VirtualManager virtualManager = new VirtualManager(); 823 824 private class CycleManager { 825 826 private Collection<OsmPrimitive> cycleList = Collections.emptyList(); 827 private boolean cyclePrims = false; 828 private OsmPrimitive cycleStart = null; 829 830 /** 831 * 832 * @param osm nearest primitive found by simple method 833 * @param e 834 * @return 835 */ 836 private Collection<OsmPrimitive> cycleSetup(Collection<OsmPrimitive> single, Point p) { 837 OsmPrimitive osm = null; 838 839 if (single != null && !single.isEmpty()) { 840 osm = single.iterator().next(); 841 842 // Point p = e.getPoint(); 843 boolean waitForMouseUp = Main.pref.getBoolean("mappaint.select.waits-for-mouse-up", false); 844 // updateKeyModifiers(e); // cycleSetup called only after updateModifiers ! 845 alt = alt || Main.pref.getBoolean("selectaction.cycles.multiple.matches", false); 846 847 if (!alt) { 848 cycleList = MapView.asColl(osm); 849 850 if (waitForMouseUp) { 851 // prefer a selected nearest node or way, if possible 852 osm = mv.getNearestNodeOrWay(p, OsmPrimitive.isSelectablePredicate, true); 853 } 854 } else { 855 cycleList = mv.getAllNearest(p, OsmPrimitive.isSelectablePredicate); 856 857 if (cycleList.size() > 1) { 858 cyclePrims = false; 859 860 OsmPrimitive old = osm; 861 for (OsmPrimitive o : cycleList) { 862 if (o.isSelected()) { 863 cyclePrims = true; 864 osm = o; 865 break; 866 } 867 } 868 869 // special case: for cycle groups of 2, we can toggle to the 870 // true nearest primitive on mousePressed right away 871 if (cycleList.size() == 2 && !waitForMouseUp) { 872 if (!(osm.equals(old) || osm.isNew() || ctrl)) { 873 cyclePrims = false; 874 osm = old; 875 } // else defer toggling to mouseRelease time in those cases: 876 /* 877 * osm == old -- the true nearest node is the 878 * selected one osm is a new node -- do not break 879 * unglue ways in ALT mode ctrl is pressed -- ctrl 880 * generally works on mouseReleased 881 */ 882 } 883 } 884 } 885 } 886 887 return MapView.asColl(osm); 888 } 889 890 /** 891 * Modifies current selection state and returns the next element in a 892 * selection cycle given by 893 * <code>prims</code>. 894 * 895 * @param prims the primitives that form the selection cycle 896 * @param mouse event 897 * @return the next element of cycle list 898 * <code>prims</code>. 899 */ 900 private Collection<OsmPrimitive> cyclePrims() { 901 Collection<OsmPrimitive> prims = cycleList; 902 OsmPrimitive nxt = null; 903 904 if (prims.size() > 1) { 905 // updateKeyModifiers(e); // already called before ! 906 907 DataSet ds = getCurrentDataSet(); 908 OsmPrimitive first = prims.iterator().next(), foundInDS = null; 909 nxt = first; 910 911 for (Iterator<OsmPrimitive> i = prims.iterator(); i.hasNext();) { 912 if (cyclePrims && shift) { 913 if (!(nxt = i.next()).isSelected()) { 914 break; // take first primitive in prims list not in sel 915 } 916 } else { 917 if ((nxt = i.next()).isSelected()) { 918 foundInDS = nxt; 919 if (cyclePrims || ctrl) { 920 ds.clearSelection(foundInDS); 921 nxt = i.hasNext() ? i.next() : first; 922 } 923 break; // take next primitive in prims list 924 } 925 } 926 } 927 928 if (ctrl) { 929 // a member of prims was found in the current dataset selection 930 if (foundInDS != null) { 931 // mouse was moved to a different selection group w/ a previous sel 932 if (!prims.contains(cycleStart)) { 933 ds.clearSelection(prims); 934 cycleStart = foundInDS; 935 } else if (cycleStart.equals(nxt)) { 936 // loop detected, insert deselect step 937 ds.addSelected(nxt); 938 } 939 } else { 940 // setup for iterating a sel group again or a new, different one.. 941 nxt = (prims.contains(cycleStart)) ? cycleStart : first; 942 cycleStart = nxt; 943 } 944 } else { 945 cycleStart = null; 946 } 947 } 948 949 // pass on prims, if it had less than 2 elements 950 return (nxt != null) ? MapView.asColl(nxt) : prims; 951 } 952 } 953 954 private class VirtualManager { 955 956 private Node virtualNode = null; 957 private Collection<WaySegment> virtualWays = new LinkedList<WaySegment>(); 958 959 /** 960 * Calculate a virtual node if there is enough visual space to draw a 961 * crosshair node and the middle of a way segment is clicked. If the 962 * user drags the crosshair node, it will be added to all ways in 963 * <code>virtualWays</code>. 964 * 965 * @param e contains the point clicked 966 * @return whether 967 * <code>virtualNode</code> and 968 * <code>virtualWays</code> were setup. 969 */ 970 private boolean setupVirtual(Point p) { 971 if (Main.pref.getInteger("mappaint.node.virtual-size", 8) > 0) { 972 int virtualSnapDistSq = Main.pref.getInteger("mappaint.node.virtual-snap-distance", 8); 973 int virtualSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70); 974 virtualSnapDistSq *= virtualSnapDistSq; 975 976 Collection<WaySegment> selVirtualWays = new LinkedList<WaySegment>(); 977 Pair<Node, Node> vnp = null, wnp = new Pair<Node, Node>(null, null); 978 979 Way w = null; 980 for (WaySegment ws : mv.getNearestWaySegments(p, OsmPrimitive.isSelectablePredicate)) { 981 w = ws.way; 982 983 Point2D p1 = mv.getPoint2D(wnp.a = w.getNode(ws.lowerIndex)); 984 Point2D p2 = mv.getPoint2D(wnp.b = w.getNode(ws.lowerIndex + 1)); 985 if (WireframeMapRenderer.isLargeSegment(p1, p2, virtualSpace)) { 986 Point2D pc = new Point2D.Double((p1.getX() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2); 987 if (p.distanceSq(pc) < virtualSnapDistSq) { 988 // Check that only segments on top of each other get added to the 989 // virtual ways list. Otherwise ways that coincidentally have their 990 // virtual node at the same spot will be joined which is likely unwanted 991 Pair.sort(wnp); 992 if (vnp == null) { 993 vnp = new Pair<Node, Node>(wnp.a, wnp.b); 994 virtualNode = new Node(mv.getLatLon(pc.getX(), pc.getY())); 995 } 996 if (vnp.equals(wnp)) { 997 (w.isSelected() ? selVirtualWays : virtualWays).add(ws); 998 } 999 } 1000 } 1001 } 1002 1003 if (!selVirtualWays.isEmpty()) { 1004 virtualWays = selVirtualWays; 1005 } 1006 } 1007 1008 return !virtualWays.isEmpty(); 1009 } 1010 1011 private void processVirtualNodeMovements(double dx, double dy) { 1012 Collection<Command> virtualCmds = new LinkedList<Command>(); 1013 virtualCmds.add(new AddCommand(virtualNode)); 1014 for (WaySegment virtualWay : virtualWays) { 1015 Way w = virtualWay.way; 1016 Way wnew = new Way(w); 1017 wnew.addNode(virtualWay.lowerIndex + 1, virtualNode); 1018 virtualCmds.add(new ChangeCommand(w, wnew)); 1019 } 1020 virtualCmds.add(new MoveCommand(virtualNode, dx, dy)); 1021 String text = trn("Add and move a virtual new node to way", 1022 "Add and move a virtual new node to {0} ways", virtualWays.size(), 1023 virtualWays.size()); 1024 Main.main.undoRedo.add(new SequenceCommand(text, virtualCmds)); 1025 getCurrentDataSet().setSelected(Collections.singleton((OsmPrimitive) virtualNode)); 1026 clear(); 1027 } 1028 1029 private void clear() { 1030 virtualWays.clear(); 1031 virtualNode = null; 1032 } 1033 1034 private boolean hasVirtualNode() { 1035 return virtualNode != null; 1036 } 1037 1038 private boolean hasVirtualWays() { 1039 return !virtualWays.isEmpty(); 1040 } 1041 } 984 1042 }
Note:
See TracChangeset
for help on using the changeset viewer.