source: josm/trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java@ 1405

Last change on this file since 1405 was 1405, checked in by stoecker, 15 years ago

final patch for modeless drawing by xeen. Closes #1937

  • Property svn:eol-style set to native
File size: 26.0 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.actions.mapmode;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.AWTEvent;
8import java.awt.BasicStroke;
9import java.awt.Color;
10import java.awt.Cursor;
11import java.awt.Graphics;
12import java.awt.Graphics2D;
13import java.awt.Point;
14import java.awt.Toolkit;
15import java.awt.event.AWTEventListener;
16import java.awt.event.ActionEvent;
17import java.awt.event.InputEvent;
18import java.awt.event.KeyEvent;
19import java.awt.event.MouseEvent;
20import java.awt.geom.GeneralPath;
21import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.LinkedList;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31
32import javax.swing.JComponent;
33import javax.swing.JOptionPane;
34
35import org.openstreetmap.josm.Main;
36import org.openstreetmap.josm.command.AddCommand;
37import org.openstreetmap.josm.command.ChangeCommand;
38import org.openstreetmap.josm.command.Command;
39import org.openstreetmap.josm.command.SequenceCommand;
40import org.openstreetmap.josm.data.SelectionChangedListener;
41import org.openstreetmap.josm.data.coor.EastNorth;
42import org.openstreetmap.josm.data.coor.LatLon;
43import org.openstreetmap.josm.data.osm.DataSet;
44import org.openstreetmap.josm.data.osm.Node;
45import org.openstreetmap.josm.data.osm.OsmPrimitive;
46import org.openstreetmap.josm.data.osm.Way;
47import org.openstreetmap.josm.data.osm.WaySegment;
48import org.openstreetmap.josm.gui.MapFrame;
49import org.openstreetmap.josm.gui.MapView;
50import org.openstreetmap.josm.gui.layer.Layer;
51import org.openstreetmap.josm.gui.layer.MapViewPaintable;
52import org.openstreetmap.josm.gui.layer.OsmDataLayer;
53import org.openstreetmap.josm.tools.ImageProvider;
54import org.openstreetmap.josm.tools.Pair;
55import org.openstreetmap.josm.tools.Shortcut;
56
57/**
58 *
59 */
60public class DrawAction extends MapMode implements MapViewPaintable, SelectionChangedListener, AWTEventListener {
61
62 private static Node lastUsedNode = null;
63 private double PHI=Math.toRadians(90);
64
65 private boolean ctrl;
66 private boolean alt;
67 private boolean shift;
68 private boolean mouseOnExistingNode;
69 private boolean drawHelperLine;
70 private boolean wayIsFinished = false;
71 private Point mousePos;
72 private Color selectedColor;
73
74 private Node currentBaseNode;
75 private EastNorth currentMouseEastNorth;
76
77 public DrawAction(MapFrame mapFrame) {
78 super(tr("Draw"), "node/autonode", tr("Draw nodes"),
79 Shortcut.registerShortcut("mapmode:draw", tr("Mode: {0}", tr("Draw")), KeyEvent.VK_A, Shortcut.GROUP_EDIT),
80 mapFrame, getCursor());
81
82 // Add extra shortcut N
83 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
84 Shortcut.registerShortcut("mapmode:drawfocus", tr("Mode: Draw Focus"), KeyEvent.VK_N, Shortcut.GROUP_EDIT).getKeyStroke(), tr("Draw"));
85 }
86
87 private static Cursor getCursor() {
88 try {
89 return ImageProvider.getCursor("crosshair", null);
90 } catch (Exception e) {
91 }
92 return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
93 }
94
95 @Override public void enterMode() {
96 super.enterMode();
97 selectedColor = Main.pref.getColor(marktr("selected"), Color.red);
98 drawHelperLine = Main.pref.getBoolean("draw.helper-line", true);
99 wayIsFinished = false;
100
101 Main.map.mapView.addMouseListener(this);
102 Main.map.mapView.addMouseMotionListener(this);
103 Main.map.mapView.addTemporaryLayer(this);
104 DataSet.selListeners.add(this);
105 try {
106 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
107 } catch (SecurityException ex) {
108 }
109 // would like to but haven't got mouse position yet:
110 // computeHelperLine(false, false, false);
111 }
112 @Override public void exitMode() {
113 super.exitMode();
114 Main.map.mapView.removeMouseListener(this);
115 Main.map.mapView.removeMouseMotionListener(this);
116 Main.map.mapView.removeTemporaryLayer(this);
117 DataSet.selListeners.remove(this);
118 try {
119 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
120 } catch (SecurityException ex) {
121 }
122 }
123
124 /**
125 * redraw to (possibly) get rid of helper line if selection changes.
126 */
127 public void eventDispatched(AWTEvent event) {
128 if(!Main.map.mapView.isDrawableLayer())
129 return;
130 InputEvent e = (InputEvent) event;
131 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
132 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
133 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
134 computeHelperLine();
135 }
136 /**
137 * redraw to (possibly) get rid of helper line if selection changes.
138 */
139 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
140 if(!Main.map.mapView.isDrawableLayer())
141 return;
142 computeHelperLine();
143 }
144
145 private void tryAgain(MouseEvent e) {
146 Main.ds.setSelected();
147 mouseClicked(e);
148 }
149
150 /**
151 * If user clicked with the left button, add a node at the current mouse
152 * position.
153 *
154 * If in nodeway mode, insert the node into the way.
155 */
156 @Override public void mouseClicked(MouseEvent e) {
157 if (e.getButton() != MouseEvent.BUTTON1)
158 return;
159 if(!Main.map.mapView.isDrawableLayer())
160 return;
161 if(e.getClickCount() > 1) {
162 // A double click equals "user clicked last node again, finish way"
163 lastUsedNode = null;
164 wayIsFinished = true;
165 Main.map.selectSelectTool(true);
166 return;
167 }
168 // we copy ctrl/alt/shift from the event just in case our global
169 // AWTEvent didn't make it through the security manager. Unclear
170 // if that can ever happen but better be safe.
171 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
172 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
173 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
174 mousePos = e.getPoint();
175
176 Collection<OsmPrimitive> selection = Main.ds.getSelected();
177 Collection<Command> cmds = new LinkedList<Command>();
178
179 ArrayList<Way> reuseWays = new ArrayList<Way>(),
180 replacedWays = new ArrayList<Way>();
181 boolean newNode = false;
182 Node n = null;
183
184 if (!ctrl)
185 n = Main.map.mapView.getNearestNode(mousePos);
186
187 if (n != null) {
188 // user clicked on node
189 if (shift || selection.isEmpty()) {
190 // select the clicked node and do nothing else
191 // (this is just a convenience option so that people don't
192 // have to switch modes)
193 Main.ds.setSelected(n);
194 return;
195 }
196 } else {
197 // no node found in clicked area
198 n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
199 if (n.coor.isOutSideWorld()) {
200 JOptionPane.showMessageDialog(Main.parent,
201 tr("Cannot add a node outside of the world."));
202 return;
203 }
204 newNode = true;
205
206 cmds.add(new AddCommand(n));
207
208 if (!ctrl) {
209 // Insert the node into all the nearby way segments
210 List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(e.getPoint());
211 Map<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>();
212 for (WaySegment ws : wss) {
213 List<Integer> is;
214 if (insertPoints.containsKey(ws.way)) {
215 is = insertPoints.get(ws.way);
216 } else {
217 is = new ArrayList<Integer>();
218 insertPoints.put(ws.way, is);
219 }
220
221 is.add(ws.lowerIndex);
222 }
223
224 Set<Pair<Node,Node>> segSet = new HashSet<Pair<Node,Node>>();
225
226 for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) {
227 Way w = insertPoint.getKey();
228 List<Integer> is = insertPoint.getValue();
229
230 Way wnew = new Way(w);
231
232 pruneSuccsAndReverse(is);
233 for (int i : is) segSet.add(
234 Pair.sort(new Pair<Node,Node>(w.nodes.get(i), w.nodes.get(i+1))));
235 for (int i : is) wnew.addNode(i + 1, n);
236
237 cmds.add(new ChangeCommand(insertPoint.getKey(), wnew));
238 replacedWays.add(insertPoint.getKey());
239 reuseWays.add(wnew);
240 }
241
242 adjustNode(segSet, n);
243 }
244 }
245
246 // This part decides whether or not a "segment" (i.e. a connection) is made to an
247 // existing node.
248
249 // For a connection to be made, the user must either have a node selected (connection
250 // is made to that node), or he must have a way selected *and* one of the endpoints
251 // of that way must be the last used node (connection is made to last used node), or
252 // he must have a way and a node selected (connection is made to the selected node).
253
254 // If the above does not apply, the selection is cleared and a new try is started
255 boolean extendedWay = false;
256 boolean wayIsFinishedTemp = wayIsFinished;
257 wayIsFinished = false;
258 if (!shift && selection.size() > 0 && !wayIsFinishedTemp) {
259 Node selectedNode = null;
260 Way selectedWay = null;
261
262 for (OsmPrimitive p : selection) {
263 if (p instanceof Node) {
264 if (selectedNode != null) {
265 // Too many nodes selected to do something useful
266 tryAgain(e);
267 return;
268 }
269 selectedNode = (Node) p;
270 } else if (p instanceof Way) {
271 if (selectedWay != null) {
272 // Too many ways selected to do something useful
273 tryAgain(e);
274 return;
275 }
276 selectedWay = (Way) p;
277 }
278 }
279
280 // No nodes or ways have been selected, try again with no selection
281 // This occurs when a relation has been selected
282 if(selectedNode == null && selectedWay == null) {
283 tryAgain(e);
284 return;
285 }
286
287 // the node from which we make a connection
288 Node n0 = null;
289
290 if (selectedNode == null) {
291 if (selectedWay.isFirstLastNode(lastUsedNode)) {
292 n0 = lastUsedNode;
293 } else {
294 // We have a way selected, but no suitable node to continue from. Start anew.
295 tryAgain(e);
296 return;
297 }
298 } else if (selectedWay == null) {
299 n0 = selectedNode;
300 } else {
301 if (selectedWay.isFirstLastNode(selectedNode)) {
302 n0 = selectedNode;
303 } else {
304 // We have a way and node selected, but it's not at the start/end of the way. Start anew.
305 tryAgain(e);
306 return;
307 }
308 }
309
310 // Prevent creation of ways that look like this: <---->
311 // This happens if users want to draw a no-exit-sideway from the main way like this:
312 // ^
313 // |<---->
314 // |
315 // The solution isn't ideal because the main way will end in the side way, which is bad for
316 // navigation software ("drive straight on") but at least easier to fix. Maybe users will fix
317 // it on their own, too. At least it's better than producing an error.
318 if(selectedWay != null && selectedWay.nodes != null) {
319 int posn0 = selectedWay.nodes.indexOf(n0);
320 if( posn0 != -1 && // n0 is part of way
321 (posn0 >= 1 && n.equals(selectedWay.nodes.get(posn0-1))) || // previous node
322 (posn0 < selectedWay.nodes.size()-1) && n.equals(selectedWay.nodes.get(posn0+1))) { // next node
323 Main.ds.setSelected(n);
324 lastUsedNode = n;
325 return;
326 }
327 }
328
329 // User clicked last node again, finish way
330 if(n0 == n) {
331 lastUsedNode = null;
332 wayIsFinished = true;
333 Main.map.selectSelectTool(true);
334 return;
335 }
336
337 // Ok we know now that we'll insert a line segment, but will it connect to an
338 // existing way or make a new way of its own? The "alt" modifier means that the
339 // user wants a new way.
340 Way way = alt ? null : (selectedWay != null) ? selectedWay : getWayForNode(n0);
341
342 // Don't allow creation of self-overlapping ways
343 if(way != null) {
344 int nodeCount=0;
345 for (Node p : way.nodes)
346 if(p.equals(n0)) nodeCount++;
347 if(nodeCount > 1) way = null;
348 }
349
350 if (way == null) {
351 way = new Way();
352 way.addNode(n0);
353 cmds.add(new AddCommand(way));
354 } else {
355 int i;
356 if ((i = replacedWays.indexOf(way)) != -1) {
357 way = reuseWays.get(i);
358 } else {
359 Way wnew = new Way(way);
360 cmds.add(new ChangeCommand(way, wnew));
361 way = wnew;
362 }
363 }
364
365 // Connected to a node that's already in the way
366 if(way.nodes.contains(n)) {
367 //System.out.println("Stop drawing, node is part of current way");
368 wayIsFinished = true;
369 selection.clear();
370 }
371
372 // Add new node to way
373 if (way.nodes.get(way.nodes.size() - 1) == n0)
374 way.addNode(n);
375 else
376 way.addNode(0, n);
377
378 extendedWay = true;
379 Main.ds.setSelected(way);
380 }
381
382 String title;
383 if (!extendedWay) {
384 if (!newNode) {
385 return; // We didn't do anything.
386 } else if (reuseWays.isEmpty()) {
387 title = tr("Add node");
388 } else {
389 title = tr("Add node into way");
390 for (Way w : reuseWays) w.selected = false;
391 }
392 Main.ds.setSelected(n);
393 } else if (!newNode) {
394 title = tr("Connect existing way to node");
395 } else if (reuseWays.isEmpty()) {
396 title = tr("Add a new node to an existing way");
397 } else {
398 title = tr("Add node into way and connect");
399 }
400
401 Command c = new SequenceCommand(title, cmds);
402
403 Main.main.undoRedo.add(c);
404 if(!wayIsFinished) lastUsedNode = n;
405 computeHelperLine();
406 Main.map.mapView.repaint();
407 }
408
409 @Override public void mouseMoved(MouseEvent e) {
410 if(!Main.map.mapView.isDrawableLayer())
411 return;
412
413 // we copy ctrl/alt/shift from the event just in case our global
414 // AWTEvent didn't make it through the security manager. Unclear
415 // if that can ever happen but better be safe.
416
417 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
418 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
419 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
420 mousePos = e.getPoint();
421
422 computeHelperLine();
423 }
424
425 /**
426 * This method prepares data required for painting the "helper line" from
427 * the last used position to the mouse cursor. It duplicates some code from
428 * mouseClicked() (FIXME).
429 */
430 private void computeHelperLine() {
431 if (mousePos == null) {
432 // Don't draw the line.
433 currentMouseEastNorth = null;
434 currentBaseNode = null;
435 return;
436 }
437
438 double distance = -1;
439 double angle = -1;
440
441 Collection<OsmPrimitive> selection = Main.ds.getSelected();
442
443 Node selectedNode = null;
444 Way selectedWay = null;
445 Node currentMouseNode = null;
446 mouseOnExistingNode = false;
447
448 Main.map.statusLine.setAngle(-1);
449 Main.map.statusLine.setHeading(-1);
450 Main.map.statusLine.setDist(-1);
451
452 if (!ctrl && mousePos != null) {
453 currentMouseNode = Main.map.mapView.getNearestNode(mousePos);
454 }
455
456 if (currentMouseNode != null) {
457 // user clicked on node
458 if (selection.isEmpty()) return;
459 currentMouseEastNorth = currentMouseNode.eastNorth;
460 mouseOnExistingNode = true;
461 } else {
462 // no node found in clicked area
463 currentMouseEastNorth = Main.map.mapView.getEastNorth(mousePos.x, mousePos.y);
464 }
465
466 for (OsmPrimitive p : selection) {
467 if (p instanceof Node) {
468 if (selectedNode != null) return;
469 selectedNode = (Node) p;
470 } else if (p instanceof Way) {
471 if (selectedWay != null) return;
472 selectedWay = (Way) p;
473 }
474 }
475
476 // the node from which we make a connection
477 currentBaseNode = null;
478 Node previousNode = null;
479
480 if (selectedNode == null) {
481 if (selectedWay == null) return;
482 if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
483 currentBaseNode = lastUsedNode;
484 if (lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1) && selectedWay.nodes.size() > 1) {
485 previousNode = selectedWay.nodes.get(selectedWay.nodes.size()-2);
486 }
487 }
488 } else if (selectedWay == null) {
489 currentBaseNode = selectedNode;
490 } else {
491 if (selectedNode == selectedWay.nodes.get(0) || selectedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
492 currentBaseNode = selectedNode;
493 }
494 }
495
496 if (currentBaseNode == null || currentBaseNode == currentMouseNode) {
497 return; // Don't create zero length way segments.
498 }
499
500 // find out the distance, in metres, between the base point and the mouse cursor
501 LatLon mouseLatLon = Main.proj.eastNorth2latlon(currentMouseEastNorth);
502 distance = currentBaseNode.coor.greatCircleDistance(mouseLatLon);
503 double hdg = Math.toDegrees(currentBaseNode.coor.heading(mouseLatLon));
504 if (previousNode != null) {
505 angle = hdg - Math.toDegrees(previousNode.coor.heading(currentBaseNode.coor));
506 if (angle < 0) angle += 360;
507 }
508 Main.map.statusLine.setAngle(angle);
509 Main.map.statusLine.setHeading(hdg);
510 Main.map.statusLine.setDist(distance);
511 updateStatusLine();
512
513 if (!drawHelperLine || wayIsFinished) return;
514
515 Main.map.mapView.repaint();
516 }
517
518 /**
519 * Repaint on mouse exit so that the helper line goes away.
520 */
521 @Override public void mouseExited(MouseEvent e) {
522 if(!Main.map.mapView.isDrawableLayer())
523 return;
524 mousePos = e.getPoint();
525 Main.map.mapView.repaint();
526 }
527
528 /**
529 * @return If the node is the end of exactly one way, return this.
530 * <code>null</code> otherwise.
531 */
532 public static Way getWayForNode(Node n) {
533 Way way = null;
534 for (Way w : Main.ds.ways) {
535 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
536 Node firstNode = w.nodes.get(0);
537 Node lastNode = w.nodes.get(w.nodes.size() - 1);
538 if ((firstNode == n || lastNode == n) && (firstNode != lastNode)) {
539 if (way != null)
540 return null;
541 way = w;
542 }
543 }
544 return way;
545 }
546
547 private static void pruneSuccsAndReverse(List<Integer> is) {
548 //if (is.size() < 2) return;
549
550 HashSet<Integer> is2 = new HashSet<Integer>();
551 for (int i : is) {
552 if (!is2.contains(i - 1) && !is2.contains(i + 1)) {
553 is2.add(i);
554 }
555 }
556 is.clear();
557 is.addAll(is2);
558 Collections.sort(is);
559 Collections.reverse(is);
560 }
561
562 /**
563 * Adjusts the position of a node to lie on a segment (or a segment
564 * intersection).
565 *
566 * If one or more than two segments are passed, the node is adjusted
567 * to lie on the first segment that is passed.
568 *
569 * If two segments are passed, the node is adjusted to be at their
570 * intersection.
571 *
572 * No action is taken if no segments are passed.
573 *
574 * @param segs the segments to use as a reference when adjusting
575 * @param n the node to adjust
576 */
577 private static void adjustNode(Collection<Pair<Node,Node>> segs, Node n) {
578
579 switch (segs.size()) {
580 case 0:
581 return;
582 case 2:
583 // This computes the intersection between
584 // the two segments and adjusts the node position.
585 Iterator<Pair<Node,Node>> i = segs.iterator();
586 Pair<Node,Node> seg = i.next();
587 EastNorth A = seg.a.eastNorth;
588 EastNorth B = seg.b.eastNorth;
589 seg = i.next();
590 EastNorth C = seg.a.eastNorth;
591 EastNorth D = seg.b.eastNorth;
592
593 double u=det(B.east() - A.east(), B.north() - A.north(), C.east() - D.east(), C.north() - D.north());
594
595 // Check for parallel segments and do nothing if they are
596 // In practice this will probably only happen when a way has been duplicated
597
598 if (u == 0) return;
599
600 // q is a number between 0 and 1
601 // It is the point in the segment where the intersection occurs
602 // if the segment is scaled to lenght 1
603
604 double q = det(B.north() - C.north(), B.east() - C.east(), D.north() - C.north(), D.east() - C.east()) / u;
605 EastNorth intersection = new EastNorth(
606 B.east() + q * (A.east() - B.east()),
607 B.north() + q * (A.north() - B.north()));
608
609 int snapToIntersectionThreshold
610 = Main.pref.getInteger("edit.snap-intersection-threshold",10);
611
612 // only adjust to intersection if within snapToIntersectionThreshold pixel of mouse click; otherwise
613 // fall through to default action.
614 // (for semi-parallel lines, intersection might be miles away!)
615 if (Main.map.mapView.getPoint(n.eastNorth).distance(Main.map.mapView.getPoint(intersection)) < snapToIntersectionThreshold) {
616 n.eastNorth = intersection;
617 return;
618 }
619
620 default:
621 EastNorth P = n.eastNorth;
622 seg = segs.iterator().next();
623 A = seg.a.eastNorth;
624 B = seg.b.eastNorth;
625 double a = P.distanceSq(B);
626 double b = P.distanceSq(A);
627 double c = A.distanceSq(B);
628 q = (a - b + c) / (2*c);
629 n.eastNorth = new EastNorth(
630 B.east() + q * (A.east() - B.east()),
631 B.north() + q * (A.north() - B.north()));
632 }
633 }
634
635 // helper for adjustNode
636 static double det(double a, double b, double c, double d)
637 {
638 return a * d - b * c;
639 }
640
641 public void paint(Graphics g, MapView mv) {
642
643 // don't draw line if disabled in prefs
644 if (!drawHelperLine) return;
645
646 // sanity checks
647 if (Main.map.mapView == null) return;
648 if (mousePos == null) return;
649
650 // if shift key is held ("no auto-connect"), don't draw a line
651 if (shift) return;
652
653 // don't draw line if we don't know where from or where to
654 if (currentBaseNode == null) return;
655 if (currentMouseEastNorth == null) return;
656
657 // don't draw line if mouse is outside window
658 if (!Main.map.mapView.getBounds().contains(mousePos)) return;
659
660 Graphics2D g2 = (Graphics2D) g;
661 g2.setColor(selectedColor);
662 g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
663 GeneralPath b = new GeneralPath();
664 Point p1=mv.getPoint(currentBaseNode.eastNorth);
665 Point p2=mv.getPoint(currentMouseEastNorth);
666
667 double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
668
669 b.moveTo(p1.x,p1.y); b.lineTo(p2.x, p2.y);
670
671 // if alt key is held ("start new way"), draw a little perpendicular line
672 if (alt) {
673 b.moveTo((int)(p1.x + 8*Math.cos(t+PHI)), (int)(p1.y + 8*Math.sin(t+PHI)));
674 b.lineTo((int)(p1.x + 8*Math.cos(t-PHI)), (int)(p1.y + 8*Math.sin(t-PHI)));
675 }
676
677 g2.draw(b);
678 g2.setStroke(new BasicStroke(1));
679
680 }
681
682 @Override public String getModeHelpText() {
683 String rv;
684
685 if (currentBaseNode != null && !shift) {
686 if (mouseOnExistingNode) {
687 if (alt && /* FIXME: way exists */true)
688 rv = tr("Click to create a new way to the existing node.");
689 else
690 rv =tr("Click to make a connection to the existing node.");
691 } else {
692 if (alt && /* FIXME: way exists */true)
693 rv = tr("Click to insert a node and create a new way.");
694 else
695 rv = tr("Click to insert a new node and make a connection.");
696 }
697 }
698 else {
699 rv = tr("Click to insert a new node.");
700 }
701
702 //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way"));
703 //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way"));
704 return rv.toString();
705 }
706
707 @Override public boolean layerIsSupported(Layer l) {
708 return l instanceof OsmDataLayer;
709 }
710}
Note: See TracBrowser for help on using the repository browser.