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

Last change on this file since 14134 was 14134, checked in by Don-vip, 4 months ago

see #15229 - deprecate Main*.undoRedo - make UndoRedoHandler a singleton

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