Ticket #383: Update DrawAction.patch
| File Update DrawAction.patch, 19.9 KB (added by , 17 years ago) |
|---|
-
src/org/openstreetmap/josm/actions/mapmode/DrawAction.java
131 131 * (if feature enabled). Also sets the target cursor if appropriate. 132 132 */ 133 133 private void addHighlighting() { 134 removeHighlighting(); 134 135 // if ctrl key is held ("no join"), don't highlight anything 135 136 if (ctrl) { 136 removeHighlighting();137 137 resetCursor(); 138 138 return; 139 139 } 140 140 141 // This happens when nothing is selected 142 if(mouseOnExistingNode == null && Main.ds.getSelected().size() == 0) 143 mouseOnExistingNode = Main.map.mapView.getNearestNode(mousePos); 144 141 145 if (mouseOnExistingNode != null) { 142 146 setJoinCursor(false); 143 // Clean old highlights 144 removeHighlighting(); 145 if(drawTargetHighlight) { 146 oldHighlights.add(mouseOnExistingNode); 147 // We also need this list for the statusbar help text 148 oldHighlights.add(mouseOnExistingNode); 149 if(drawTargetHighlight) 147 150 mouseOnExistingNode.highlighted = true; 148 }149 151 return; 150 152 } 151 153 152 154 // Insert the node into all the nearby way segments 153 155 if(mouseOnExistingWays.size() == 0) { 154 removeHighlighting();155 156 resetCursor(); 156 157 return; 157 158 } 158 159 159 160 setJoinCursor(true); 160 // Clean old highlights161 removeHighlighting();162 161 162 // We also need this list for the statusbar help text 163 oldHighlights.addAll(mouseOnExistingWays); 163 164 if(!drawTargetHighlight) return; 164 oldHighlights.addAll(mouseOnExistingWays);165 165 for(Way w : mouseOnExistingWays) { 166 166 w.highlighted = true; 167 167 } … … 174 174 for(OsmPrimitive prim : oldHighlights) { 175 175 prim.highlighted = false; 176 176 } 177 oldHighlights = new HashSet<OsmPrimitive>(); 177 178 } 178 179 179 180 @Override public void enterMode() { … … 214 215 * redraw to (possibly) get rid of helper line if selection changes. 215 216 */ 216 217 public void eventDispatched(AWTEvent event) { 217 if(Main.map == null || Main.map.mapView == null || !Main.map.mapView.isDrawableLayer())218 if(Main.map == null || Main.map.mapView == null || !Main.map.mapView.isDrawableLayer()) 218 219 return; 219 InputEvent e = (InputEvent) event; 220 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 221 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 222 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 220 updateKeyModifiers((InputEvent) event); 221 addHighlighting(); 223 222 computeHelperLine(); 224 223 } 225 224 /** … … 237 236 } 238 237 239 238 /** 239 * This function should be called when the user wishes to finish his current draw action. 240 * If Potlatch Style is enabled, it will switch to select tool, otherwise simply disable 241 * the helper line until the user chooses to draw something else. 242 */ 243 private void finishDrawing() { 244 lastUsedNode = null; 245 wayIsFinished = true; 246 Main.map.selectSelectTool(true); 247 248 // Redraw to remove the helper line stub 249 removeHighlighting(); 250 computeHelperLine(); 251 Main.map.mapView.repaint(); 252 } 253 254 /** 240 255 * If user clicked with the left button, add a node at the current mouse 241 256 * position. 242 257 * … … 247 262 return; 248 263 if(!Main.map.mapView.isDrawableLayer()) 249 264 return; 250 265 251 266 if(e.getClickCount() > 1 && mousePos != null && mousePos.equals(oldMousePos)) { 252 267 // A double click equals "user clicked last node again, finish way" 253 268 // Change draw tool only if mouse position is nearly the same, as 254 269 // otherwise fast clicks will count as a double click 255 lastUsedNode = null; 256 wayIsFinished = true; 257 258 Main.map.selectSelectTool(true); 270 finishDrawing(); 259 271 return; 260 272 } 261 273 oldMousePos = mousePos; 262 274 263 275 // we copy ctrl/alt/shift from the event just in case our global 264 276 // AWTEvent didn't make it through the security manager. Unclear 265 277 // if that can ever happen but better be safe. 266 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 267 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 268 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 278 updateKeyModifiers(e); 269 279 mousePos = e.getPoint(); 270 280 271 281 Collection<OsmPrimitive> selection = Main.ds.getSelected(); … … 276 286 boolean newNode = false; 277 287 Node n = null; 278 288 279 if (!ctrl )289 if (!ctrl && !shift) 280 290 n = Main.map.mapView.getNearestNode(mousePos); 281 291 282 292 if (n != null) { 283 293 // user clicked on node 284 if (s hift || selection.isEmpty()) {294 if (selection.isEmpty()) { 285 295 // select the clicked node and do nothing else 286 296 // (this is just a convenience option so that people don't 287 297 // have to switch modes) … … 329 339 Pair.sort(new Pair<Node,Node>(w.nodes.get(i), w.nodes.get(i+1)))); 330 340 for (int i : is) wnew.addNode(i + 1, n); 331 341 342 // If ALT is pressed, a new way should be created and that new way should get 343 // selected. This works everytime unless the ways the nodes get inserted into 344 // are already selected. This is the case when creating a self-overlapping way 345 // but pressing ALT prevents this. Therefore we must de-select the way manually 346 // here so /only/ the new way will be selected after this method finishes. 347 if(alt) wnew.selected = false; 348 332 349 cmds.add(new ChangeCommand(insertPoint.getKey(), wnew)); 333 350 replacedWays.add(insertPoint.getKey()); 334 351 reuseWays.add(wnew); … … 350 367 boolean extendedWay = false; 351 368 boolean wayIsFinishedTemp = wayIsFinished; 352 369 wayIsFinished = false; 353 if (!shift && selection.size() > 0 && !wayIsFinishedTemp) { 370 Way way = null; 371 if (selection.size() > 0 && !wayIsFinishedTemp) { 354 372 Node selectedNode = null; 355 373 Way selectedWay = null; 356 374 … … 372 390 } 373 391 } 374 392 375 // No nodes or ways have been selected, try again with no selection 376 // This occurs when a relation has been selected 377 if(selectedNode == null && selectedWay == null) { 393 /* 394 * the node from which we make a connection 395 */ 396 Node n0 = findNodeToContinueFrom(selectedNode, selectedWay); 397 // We have a selection but it isn't suitable. Try again. 398 if(n0 == null) { 378 399 tryAgain(e); 379 400 return; 380 401 } 381 402 382 // the node from which we make a connection383 Node n0 = null;403 if(isSelfContainedWay(selectedWay, n0, n)) 404 return; 384 405 385 if (selectedNode == null) {386 if (selectedWay.isFirstLastNode(lastUsedNode)) {387 n0 = lastUsedNode;388 } else {389 // We have a way selected, but no suitable node to continue from. Start anew.390 tryAgain(e);391 return;392 }393 } else if (selectedWay == null) {394 n0 = selectedNode;395 } else {396 if (selectedWay.isFirstLastNode(selectedNode)) {397 n0 = selectedNode;398 } else {399 // We have a way and node selected, but it's not at the start/end of the way. Start anew.400 tryAgain(e);401 return;402 }403 }404 405 // Prevent creation of ways that look like this: <---->406 // This happens if users want to draw a no-exit-sideway from the main way like this:407 // ^408 // |<---->409 // |410 // The solution isn't ideal because the main way will end in the side way, which is bad for411 // navigation software ("drive straight on") but at least easier to fix. Maybe users will fix412 // it on their own, too. At least it's better than producing an error.413 if(selectedWay != null && selectedWay.nodes != null) {414 int posn0 = selectedWay.nodes.indexOf(n0);415 if( posn0 != -1 && // n0 is part of way416 (posn0 >= 1 && n.equals(selectedWay.nodes.get(posn0-1))) || // previous node417 (posn0 < selectedWay.nodes.size()-1) && n.equals(selectedWay.nodes.get(posn0+1))) { // next node418 Main.ds.setSelected(n);419 lastUsedNode = n;420 return;421 }422 }423 424 406 // User clicked last node again, finish way 425 407 if(n0 == n) { 426 lastUsedNode = null; 427 wayIsFinished = true; 428 Main.map.selectSelectTool(true); 408 finishDrawing(); 429 409 return; 430 410 } 431 411 432 412 // Ok we know now that we'll insert a line segment, but will it connect to an 433 413 // existing way or make a new way of its own? The "alt" modifier means that the 434 414 // user wants a new way. 435 Wayway = alt ? null : (selectedWay != null) ? selectedWay : getWayForNode(n0);415 way = alt ? null : (selectedWay != null) ? selectedWay : getWayForNode(n0); 436 416 437 417 // Don't allow creation of self-overlapping ways 438 418 if(way != null) { … … 459 439 460 440 // Connected to a node that's already in the way 461 441 if(way.nodes.contains(n)) { 462 //System.out.println("Stop drawing, node is part of current way");463 442 wayIsFinished = true; 464 443 selection.clear(); 465 444 } … … 497 476 498 477 Main.main.undoRedo.add(c); 499 478 if(!wayIsFinished) lastUsedNode = n; 479 500 480 computeHelperLine(); 501 481 removeHighlighting(); 502 482 Main.map.mapView.repaint(); 503 483 } 504 484 485 /** 486 * Prevent creation of ways that look like this: <----> 487 * This happens if users want to draw a no-exit-sideway from the main way like this: 488 * ^ 489 * |<----> 490 * | 491 * The solution isn't ideal because the main way will end in the side way, which is bad for 492 * navigation software ("drive straight on") but at least easier to fix. Maybe users will fix 493 * it on their own, too. At least it's better than producing an error. 494 * 495 * @param Way the way to check 496 * @param Node the current node (i.e. the one the connection will be made from) 497 * @param Node the target node (i.e. the one the connection will be made to) 498 * @return Boolean True if this would create a selfcontaining way, false otherwise. 499 */ 500 private boolean isSelfContainedWay(Way selectedWay, Node currentNode, Node targetNode) { 501 if(selectedWay != null && selectedWay.nodes != null) { 502 int posn0 = selectedWay.nodes.indexOf(currentNode); 503 if( posn0 != -1 && // n0 is part of way 504 (posn0 >= 1 && targetNode.equals(selectedWay.nodes.get(posn0-1))) || // previous node 505 (posn0 < selectedWay.nodes.size()-1) && targetNode.equals(selectedWay.nodes.get(posn0+1))) { // next node 506 Main.ds.setSelected(targetNode); 507 lastUsedNode = targetNode; 508 return true; 509 } 510 } 511 512 return false; 513 } 514 515 /** 516 * Finds a node to continue drawing from. Decision is based upon given node and way. 517 * @param selectedNode Currently selected node, may be null 518 * @param selectedWay Currently selected way, may be null 519 * @return Node if a suitable node is found, null otherwise 520 */ 521 private Node findNodeToContinueFrom(Node selectedNode, Way selectedWay) { 522 // No nodes or ways have been selected, this occurs when a relation 523 // has been selected or the selection is empty 524 if(selectedNode == null && selectedWay == null) 525 return null; 526 527 if (selectedNode == null) { 528 if (selectedWay.isFirstLastNode(lastUsedNode)) 529 return lastUsedNode; 530 531 // We have a way selected, but no suitable node to continue from. Start anew. 532 return null; 533 } 534 535 if (selectedWay == null) 536 return selectedNode; 537 538 if (selectedWay.isFirstLastNode(selectedNode)) 539 return selectedNode; 540 541 // We have a way and node selected, but it's not at the start/end of the way. Start anew. 542 return null; 543 } 544 505 545 @Override public void mouseMoved(MouseEvent e) { 506 546 if(!Main.map.mapView.isDrawableLayer()) 507 547 return; … … 509 549 // we copy ctrl/alt/shift from the event just in case our global 510 550 // AWTEvent didn't make it through the security manager. Unclear 511 551 // if that can ever happen but better be safe. 552 updateKeyModifiers(e); 553 mousePos = e.getPoint(); 512 554 555 addHighlighting(); 556 computeHelperLine(); 557 } 558 559 private void updateKeyModifiers(InputEvent e) { 513 560 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 514 561 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 515 562 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 516 mousePos = e.getPoint();563 } 517 564 518 addHighlighting(); 519 computeHelperLine(); 565 private void updateKeyModifiers(MouseEvent e) { 566 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 567 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 568 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 520 569 } 521 570 522 571 /** … … 531 580 currentBaseNode = null; 532 581 return; 533 582 } 534 583 535 584 double distance = -1; 536 585 double angle = -1; 537 586 … … 547 596 Main.map.statusLine.setHeading(-1); 548 597 Main.map.statusLine.setDist(-1); 549 598 550 if (!ctrl && mousePos != null) {599 if (!ctrl && !shift && mousePos != null) { 551 600 currentMouseNode = Main.map.mapView.getNearestNode(mousePos); 552 601 } 553 602 … … 585 634 586 635 if (selectedNode == null) { 587 636 if (selectedWay == null) return; 588 if ( lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {637 if (selectedWay.isFirstLastNode(lastUsedNode)) { 589 638 currentBaseNode = lastUsedNode; 590 639 if (lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1) && selectedWay.nodes.size() > 1) { 591 640 previousNode = selectedWay.nodes.get(selectedWay.nodes.size()-2); … … 600 649 } 601 650 602 651 if (currentBaseNode == null || currentBaseNode == currentMouseNode) { 652 updateStatusLine(); 603 653 return; // Don't create zero length way segments. 604 654 } 605 655 … … 616 666 Main.map.statusLine.setDist(distance); 617 667 updateStatusLine(); 618 668 619 if ( (!drawHelperLine || wayIsFinished) && !drawTargetHighlight) return;669 if (!drawHelperLine || wayIsFinished) return; 620 670 Main.map.mapView.repaint(); 621 671 } 622 672 … … 738 788 } 739 789 740 790 // helper for adjustNode 741 static double det(double a, double b, double c, double d) 742 { 791 static double det(double a, double b, double c, double d) { 743 792 return a * d - b * c; 744 793 } 745 794 … … 750 799 if (Main.map.mapView == null) return; 751 800 if (mousePos == null) return; 752 801 753 // if shift key is held ("no auto-connect"), don't draw a line754 if (shift) return;755 756 802 // don't draw line if we don't know where from or where to 757 803 if (currentBaseNode == null || currentMouseEastNorth == null) return; 758 804 … … 781 827 } 782 828 783 829 @Override public String getModeHelpText() { 784 String rv; 830 String rv = ""; 831 /* 832 * No modifiers: all (Connect, Node Re-Use, Auto-Weld) 833 * CTRL: disables node re-use, auto-weld 834 * Shift: disables node re-use 835 * ALT: disables connect 836 */ 785 837 786 if (currentBaseNode != null && !shift) { 787 if (mouseOnExistingNode != null) { 788 if (alt && /* FIXME: way exists */true) 789 rv = tr("Click to create a new way to the existing node."); 838 /* 839 * Status line text generation is split into two parts to keep it maintainable. 840 * First part looks at what will happen to the new node inserted on click and 841 * the second part will look if a connection is made or not. 842 * 843 * Note that this help text is not absolutely accurate as it doesn't catch any special 844 * cases (e.g. when preventing <---> ways). The only special that it catches is when 845 * a way is about to be finished. 846 * 847 * First check what happens to the new node. 848 */ 849 850 // oldHighlights stores the current highlights. If this 851 // list is empty we can assume that we won't do any joins 852 if(ctrl || oldHighlights.isEmpty()) 853 rv = tr("Create new node."); 854 else if(shift) { 855 // We already know oldHighlights is not empty, but shift is pressed. 856 // We can assume the new node will be joined into an existing way 857 rv = tr("Insert new node into {0} way(s).", oldHighlights.size()); 858 } else { 859 // oldHighlights may store a node or way, check if it's a node 860 for(OsmPrimitive x : oldHighlights) { 861 if(x instanceof Node) 862 rv = tr("Select node under cursor."); 790 863 else 791 rv =tr("Click to make a connection to the existing node."); 792 } else { 793 if (alt && /* FIXME: way exists */true) 794 rv = tr("Click to insert a node and create a new way."); 795 else 796 rv = tr("Click to insert a new node and make a connection."); 864 rv = tr("Insert new node into {0} way(s).", oldHighlights.size()); 865 break; 797 866 } 798 867 } 799 else { 800 rv = tr("Click to insert a new node."); 868 869 /* 870 * Check whether a connection will be made 871 */ 872 if (currentBaseNode != null) { 873 if(alt) 874 rv += " " + tr("Start new way from last node."); 875 else 876 rv += " " + tr("Continue way from last node."); 801 877 } 802 878 803 //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way")); 804 //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way")); 805 return rv.toString(); 879 /* 880 * Handle special case: Highlighted node == selected node => finish drawing 881 */ 882 Node n = mouseOnExistingNode; 883 if(n != null && Main.ds.getSelectedNodes().contains(n)) { 884 rv = tr("Finish drawing."); 885 } 886 887 /* 888 * Handle special case: Self-Overlapping or closing way 889 */ 890 if(Main.ds.getSelectedWays().size() > 0 && !wayIsFinished && !alt) { 891 Way w = (Way) Main.ds.getSelectedWays().iterator().next(); 892 for(Node m : w.nodes) { 893 if(m.equals(mouseOnExistingNode) || mouseOnExistingWays.contains(w)) { 894 rv += " " + tr("Finish drawing."); 895 break; 896 } 897 } 898 } 899 900 return rv; 806 901 } 807 902 808 903 @Override public boolean layerIsSupported(Layer l) {
