source: josm/trunk/src/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyAction.java@ 13964

Last change on this file since 13964 was 13964, checked in by Don-vip, 6 years ago

fix #16415 - NPE

  • Property svn:eol-style set to native
File size: 25.4 KB
RevLine 
[8378]1// License: GPL. For details, see LICENSE file.
[4846]2package org.openstreetmap.josm.actions.mapmode;
3
[6028]4import static org.openstreetmap.josm.tools.I18n.marktr;
[4846]5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
[10875]8import java.awt.BasicStroke;
[4846]9import java.awt.Color;
10import java.awt.Cursor;
11import java.awt.Graphics2D;
12import java.awt.Point;
13import java.awt.event.KeyEvent;
14import java.awt.event.MouseEvent;
15import java.util.ArrayList;
16import java.util.Collection;
[11082]17import java.util.Collections;
[4846]18import java.util.LinkedList;
19import java.util.List;
20
21import javax.swing.JOptionPane;
22
23import org.openstreetmap.josm.Main;
24import org.openstreetmap.josm.command.AddCommand;
25import org.openstreetmap.josm.command.ChangeCommand;
26import org.openstreetmap.josm.command.Command;
27import org.openstreetmap.josm.command.DeleteCommand;
28import org.openstreetmap.josm.command.MoveCommand;
29import org.openstreetmap.josm.command.SequenceCommand;
30import org.openstreetmap.josm.data.Bounds;
31import org.openstreetmap.josm.data.coor.EastNorth;
[13925]32import org.openstreetmap.josm.data.osm.DataSelectionListener;
[4846]33import org.openstreetmap.josm.data.osm.DataSet;
34import org.openstreetmap.josm.data.osm.Node;
35import org.openstreetmap.josm.data.osm.OsmPrimitive;
36import org.openstreetmap.josm.data.osm.Way;
37import org.openstreetmap.josm.data.osm.WaySegment;
[13925]38import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
[10875]39import org.openstreetmap.josm.data.preferences.CachingProperty;
[13048]40import org.openstreetmap.josm.data.preferences.IntegerProperty;
[12987]41import org.openstreetmap.josm.data.preferences.NamedColorProperty;
[10875]42import org.openstreetmap.josm.data.preferences.StrokeProperty;
[12630]43import org.openstreetmap.josm.gui.MainApplication;
44import org.openstreetmap.josm.gui.MapFrame;
[4846]45import org.openstreetmap.josm.gui.MapView;
[10875]46import org.openstreetmap.josm.gui.draw.MapViewPath;
47import org.openstreetmap.josm.gui.draw.SymbolShape;
[10031]48import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
[4846]49import org.openstreetmap.josm.gui.layer.Layer;
[12517]50import org.openstreetmap.josm.gui.util.ModifierExListener;
[4846]51import org.openstreetmap.josm.tools.ImageProvider;
[13512]52import org.openstreetmap.josm.tools.Logging;
[4846]53import org.openstreetmap.josm.tools.Pair;
54import org.openstreetmap.josm.tools.Shortcut;
55
56/**
[12581]57 * A special map mode that is optimized for improving way geometry.
58 * (by efficiently moving, adding and deleting way-nodes)
59 *
[6830]60 * @author Alexander Kachkaev <alexander@kachkaev.ru>, 2011
[4846]61 */
[13925]62public class ImproveWayAccuracyAction extends MapMode implements DataSelectionListener, ModifierExListener {
[4846]63
[13277]64 private static final String CROSSHAIR = /* ICON(cursor/)*/ "crosshair";
[11913]65
[11978]66 enum State {
[10875]67 SELECTING, IMPROVING
[4846]68 }
69
70 private State state;
71
72 private MapView mv;
73
74 private static final long serialVersionUID = 42L;
75
[8308]76 private transient Way targetWay;
[8840]77 private transient Node candidateNode;
78 private transient WaySegment candidateSegment;
[4846]79
[8840]80 private Point mousePos;
81 private boolean dragging;
[4846]82
[13277]83 private final Cursor cursorSelect = ImageProvider.getCursor(/* ICON(cursor/)*/ "normal", /* ICON(cursor/modifier/)*/ "mode");
84 private final Cursor cursorSelectHover = ImageProvider.getCursor(/* ICON(cursor/)*/ "hand", /* ICON(cursor/modifier/)*/ "mode");
[11913]85 private final Cursor cursorImprove = ImageProvider.getCursor(CROSSHAIR, null);
[13277]86 private final Cursor cursorImproveAdd = ImageProvider.getCursor(CROSSHAIR, /* ICON(cursor/modifier/)*/ "addnode");
87 private final Cursor cursorImproveDelete = ImageProvider.getCursor(CROSSHAIR, /* ICON(cursor/modifier/)*/ "delete_node");
88 private final Cursor cursorImproveAddLock = ImageProvider.getCursor(CROSSHAIR, /* ICON(cursor/modifier/)*/ "add_node_lock");
89 private final Cursor cursorImproveLock = ImageProvider.getCursor(CROSSHAIR, /* ICON(cursor/modifier/)*/ "lock");
[4846]90
[6028]91 private Color guideColor;
[6069]92
[10875]93 private static final CachingProperty<BasicStroke> SELECT_TARGET_WAY_STROKE
94 = new StrokeProperty("improvewayaccuracy.stroke.select-target", "2").cached();
95 private static final CachingProperty<BasicStroke> MOVE_NODE_STROKE
96 = new StrokeProperty("improvewayaccuracy.stroke.move-node", "1 6").cached();
97 private static final CachingProperty<BasicStroke> MOVE_NODE_INTERSECTING_STROKE
98 = new StrokeProperty("improvewayaccuracy.stroke.move-node-intersecting", "1 2 6").cached();
99 private static final CachingProperty<BasicStroke> ADD_NODE_STROKE
100 = new StrokeProperty("improvewayaccuracy.stroke.add-node", "1").cached();
101 private static final CachingProperty<BasicStroke> DELETE_NODE_STROKE
102 = new StrokeProperty("improvewayaccuracy.stroke.delete-node", "1").cached();
103 private static final CachingProperty<Integer> DOT_SIZE
104 = new IntegerProperty("improvewayaccuracy.dot-size", 6).cached();
105
[8840]106 private boolean selectionChangedBlocked;
[4846]107
108 protected String oldModeHelpText;
109
[10214]110 private final transient AbstractMapViewPaintable temporaryLayer = new AbstractMapViewPaintable() {
[10031]111 @Override
112 public void paint(Graphics2D g, MapView mv, Bounds bbox) {
[10047]113 ImproveWayAccuracyAction.this.paint(g, mv, bbox);
[10031]114 }
115 };
116
[7278]117 /**
118 * Constructs a new {@code ImproveWayAccuracyAction}.
[11713]119 * @since 11713
[7278]120 */
[11713]121 public ImproveWayAccuracyAction() {
[8061]122 super(tr("Improve Way Accuracy"), "improvewayaccuracy",
[4851]123 tr("Improve Way Accuracy mode"),
124 Shortcut.registerShortcut("mapmode:ImproveWayAccuracy",
[4846]125 tr("Mode: {0}", tr("Improve Way Accuracy")),
[11713]126 KeyEvent.VK_W, Shortcut.DIRECT), Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
[4846]127
[7227]128 readPreferences();
[4846]129 }
130
131 // -------------------------------------------------------------------------
132 // Mode methods
133 // -------------------------------------------------------------------------
134 @Override
135 public void enterMode() {
136 if (!isEnabled()) {
137 return;
138 }
139 super.enterMode();
[7227]140 readPreferences();
[4846]141
[12630]142 MapFrame map = MainApplication.getMap();
143 mv = map.mapView;
[4846]144 mousePos = null;
145 oldModeHelpText = "";
146
[10382]147 if (getLayerManager().getEditDataSet() == null) {
[4846]148 return;
149 }
150
151 updateStateByCurrentSelection();
152
[12630]153 map.mapView.addMouseListener(this);
154 map.mapView.addMouseMotionListener(this);
155 map.mapView.addTemporaryLayer(temporaryLayer);
[13925]156 SelectionEventManager.getInstance().addSelectionListener(this);
[4846]157
[12630]158 map.keyDetector.addModifierExListener(this);
[4846]159 }
160
[9572]161 @Override
162 protected void readPreferences() {
[12987]163 guideColor = new NamedColorProperty(marktr("improve way accuracy helper line"), Color.RED).get();
[7227]164 }
165
[4846]166 @Override
167 public void exitMode() {
168 super.exitMode();
169
[12630]170 MapFrame map = MainApplication.getMap();
171 map.mapView.removeMouseListener(this);
172 map.mapView.removeMouseMotionListener(this);
173 map.mapView.removeTemporaryLayer(temporaryLayer);
[13925]174 SelectionEventManager.getInstance().removeSelectionListener(this);
[4846]175
[12630]176 map.keyDetector.removeModifierExListener(this);
[10031]177 temporaryLayer.invalidate();
[4846]178 }
179
180 @Override
181 protected void updateStatusLine() {
182 String newModeHelpText = getModeHelpText();
183 if (!newModeHelpText.equals(oldModeHelpText)) {
184 oldModeHelpText = newModeHelpText;
[12630]185 MapFrame map = MainApplication.getMap();
186 map.statusLine.setHelpText(newModeHelpText);
187 map.statusLine.repaint();
[4846]188 }
189 }
190
191 @Override
192 public String getModeHelpText() {
[10875]193 if (state == State.SELECTING) {
[4846]194 if (targetWay != null) {
195 return tr("Click on the way to start improving its shape.");
196 } else {
197 return tr("Select a way that you want to make more accurate.");
198 }
199 } else {
200 if (ctrl) {
201 return tr("Click to add a new node. Release Ctrl to move existing nodes or hold Alt to delete.");
202 } else if (alt) {
203 return tr("Click to delete the highlighted node. Release Alt to move existing nodes or hold Ctrl to add new nodes.");
204 } else {
205 return tr("Click to move the highlighted node. Hold Ctrl to add new nodes, or Alt to delete.");
206 }
207 }
208 }
209
210 @Override
211 public boolean layerIsSupported(Layer l) {
[13434]212 return isEditableDataLayer(l);
[4846]213 }
214
215 @Override
216 protected void updateEnabledState() {
[10382]217 setEnabled(getLayerManager().getEditLayer() != null);
[4846]218 }
219
220 // -------------------------------------------------------------------------
221 // MapViewPaintable methods
222 // -------------------------------------------------------------------------
223 /**
224 * Redraws temporary layer. Highlights targetWay in select mode. Draws
225 * preview lines in improve mode and highlights the candidateNode
[10031]226 * @param g The graphics
227 * @param mv The map view
228 * @param bbox The bounding box
[4846]229 */
230 public void paint(Graphics2D g, MapView mv, Bounds bbox) {
231 if (mousePos == null) {
232 return;
233 }
234
235 g.setColor(guideColor);
236
[10875]237 if (state == State.SELECTING && targetWay != null) {
[4846]238 // Highlighting the targetWay in Selecting state
239 // Non-native highlighting is used, because sometimes highlighted
240 // segments are covered with others, which is bad.
[11144]241 BasicStroke stroke = SELECT_TARGET_WAY_STROKE.get();
242 g.setStroke(stroke);
[4846]243
244 List<Node> nodes = targetWay.getNodes();
245
[11144]246 g.draw(new MapViewPath(mv).append(nodes, false).computeClippedLine(stroke));
[4846]247
[10875]248 } else if (state == State.IMPROVING) {
[4846]249 // Drawing preview lines and highlighting the node
250 // that is going to be moved.
251 // Non-native highlighting is used here as well.
252
253 // Finding endpoints
[10875]254 Node p1 = null;
255 Node p2 = null;
[4846]256 if (ctrl && candidateSegment != null) {
[10875]257 g.setStroke(ADD_NODE_STROKE.get());
[13512]258 try {
259 p1 = candidateSegment.getFirstNode();
260 p2 = candidateSegment.getSecondNode();
261 } catch (ArrayIndexOutOfBoundsException e) {
262 Logging.error(e);
263 }
[4846]264 } else if (!alt && !ctrl && candidateNode != null) {
[10875]265 g.setStroke(MOVE_NODE_STROKE.get());
[4846]266 List<Pair<Node, Node>> wpps = targetWay.getNodePairs(false);
267 for (Pair<Node, Node> wpp : wpps) {
268 if (wpp.a == candidateNode) {
[10875]269 p1 = wpp.b;
[4846]270 }
271 if (wpp.b == candidateNode) {
[10875]272 p2 = wpp.a;
[4846]273 }
274 if (p1 != null && p2 != null) {
275 break;
276 }
277 }
278 } else if (alt && !ctrl && candidateNode != null) {
[10875]279 g.setStroke(DELETE_NODE_STROKE.get());
[4846]280 List<Node> nodes = targetWay.getNodes();
281 int index = nodes.indexOf(candidateNode);
282
283 // Only draw line if node is not first and/or last
[13048]284 if (index > 0 && index < (nodes.size() - 1)) {
[10875]285 p1 = nodes.get(index - 1);
286 p2 = nodes.get(index + 1);
[10701]287 } else if (targetWay.isClosed()) {
[10875]288 p1 = targetWay.getNode(1);
289 p2 = targetWay.getNode(nodes.size() - 2);
[4846]290 }
291 // TODO: indicate what part that will be deleted? (for end nodes)
292 }
293
294
295 // Drawing preview lines
[10875]296 MapViewPath b = new MapViewPath(mv);
[4846]297 if (alt && !ctrl) {
298 // In delete mode
299 if (p1 != null && p2 != null) {
[10875]300 b.moveTo(p1);
301 b.lineTo(p2);
[4846]302 }
303 } else {
304 // In add or move mode
305 if (p1 != null) {
306 b.moveTo(mousePos.x, mousePos.y);
[10875]307 b.lineTo(p1);
[4846]308 }
309 if (p2 != null) {
310 b.moveTo(mousePos.x, mousePos.y);
[10875]311 b.lineTo(p2);
[4846]312 }
313 }
[11144]314 g.draw(b.computeClippedLine(g.getStroke()));
[4846]315
316 // Highlighting candidateNode
317 if (candidateNode != null) {
[10875]318 p1 = candidateNode;
319 g.fill(new MapViewPath(mv).shapeAround(p1, SymbolShape.SQUARE, DOT_SIZE.get()));
[4846]320 }
321
[9358]322 if (!alt && !ctrl && candidateNode != null) {
323 b.reset();
324 drawIntersectingWayHelperLines(mv, b);
[10875]325 g.setStroke(MOVE_NODE_INTERSECTING_STROKE.get());
[11144]326 g.draw(b.computeClippedLine(g.getStroke()));
[9358]327 }
328
[4846]329 }
330 }
331
[10875]332 protected void drawIntersectingWayHelperLines(MapView mv, MapViewPath b) {
[9358]333 for (final OsmPrimitive referrer : candidateNode.getReferrers()) {
334 if (!(referrer instanceof Way) || targetWay.equals(referrer)) {
335 continue;
336 }
337 final List<Node> nodes = ((Way) referrer).getNodes();
338 for (int i = 0; i < nodes.size(); i++) {
339 if (!candidateNode.equals(nodes.get(i))) {
340 continue;
341 }
342 if (i > 0) {
343 b.moveTo(mousePos.x, mousePos.y);
[10875]344 b.lineTo(nodes.get(i - 1));
[9358]345 }
346 if (i < nodes.size() - 1) {
347 b.moveTo(mousePos.x, mousePos.y);
[10875]348 b.lineTo(nodes.get(i + 1));
[9358]349 }
350 }
351 }
352 }
353
[4846]354 // -------------------------------------------------------------------------
355 // Event handlers
356 // -------------------------------------------------------------------------
357 @Override
[12517]358 public void modifiersExChanged(int modifiers) {
[12630]359 if (!MainApplication.isDisplayingMapView() || !MainApplication.getMap().mapView.isActiveLayerDrawable()) {
[4846]360 return;
361 }
[12517]362 updateKeyModifiersEx(modifiers);
[4846]363 updateCursorDependentObjectsIfNeeded();
364 updateCursor();
365 updateStatusLine();
[10031]366 temporaryLayer.invalidate();
[4846]367 }
368
369 @Override
[13925]370 public void selectionChanged(SelectionChangeEvent event) {
[4846]371 if (selectionChangedBlocked) {
372 return;
373 }
374 updateStateByCurrentSelection();
375 }
376
377 @Override
378 public void mouseDragged(MouseEvent e) {
379 dragging = true;
380 mouseMoved(e);
381 }
382
383 @Override
384 public void mouseMoved(MouseEvent e) {
385 if (!isEnabled()) {
386 return;
387 }
388
389 mousePos = e.getPoint();
390
391 updateKeyModifiers(e);
392 updateCursorDependentObjectsIfNeeded();
393 updateCursor();
394 updateStatusLine();
[10031]395 temporaryLayer.invalidate();
[4846]396 }
397
398 @Override
399 public void mouseReleased(MouseEvent e) {
400 dragging = false;
401 if (!isEnabled() || e.getButton() != MouseEvent.BUTTON1) {
402 return;
403 }
404
[12726]405 DataSet ds = getLayerManager().getEditDataSet();
[4846]406 updateKeyModifiers(e);
407 mousePos = e.getPoint();
408
[10875]409 if (state == State.SELECTING) {
[4846]410 if (targetWay != null) {
[12726]411 ds.setSelected(targetWay.getPrimitiveId());
[4846]412 updateStateByCurrentSelection();
413 }
[11381]414 } else if (state == State.IMPROVING) {
[4846]415 // Checking if the new coordinate is outside of the world
416 if (mv.getLatLon(mousePos.x, mousePos.y).isOutSideWorld()) {
417 JOptionPane.showMessageDialog(Main.parent,
[8915]418 tr("Cannot add a node outside of the world."),
[4846]419 tr("Warning"), JOptionPane.WARNING_MESSAGE);
420 return;
421 }
422
423 if (ctrl && !alt && candidateSegment != null) {
424 // Adding a new node to the highlighted segment
425 // Important: If there are other ways containing the same
426 // segment, a node must added to all of that ways.
[7005]427 Collection<Command> virtualCmds = new LinkedList<>();
[4846]428
429 // Creating a new node
430 Node virtualNode = new Node(mv.getEastNorth(mousePos.x,
431 mousePos.y));
[12726]432 virtualCmds.add(new AddCommand(ds, virtualNode));
[4846]433
434 // Looking for candidateSegment copies in ways that are
435 // referenced
436 // by candidateSegment nodes
437 List<Way> firstNodeWays = OsmPrimitive.getFilteredList(
438 candidateSegment.getFirstNode().getReferrers(),
439 Way.class);
440 List<Way> secondNodeWays = OsmPrimitive.getFilteredList(
441 candidateSegment.getFirstNode().getReferrers(),
442 Way.class);
443
[7005]444 Collection<WaySegment> virtualSegments = new LinkedList<>();
[4846]445 for (Way w : firstNodeWays) {
446 List<Pair<Node, Node>> wpps = w.getNodePairs(true);
447 for (Way w2 : secondNodeWays) {
448 if (!w.equals(w2)) {
449 continue;
450 }
451 // A way is referenced in both nodes.
452 // Checking if there is such segment
453 int i = -1;
454 for (Pair<Node, Node> wpp : wpps) {
455 ++i;
[8345]456 boolean ab = wpp.a.equals(candidateSegment.getFirstNode())
457 && wpp.b.equals(candidateSegment.getSecondNode());
458 boolean ba = wpp.b.equals(candidateSegment.getFirstNode())
459 && wpp.a.equals(candidateSegment.getSecondNode());
460 if (ab || ba) {
[4846]461 virtualSegments.add(new WaySegment(w, i));
462 }
463 }
464 }
465 }
466
467 // Adding the node to all segments found
468 for (WaySegment virtualSegment : virtualSegments) {
469 Way w = virtualSegment.way;
470 Way wnew = new Way(w);
471 wnew.addNode(virtualSegment.lowerIndex + 1, virtualNode);
472 virtualCmds.add(new ChangeCommand(w, wnew));
473 }
474
475 // Finishing the sequence command
[6388]476 String text = trn("Add a new node to way",
477 "Add a new node to {0} ways",
[4846]478 virtualSegments.size(), virtualSegments.size());
479
[12641]480 MainApplication.undoRedo.add(new SequenceCommand(text, virtualCmds));
[4846]481
482 } else if (alt && !ctrl && candidateNode != null) {
483 // Deleting the highlighted node
484
485 //check to see if node is in use by more than one object
486 List<OsmPrimitive> referrers = candidateNode.getReferrers();
487 List<Way> ways = OsmPrimitive.getFilteredList(referrers, Way.class);
488 if (referrers.size() != 1 || ways.size() != 1) {
[8892]489 // detach node from way
490 final Way newWay = new Way(targetWay);
491 final List<Node> nodes = newWay.getNodes();
492 nodes.remove(candidateNode);
493 newWay.setNodes(nodes);
[11082]494 if (nodes.size() < 2) {
[12718]495 final Command deleteCmd = DeleteCommand.delete(Collections.singleton(targetWay), true);
[11082]496 if (deleteCmd != null) {
[12641]497 MainApplication.undoRedo.add(deleteCmd);
[11082]498 }
499 } else {
[12641]500 MainApplication.undoRedo.add(new ChangeCommand(targetWay, newWay));
[11082]501 }
[6355]502 } else if (candidateNode.isTagged()) {
[4846]503 JOptionPane.showMessageDialog(Main.parent,
504 tr("Cannot delete node that has tags"),
505 tr("Error"), JOptionPane.ERROR_MESSAGE);
506 } else {
[12718]507 final Command deleteCmd = DeleteCommand.delete(Collections.singleton(candidateNode), true);
[5947]508 if (deleteCmd != null) {
[12641]509 MainApplication.undoRedo.add(deleteCmd);
[5947]510 }
[4846]511 }
512
513
514 } else if (candidateNode != null) {
515 // Moving the highlighted node
516 EastNorth nodeEN = candidateNode.getEastNorth();
517 EastNorth cursorEN = mv.getEastNorth(mousePos.x, mousePos.y);
518
[12641]519 MainApplication.undoRedo.add(new MoveCommand(candidateNode, cursorEN.east() - nodeEN.east(), cursorEN.north() - nodeEN.north()));
[4846]520 }
521 }
522
523 mousePos = null;
524 updateCursor();
525 updateStatusLine();
[10031]526 temporaryLayer.invalidate();
[4846]527 }
528
529 @Override
530 public void mouseExited(MouseEvent e) {
531 if (!isEnabled()) {
532 return;
533 }
534
535 if (!dragging) {
536 mousePos = null;
537 }
[10031]538 temporaryLayer.invalidate();
[4846]539 }
540
541 // -------------------------------------------------------------------------
542 // Custom methods
543 // -------------------------------------------------------------------------
544 /**
545 * Sets new cursor depending on state, mouse position
546 */
547 private void updateCursor() {
548 if (!isEnabled()) {
549 mv.setNewCursor(null, this);
550 return;
551 }
552
[10875]553 if (state == State.SELECTING) {
[4846]554 mv.setNewCursor(targetWay == null ? cursorSelect
555 : cursorSelectHover, this);
[10875]556 } else if (state == State.IMPROVING) {
[4846]557 if (alt && !ctrl) {
558 mv.setNewCursor(cursorImproveDelete, this);
559 } else if (shift || dragging) {
560 if (ctrl) {
561 mv.setNewCursor(cursorImproveAddLock, this);
562 } else {
563 mv.setNewCursor(cursorImproveLock, this);
564 }
565 } else if (ctrl && !alt) {
566 mv.setNewCursor(cursorImproveAdd, this);
567 } else {
568 mv.setNewCursor(cursorImprove, this);
569 }
570 }
571 }
572
573 /**
574 * Updates these objects under cursor: targetWay, candidateNode,
575 * candidateSegment
576 */
577 public void updateCursorDependentObjectsIfNeeded() {
[10875]578 if (state == State.IMPROVING && (shift || dragging)
[4846]579 && !(candidateNode == null && candidateSegment == null)) {
580 return;
581 }
582
583 if (mousePos == null) {
584 candidateNode = null;
585 candidateSegment = null;
586 return;
587 }
588
[10875]589 if (state == State.SELECTING) {
[4846]590 targetWay = ImproveWayAccuracyHelper.findWay(mv, mousePos);
[10875]591 } else if (state == State.IMPROVING) {
[4846]592 if (ctrl && !alt) {
593 candidateSegment = ImproveWayAccuracyHelper.findCandidateSegment(mv,
594 targetWay, mousePos);
595 candidateNode = null;
596 } else {
597 candidateNode = ImproveWayAccuracyHelper.findCandidateNode(mv,
598 targetWay, mousePos);
599 candidateSegment = null;
600 }
601 }
602 }
603
604 /**
605 * Switches to Selecting state
606 */
607 public void startSelecting() {
[10875]608 state = State.SELECTING;
[4846]609
610 targetWay = null;
611
[10031]612 temporaryLayer.invalidate();
[4846]613 updateStatusLine();
614 }
615
616 /**
617 * Switches to Improving state
618 *
619 * @param targetWay Way that is going to be improved
620 */
621 public void startImproving(Way targetWay) {
[10875]622 state = State.IMPROVING;
[4846]623
[10382]624 DataSet ds = getLayerManager().getEditDataSet();
625 Collection<OsmPrimitive> currentSelection = ds.getSelected();
[4846]626 if (currentSelection.size() != 1
[5364]627 || !currentSelection.iterator().next().equals(targetWay)) {
[4846]628 selectionChangedBlocked = true;
[10382]629 ds.clearSelection();
630 ds.setSelected(targetWay.getPrimitiveId());
[4846]631 selectionChangedBlocked = false;
632 }
633
634 this.targetWay = targetWay;
635 this.candidateNode = null;
636 this.candidateSegment = null;
637
[10031]638 temporaryLayer.invalidate();
[4846]639 updateStatusLine();
640 }
641
642 /**
643 * Updates the state according to the current selection. Goes to Improve
644 * state if a single way or node is selected. Extracts a way by a node in
645 * the second case.
646 */
647 private void updateStateByCurrentSelection() {
[7005]648 final List<Node> nodeList = new ArrayList<>();
649 final List<Way> wayList = new ArrayList<>();
[13964]650 final DataSet ds = getLayerManager().getEditDataSet();
651 if (ds != null) {
652 final Collection<OsmPrimitive> sel = ds.getSelected();
[4846]653
[13964]654 // Collecting nodes and ways from the selection
655 for (OsmPrimitive p : sel) {
656 if (p instanceof Way) {
657 wayList.add((Way) p);
658 }
659 if (p instanceof Node) {
660 nodeList.add((Node) p);
661 }
[4846]662 }
663
[13964]664 if (wayList.size() == 1) {
665 // Starting improving the single selected way
666 startImproving(wayList.get(0));
[8318]667 return;
[13964]668 } else if (nodeList.size() == 1) {
669 // Starting improving the only way of the single selected node
670 List<OsmPrimitive> r = nodeList.get(0).getReferrers();
671 if (r.size() == 1 && (r.get(0) instanceof Way)) {
672 startImproving((Way) r.get(0));
673 return;
674 }
[4846]675 }
676 }
677
678 // Starting selecting by default
679 startSelecting();
680 }
681}
Note: See TracBrowser for help on using the repository browser.