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 months ago

fix #16415 - NPE

  • Property svn:eol-style set to native
File size: 25.4 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.coor.EastNorth;
32import org.openstreetmap.josm.data.osm.DataSelectionListener;
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;
38import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
39import org.openstreetmap.josm.data.preferences.CachingProperty;
40import org.openstreetmap.josm.data.preferences.IntegerProperty;
41import org.openstreetmap.josm.data.preferences.NamedColorProperty;
42import org.openstreetmap.josm.data.preferences.StrokeProperty;
43import org.openstreetmap.josm.gui.MainApplication;
44import org.openstreetmap.josm.gui.MapFrame;
45import org.openstreetmap.josm.gui.MapView;
46import org.openstreetmap.josm.gui.draw.MapViewPath;
47import org.openstreetmap.josm.gui.draw.SymbolShape;
48import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
49import org.openstreetmap.josm.gui.layer.Layer;
50import org.openstreetmap.josm.gui.util.ModifierExListener;
51import org.openstreetmap.josm.tools.ImageProvider;
52import org.openstreetmap.josm.tools.Logging;
53import org.openstreetmap.josm.tools.Pair;
54import org.openstreetmap.josm.tools.Shortcut;
55
56/**
57 * A special map mode that is optimized for improving way geometry.
58 * (by efficiently moving, adding and deleting way-nodes)
59 *
60 * @author Alexander Kachkaev <alexander@kachkaev.ru>, 2011
61 */
62public class ImproveWayAccuracyAction extends MapMode implements DataSelectionListener, ModifierExListener {
63
64    private static final String CROSSHAIR = /* ICON(cursor/)*/ "crosshair";
65
66    enum State {
67        SELECTING, IMPROVING
68    }
69
70    private State state;
71
72    private MapView mv;
73
74    private static final long serialVersionUID = 42L;
75
76    private transient Way targetWay;
77    private transient Node candidateNode;
78    private transient WaySegment candidateSegment;
79
80    private Point mousePos;
81    private boolean dragging;
82
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");
85    private final Cursor cursorImprove = ImageProvider.getCursor(CROSSHAIR, null);
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");
90
91    private Color guideColor;
92
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
106    private boolean selectionChangedBlocked;
107
108    protected String oldModeHelpText;
109
110    private final transient AbstractMapViewPaintable temporaryLayer = new AbstractMapViewPaintable() {
111        @Override
112        public void paint(Graphics2D g, MapView mv, Bounds bbox) {
113            ImproveWayAccuracyAction.this.paint(g, mv, bbox);
114        }
115    };
116
117    /**
118     * Constructs a new {@code ImproveWayAccuracyAction}.
119     * @since 11713
120     */
121    public ImproveWayAccuracyAction() {
122        super(tr("Improve Way Accuracy"), "improvewayaccuracy",
123                tr("Improve Way Accuracy mode"),
124                Shortcut.registerShortcut("mapmode:ImproveWayAccuracy",
125                tr("Mode: {0}", tr("Improve Way Accuracy")),
126                KeyEvent.VK_W, Shortcut.DIRECT), Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
127
128        readPreferences();
129    }
130
131    // -------------------------------------------------------------------------
132    // Mode methods
133    // -------------------------------------------------------------------------
134    @Override
135    public void enterMode() {
136        if (!isEnabled()) {
137            return;
138        }
139        super.enterMode();
140        readPreferences();
141
142        MapFrame map = MainApplication.getMap();
143        mv = map.mapView;
144        mousePos = null;
145        oldModeHelpText = "";
146
147        if (getLayerManager().getEditDataSet() == null) {
148            return;
149        }
150
151        updateStateByCurrentSelection();
152
153        map.mapView.addMouseListener(this);
154        map.mapView.addMouseMotionListener(this);
155        map.mapView.addTemporaryLayer(temporaryLayer);
156        SelectionEventManager.getInstance().addSelectionListener(this);
157
158        map.keyDetector.addModifierExListener(this);
159    }
160
161    @Override
162    protected void readPreferences() {
163        guideColor = new NamedColorProperty(marktr("improve way accuracy helper line"), Color.RED).get();
164    }
165
166    @Override
167    public void exitMode() {
168        super.exitMode();
169
170        MapFrame map = MainApplication.getMap();
171        map.mapView.removeMouseListener(this);
172        map.mapView.removeMouseMotionListener(this);
173        map.mapView.removeTemporaryLayer(temporaryLayer);
174        SelectionEventManager.getInstance().removeSelectionListener(this);
175
176        map.keyDetector.removeModifierExListener(this);
177        temporaryLayer.invalidate();
178    }
179
180    @Override
181    protected void updateStatusLine() {
182        String newModeHelpText = getModeHelpText();
183        if (!newModeHelpText.equals(oldModeHelpText)) {
184            oldModeHelpText = newModeHelpText;
185            MapFrame map = MainApplication.getMap();
186            map.statusLine.setHelpText(newModeHelpText);
187            map.statusLine.repaint();
188        }
189    }
190
191    @Override
192    public String getModeHelpText() {
193        if (state == State.SELECTING) {
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) {
212        return isEditableDataLayer(l);
213    }
214
215    @Override
216    protected void updateEnabledState() {
217        setEnabled(getLayerManager().getEditLayer() != null);
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
226     * @param g The graphics
227     * @param mv The map view
228     * @param bbox The bounding box
229     */
230    public void paint(Graphics2D g, MapView mv, Bounds bbox) {
231        if (mousePos == null) {
232            return;
233        }
234
235        g.setColor(guideColor);
236
237        if (state == State.SELECTING && targetWay != null) {
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.
241            BasicStroke stroke = SELECT_TARGET_WAY_STROKE.get();
242            g.setStroke(stroke);
243
244            List<Node> nodes = targetWay.getNodes();
245
246            g.draw(new MapViewPath(mv).append(nodes, false).computeClippedLine(stroke));
247
248        } else if (state == State.IMPROVING) {
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
254            Node p1 = null;
255            Node p2 = null;
256            if (ctrl && candidateSegment != null) {
257                g.setStroke(ADD_NODE_STROKE.get());
258                try {
259                    p1 = candidateSegment.getFirstNode();
260                    p2 = candidateSegment.getSecondNode();
261                } catch (ArrayIndexOutOfBoundsException e) {
262                    Logging.error(e);
263                }
264            } else if (!alt && !ctrl && candidateNode != null) {
265                g.setStroke(MOVE_NODE_STROKE.get());
266                List<Pair<Node, Node>> wpps = targetWay.getNodePairs(false);
267                for (Pair<Node, Node> wpp : wpps) {
268                    if (wpp.a == candidateNode) {
269                        p1 = wpp.b;
270                    }
271                    if (wpp.b == candidateNode) {
272                        p2 = wpp.a;
273                    }
274                    if (p1 != null && p2 != null) {
275                        break;
276                    }
277                }
278            } else if (alt && !ctrl && candidateNode != null) {
279                g.setStroke(DELETE_NODE_STROKE.get());
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
284                if (index > 0 && index < (nodes.size() - 1)) {
285                    p1 = nodes.get(index - 1);
286                    p2 = nodes.get(index + 1);
287                } else if (targetWay.isClosed()) {
288                    p1 = targetWay.getNode(1);
289                    p2 = targetWay.getNode(nodes.size() - 2);
290                }
291                // TODO: indicate what part that will be deleted? (for end nodes)
292            }
293
294
295            // Drawing preview lines
296            MapViewPath b = new MapViewPath(mv);
297            if (alt && !ctrl) {
298                // In delete mode
299                if (p1 != null && p2 != null) {
300                    b.moveTo(p1);
301                    b.lineTo(p2);
302                }
303            } else {
304                // In add or move mode
305                if (p1 != null) {
306                    b.moveTo(mousePos.x, mousePos.y);
307                    b.lineTo(p1);
308                }
309                if (p2 != null) {
310                    b.moveTo(mousePos.x, mousePos.y);
311                    b.lineTo(p2);
312                }
313            }
314            g.draw(b.computeClippedLine(g.getStroke()));
315
316            // Highlighting candidateNode
317            if (candidateNode != null) {
318                p1 = candidateNode;
319                g.fill(new MapViewPath(mv).shapeAround(p1, SymbolShape.SQUARE, DOT_SIZE.get()));
320            }
321
322            if (!alt && !ctrl && candidateNode != null) {
323                b.reset();
324                drawIntersectingWayHelperLines(mv, b);
325                g.setStroke(MOVE_NODE_INTERSECTING_STROKE.get());
326                g.draw(b.computeClippedLine(g.getStroke()));
327            }
328
329        }
330    }
331
332    protected void drawIntersectingWayHelperLines(MapView mv, MapViewPath b) {
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);
344                    b.lineTo(nodes.get(i - 1));
345                }
346                if (i < nodes.size() - 1) {
347                    b.moveTo(mousePos.x, mousePos.y);
348                    b.lineTo(nodes.get(i + 1));
349                }
350            }
351        }
352    }
353
354    // -------------------------------------------------------------------------
355    // Event handlers
356    // -------------------------------------------------------------------------
357    @Override
358    public void modifiersExChanged(int modifiers) {
359        if (!MainApplication.isDisplayingMapView() || !MainApplication.getMap().mapView.isActiveLayerDrawable()) {
360            return;
361        }
362        updateKeyModifiersEx(modifiers);
363        updateCursorDependentObjectsIfNeeded();
364        updateCursor();
365        updateStatusLine();
366        temporaryLayer.invalidate();
367    }
368
369    @Override
370    public void selectionChanged(SelectionChangeEvent event) {
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();
395        temporaryLayer.invalidate();
396    }
397
398    @Override
399    public void mouseReleased(MouseEvent e) {
400        dragging = false;
401        if (!isEnabled() || e.getButton() != MouseEvent.BUTTON1) {
402            return;
403        }
404
405        DataSet ds = getLayerManager().getEditDataSet();
406        updateKeyModifiers(e);
407        mousePos = e.getPoint();
408
409        if (state == State.SELECTING) {
410            if (targetWay != null) {
411                ds.setSelected(targetWay.getPrimitiveId());
412                updateStateByCurrentSelection();
413            }
414        } else if (state == State.IMPROVING) {
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,
418                        tr("Cannot add a node outside of the world."),
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.
427                Collection<Command> virtualCmds = new LinkedList<>();
428
429                // Creating a new node
430                Node virtualNode = new Node(mv.getEastNorth(mousePos.x,
431                        mousePos.y));
432                virtualCmds.add(new AddCommand(ds, virtualNode));
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
444                Collection<WaySegment> virtualSegments = new LinkedList<>();
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;
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) {
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
476                String text = trn("Add a new node to way",
477                        "Add a new node to {0} ways",
478                        virtualSegments.size(), virtualSegments.size());
479
480                MainApplication.undoRedo.add(new SequenceCommand(text, virtualCmds));
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) {
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);
494                    if (nodes.size() < 2) {
495                        final Command deleteCmd = DeleteCommand.delete(Collections.singleton(targetWay), true);
496                        if (deleteCmd != null) {
497                            MainApplication.undoRedo.add(deleteCmd);
498                        }
499                    } else {
500                        MainApplication.undoRedo.add(new ChangeCommand(targetWay, newWay));
501                    }
502                } else if (candidateNode.isTagged()) {
503                    JOptionPane.showMessageDialog(Main.parent,
504                            tr("Cannot delete node that has tags"),
505                            tr("Error"), JOptionPane.ERROR_MESSAGE);
506                } else {
507                    final Command deleteCmd = DeleteCommand.delete(Collections.singleton(candidateNode), true);
508                    if (deleteCmd != null) {
509                        MainApplication.undoRedo.add(deleteCmd);
510                    }
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
519                MainApplication.undoRedo.add(new MoveCommand(candidateNode, cursorEN.east() - nodeEN.east(), cursorEN.north() - nodeEN.north()));
520            }
521        }
522
523        mousePos = null;
524        updateCursor();
525        updateStatusLine();
526        temporaryLayer.invalidate();
527    }
528
529    @Override
530    public void mouseExited(MouseEvent e) {
531        if (!isEnabled()) {
532            return;
533        }
534
535        if (!dragging) {
536            mousePos = null;
537        }
538        temporaryLayer.invalidate();
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
553        if (state == State.SELECTING) {
554            mv.setNewCursor(targetWay == null ? cursorSelect
555                    : cursorSelectHover, this);
556        } else if (state == State.IMPROVING) {
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() {
578        if (state == State.IMPROVING && (shift || dragging)
579                && !(candidateNode == null && candidateSegment == null)) {
580            return;
581        }
582
583        if (mousePos == null) {
584            candidateNode = null;
585            candidateSegment = null;
586            return;
587        }
588
589        if (state == State.SELECTING) {
590            targetWay = ImproveWayAccuracyHelper.findWay(mv, mousePos);
591        } else if (state == State.IMPROVING) {
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() {
608        state = State.SELECTING;
609
610        targetWay = null;
611
612        temporaryLayer.invalidate();
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) {
622        state = State.IMPROVING;
623
624        DataSet ds = getLayerManager().getEditDataSet();
625        Collection<OsmPrimitive> currentSelection = ds.getSelected();
626        if (currentSelection.size() != 1
627                || !currentSelection.iterator().next().equals(targetWay)) {
628            selectionChangedBlocked = true;
629            ds.clearSelection();
630            ds.setSelected(targetWay.getPrimitiveId());
631            selectionChangedBlocked = false;
632        }
633
634        this.targetWay = targetWay;
635        this.candidateNode = null;
636        this.candidateSegment = null;
637
638        temporaryLayer.invalidate();
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() {
648        final List<Node> nodeList = new ArrayList<>();
649        final List<Way> wayList = new ArrayList<>();
650        final DataSet ds = getLayerManager().getEditDataSet();
651        if (ds != null) {
652            final Collection<OsmPrimitive> sel = ds.getSelected();
653
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                }
662            }
663
664            if (wayList.size() == 1) {
665                // Starting improving the single selected way
666                startImproving(wayList.get(0));
667                return;
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                }
675            }
676        }
677
678        // Starting selecting by default
679        startSelecting();
680    }
681}
Note: See TracBrowser for help on using the repository browser.