source: subversion/applications/editors/josm/plugins/splinex/src/org/openstreetmap/josm/plugins/Splinex/DrawSplineAction.java

Last change on this file was 34557, checked in by donvip, 8 months ago

update to JOSM 14153

File size: 17.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.plugins.Splinex;
3
4import static org.openstreetmap.josm.plugins.Splinex.SplinexPlugin.EPSILON;
5import static org.openstreetmap.josm.tools.I18n.marktr;
6import static org.openstreetmap.josm.tools.I18n.tr;
7
8import java.awt.Color;
9import java.awt.Cursor;
10import java.awt.Graphics2D;
11import java.awt.Point;
12import java.awt.event.ActionEvent;
13import java.awt.event.KeyEvent;
14import java.awt.event.MouseEvent;
15import java.util.Collection;
16import java.util.HashMap;
17import java.util.Map;
18
19import javax.swing.AbstractAction;
20
21import org.openstreetmap.josm.actions.mapmode.MapMode;
22import org.openstreetmap.josm.command.Command;
23import org.openstreetmap.josm.command.MoveCommand;
24import org.openstreetmap.josm.data.Bounds;
25import org.openstreetmap.josm.data.UndoRedoHandler;
26import org.openstreetmap.josm.data.coor.EastNorth;
27import org.openstreetmap.josm.data.osm.Node;
28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
30import org.openstreetmap.josm.data.preferences.NamedColorProperty;
31import org.openstreetmap.josm.data.projection.ProjectionRegistry;
32import org.openstreetmap.josm.gui.MainApplication;
33import org.openstreetmap.josm.gui.MapFrame;
34import org.openstreetmap.josm.gui.MapView;
35import org.openstreetmap.josm.gui.layer.Layer;
36import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
37import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
38import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
39import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
40import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
41import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
42import org.openstreetmap.josm.gui.layer.MapViewPaintable;
43import org.openstreetmap.josm.gui.layer.OsmDataLayer;
44import org.openstreetmap.josm.gui.util.KeyPressReleaseListener;
45import org.openstreetmap.josm.gui.util.ModifierExListener;
46import org.openstreetmap.josm.plugins.Splinex.Spline.PointHandle;
47import org.openstreetmap.josm.plugins.Splinex.Spline.SplinePoint;
48import org.openstreetmap.josm.spi.preferences.Config;
49import org.openstreetmap.josm.tools.ImageProvider;
50import org.openstreetmap.josm.tools.Logging;
51import org.openstreetmap.josm.tools.Shortcut;
52
53@SuppressWarnings("serial")
54public class DrawSplineAction extends MapMode implements MapViewPaintable, KeyPressReleaseListener, ModifierExListener,
55        LayerChangeListener, ActiveLayerChangeListener {
56    private final Cursor cursorJoinNode;
57    private final Cursor cursorJoinWay;
58
59    private Color rubberLineColor;
60
61    private final Shortcut backspaceShortcut;
62    private final BackSpaceAction backspaceAction;
63
64    boolean drawHelperLine;
65
66    public DrawSplineAction(MapFrame mapFrame) {
67        super(tr("Spline drawing"), // name
68                "spline2", // icon name
69                tr("Draw a spline curve"), // tooltip
70                getCursor());
71
72        backspaceShortcut = Shortcut.registerShortcut("mapmode:backspace", tr("Backspace in Add mode"),
73                KeyEvent.VK_BACK_SPACE, Shortcut.DIRECT);
74        backspaceAction = new BackSpaceAction();
75        cursorJoinNode = ImageProvider.getCursor("crosshair", "joinnode");
76        cursorJoinWay = ImageProvider.getCursor("crosshair", "joinway");
77        MainApplication.getLayerManager().addLayerChangeListener(this);
78        MainApplication.getLayerManager().addActiveLayerChangeListener(this);
79        readPreferences();
80    }
81
82    private static Cursor getCursor() {
83        try {
84            return ImageProvider.getCursor("crosshair", "spline");
85        } catch (Exception e) {
86            Logging.error(e);
87        }
88        return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
89    }
90
91    @Override
92    public void enterMode() {
93        if (!isEnabled())
94            return;
95        super.enterMode();
96
97        MainApplication.registerActionShortcut(backspaceAction, backspaceShortcut);
98
99        MapFrame map = MainApplication.getMap();
100        map.mapView.addMouseListener(this);
101        map.mapView.addMouseMotionListener(this);
102        map.mapView.addTemporaryLayer(this);
103
104        map.keyDetector.addModifierExListener(this);
105        map.keyDetector.addKeyListener(this);
106    }
107
108    int initialMoveDelay, initialMoveThreshold;
109
110    @Override
111    protected void readPreferences() {
112        rubberLineColor = new NamedColorProperty(marktr("helper line"), Color.RED).get();
113        initialMoveDelay = Config.getPref().getInt("edit.initial-move-", 200);
114        initialMoveThreshold = Config.getPref().getInt("edit.initial-move-threshold", 5);
115        initialMoveThreshold *= initialMoveThreshold;
116        drawHelperLine = Config.getPref().getBoolean("draw.helper-line", true);
117    }
118
119    @Override
120    public void exitMode() {
121        super.exitMode();
122        MapFrame map = MainApplication.getMap();
123        map.mapView.removeMouseListener(this);
124        map.mapView.removeMouseMotionListener(this);
125        map.mapView.removeTemporaryLayer(this);
126        MainApplication.unregisterActionShortcut(backspaceAction, backspaceShortcut);
127
128        map.statusLine.activateAnglePanel(false);
129        map.keyDetector.removeModifierExListener(this);
130        map.keyDetector.removeKeyListener(this);
131        removeHighlighting();
132        map.mapView.repaint();
133    }
134
135    @Override
136    public void modifiersExChanged(int modifiers) {
137        updateKeyModifiersEx(modifiers);
138    }
139
140    private Long mouseDownTime;
141    private PointHandle ph;
142    private Point helperEndpoint;
143    private Point clickPos;
144    public int index = 0;
145    boolean lockCounterpart;
146    private MoveCommand mc;
147    private boolean dragControl;
148    private boolean dragSpline;
149
150    @Override
151    public void mousePressed(MouseEvent e) {
152        mouseDownTime = null;
153        updateKeyModifiers(e);
154        if (e.getButton() != MouseEvent.BUTTON1) {
155            helperEndpoint = null; // Hide helper line when panning
156            return;
157        }
158        if (!MainApplication.getMap().mapView.isActiveLayerDrawable()) return;
159        Spline spl = getSpline();
160        if (spl == null) return;
161        helperEndpoint = null;
162        dragControl = false;
163        dragSpline = false;
164        mouseDownTime = System.currentTimeMillis();
165        ph = spl.getNearestPoint(MainApplication.getMap().mapView, e.getPoint());
166        if (e.getClickCount() == 2) {
167            if (!spl.isClosed() && spl.nodeCount() > 1 && ph != null && ph.point == SplinePoint.ENDPOINT
168                    && ((ph.idx == 0 && direction == 1) || (ph.idx == spl.nodeCount() - 1 && direction == -1))) {
169                UndoRedoHandler.getInstance().add(spl.new CloseSplineCommand());
170                return;
171            }
172            spl.finishSpline();
173            MainApplication.getMap().repaint();
174            return;
175        }
176        clickPos = e.getPoint();
177        if (ph != null) {
178            if (ctrl) {
179                if (ph.point == SplinePoint.ENDPOINT) {
180                    ph = ph.otherPoint(SplinePoint.CONTROL_NEXT);
181                    lockCounterpart = true;
182                } else
183                    lockCounterpart = false;
184            } else {
185                lockCounterpart = (ph.point != SplinePoint.ENDPOINT
186                        && Math.abs(ph.sn.cprev.east() + ph.sn.cnext.east()) < EPSILON && Math.abs(ph.sn.cprev.north()
187                        + ph.sn.cnext.north()) < EPSILON);
188            }
189            if (ph.point == SplinePoint.ENDPOINT && !UndoRedoHandler.getInstance().commands.isEmpty()) {
190                Command cmd = UndoRedoHandler.getInstance().commands.getLast();
191                if (cmd instanceof MoveCommand) {
192                    mc = (MoveCommand) cmd;
193                    Collection<Node> pp = mc.getParticipatingPrimitives();
194                    if (pp.size() != 1 || !pp.contains(ph.sn.node))
195                        mc = null;
196                    else
197                        mc.changeStartPoint(ph.sn.node.getEastNorth());
198                }
199            }
200            if (ph.point != SplinePoint.ENDPOINT && !UndoRedoHandler.getInstance().commands.isEmpty()) {
201                Command cmd = UndoRedoHandler.getInstance().commands.getLast();
202                if (!(cmd instanceof Spline.EditSplineCommand && ((Spline.EditSplineCommand) cmd).sn == ph.sn))
203                    dragControl = true;
204            }
205            return;
206        }
207        if (!ctrl && spl.doesHit(e.getX(), e.getY(), MainApplication.getMap().mapView)) {
208            dragSpline = true;
209            return;
210        }
211        if (spl.isClosed()) return;
212        if (direction == 0)
213            if (spl.nodeCount() < 2)
214                direction = 1;
215            else
216                return;
217        Node n = null;
218        boolean existing = false;
219        if (!ctrl) {
220            n = MainApplication.getMap().mapView.getNearestNode(e.getPoint(), OsmPrimitive::isUsable);
221            existing = true;
222        }
223        if (n == null) {
224            n = new Node(MainApplication.getMap().mapView.getLatLon(e.getX(), e.getY()));
225            existing = false;
226        }
227        int idx = direction == -1 ? 0 : spl.nodeCount();
228        UndoRedoHandler.getInstance().add(spl.new AddSplineNodeCommand(new Spline.SNode(n), existing, idx));
229        ph = spl.new PointHandle(idx, direction == -1 ? SplinePoint.CONTROL_PREV : SplinePoint.CONTROL_NEXT);
230        lockCounterpart = true;
231        MainApplication.getMap().repaint();
232    }
233
234    @Override
235    public void mouseReleased(MouseEvent e) {
236        mc = null;
237        mouseDownTime = null;
238        dragSpline = false;
239        clickPos = null;
240        mouseMoved(e);
241        if (direction == 0 && ph != null) {
242            if (ph.idx >= ph.getSpline().nodeCount() - 1)
243                direction = 1;
244            else if (ph.idx == 0)
245                direction = -1;
246        }
247    }
248
249    @Override
250    public void mouseDragged(MouseEvent e) {
251        updateKeyModifiers(e);
252        if (mouseDownTime == null) return;
253        if (!MainApplication.getMap().mapView.isActiveLayerDrawable()) return;
254        if (System.currentTimeMillis() - mouseDownTime < initialMoveDelay) return;
255        Spline spl = getSpline();
256        if (spl == null) return;
257        if (spl.isEmpty()) return;
258        if (clickPos != null && clickPos.distanceSq(e.getPoint()) < initialMoveThreshold)
259            return;
260        EastNorth en = MainApplication.getMap().mapView.getEastNorth(e.getX(), e.getY());
261        if (ProjectionRegistry.getProjection().eastNorth2latlon(en).isOutSideWorld())
262            return;
263        if (dragSpline) {
264            if (mc == null) {
265                mc = new MoveCommand(spl.getNodes(), MainApplication.getMap().mapView.getEastNorth(clickPos.x, clickPos.y), en);
266                UndoRedoHandler.getInstance().add(mc);
267                clickPos = null;
268            } else
269                mc.applyVectorTo(en);
270            MainApplication.getMap().repaint();
271            return;
272        }
273        clickPos = null;
274        if (ph == null) return;
275        if (ph.point == SplinePoint.ENDPOINT) {
276            if (mc == null) {
277                mc = new MoveCommand(ph.sn.node, ph.sn.node.getEastNorth(), en);
278                UndoRedoHandler.getInstance().add(mc);
279            } else
280                mc.applyVectorTo(en);
281        } else {
282            if (dragControl) {
283                UndoRedoHandler.getInstance().add(new Spline.EditSplineCommand(ph.sn));
284                dragControl = false;
285            }
286            ph.movePoint(en);
287            if (lockCounterpart) {
288                if (ph.point == SplinePoint.CONTROL_NEXT)
289                    ph.sn.cprev = new EastNorth(0, 0).subtract(ph.sn.cnext);
290                else if (ph.point == SplinePoint.CONTROL_PREV)
291                    ph.sn.cnext = new EastNorth(0, 0).subtract(ph.sn.cprev);
292            }
293        }
294        MainApplication.getMap().repaint();
295    }
296
297    Node nodeHighlight;
298    short direction;
299
300    @Override
301    public void mouseMoved(MouseEvent e) {
302        updateKeyModifiers(e);
303        if (!MainApplication.getMap().mapView.isActiveLayerDrawable()) return;
304        Spline spl = getSpline();
305        if (spl == null) return;
306        Point oldHelperEndpoint = helperEndpoint;
307        PointHandle oldph = ph;
308        boolean redraw = false;
309        ph = spl.getNearestPoint(MainApplication.getMap().mapView, e.getPoint());
310        if (ph == null)
311            if (!ctrl && spl.doesHit(e.getX(), e.getY(), MainApplication.getMap().mapView)) {
312                helperEndpoint = null;
313                MainApplication.getMap().mapView.setNewCursor(Cursor.MOVE_CURSOR, this);
314            } else {
315                Node n = null;
316                if (!ctrl)
317                    n = MainApplication.getMap().mapView.getNearestNode(e.getPoint(), OsmPrimitive::isUsable);
318                if (n == null) {
319                    redraw = removeHighlighting();
320                    helperEndpoint = e.getPoint();
321                    MainApplication.getMap().mapView.setNewCursor(cursor, this);
322                } else {
323                    redraw = setHighlight(n);
324                    MainApplication.getMap().mapView.setNewCursor(cursorJoinNode, this);
325                    helperEndpoint = MainApplication.getMap().mapView.getPoint(n);
326                }
327            }
328        else {
329            helperEndpoint = null;
330            MainApplication.getMap().mapView.setNewCursor(cursorJoinWay, this);
331            if (ph.point == SplinePoint.ENDPOINT)
332                redraw = setHighlight(ph.sn.node);
333            else
334                redraw = removeHighlighting();
335        }
336        if (!drawHelperLine || spl.isClosed() || direction == 0)
337            helperEndpoint = null;
338
339        if (redraw || oldHelperEndpoint != helperEndpoint || (oldph == null && ph != null)
340                || (oldph != null && !oldph.equals(ph)))
341            MainApplication.getMap().repaint();
342    }
343
344    /**
345     * Repaint on mouse exit so that the helper line goes away.
346     */
347    @Override
348    public void mouseExited(MouseEvent e) {
349        if (!MainApplication.getMap().mapView.isActiveLayerDrawable())
350            return;
351        removeHighlighting();
352        helperEndpoint = null;
353        MainApplication.getMap().mapView.repaint();
354    }
355
356    private boolean setHighlight(Node n) {
357        if (nodeHighlight == n)
358            return false;
359        removeHighlighting();
360        nodeHighlight = n;
361        n.setHighlighted(true);
362        return true;
363    }
364
365    /**
366     * Removes target highlighting from primitives. Issues repaint if required.
367     * Returns true if a repaint has been issued.
368     */
369    private boolean removeHighlighting() {
370        if (nodeHighlight != null) {
371            nodeHighlight.setHighlighted(false);
372            nodeHighlight = null;
373            return true;
374        }
375        return false;
376    }
377
378    @Override
379    public void paint(Graphics2D g, MapView mv, Bounds box) {
380        Spline spl = getSpline();
381        if (spl == null)
382            return;
383        spl.paint(g, mv, rubberLineColor, Color.green, helperEndpoint, direction);
384        if (ph != null && (ph.point != SplinePoint.ENDPOINT || (nodeHighlight != null && nodeHighlight.isDeleted()))) {
385            g.setColor(MapPaintSettings.INSTANCE.getSelectedColor());
386            Point p = mv.getPoint(ph.getPoint());
387            g.fillRect(p.x - 1, p.y - 1, 3, 3);
388        }
389    }
390
391    @Override
392    public boolean layerIsSupported(Layer l) {
393        return l instanceof OsmDataLayer;
394    }
395
396    @Override
397    protected void updateEnabledState() {
398        setEnabled(getLayerManager().getEditLayer() != null);
399    }
400
401    public static class BackSpaceAction extends AbstractAction {
402        @Override
403        public void actionPerformed(ActionEvent e) {
404            UndoRedoHandler.getInstance().undo();
405        }
406    }
407
408    private Spline splCached;
409
410    Spline getSpline() {
411        if (splCached != null)
412            return splCached;
413        Layer l = getLayerManager().getEditLayer();
414        if (!(l instanceof OsmDataLayer))
415            return null;
416        splCached = layerSplines.get(l);
417        if (splCached == null)
418            splCached = new Spline();
419        layerSplines.put(l, splCached);
420        return splCached;
421    }
422
423    @Override
424    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
425        splCached = layerSplines.get(MainApplication.getLayerManager().getActiveLayer());
426    }
427
428    Map<Layer, Spline> layerSplines = new HashMap<>();
429
430    @Override
431    public void layerOrderChanged(LayerOrderChangeEvent e) {
432        // Do nothing
433    }
434
435    @Override
436    public void layerAdded(LayerAddEvent e) {
437        // Do nothing
438    }
439
440    @Override
441    public void layerRemoving(LayerRemoveEvent e) {
442        layerSplines.remove(e.getRemovedLayer());
443        splCached = null;
444    }
445
446    @Override
447    public void doKeyPressed(KeyEvent e) {
448        if (e.getKeyCode() == KeyEvent.VK_DELETE && ph != null) {
449            Spline spl = ph.getSpline();
450            if (spl.nodeCount() == 3 && spl.isClosed() && ph.idx == 1)
451                return; // Don't allow to delete node when it results with two-node closed spline
452            UndoRedoHandler.getInstance().add(spl.new DeleteSplineNodeCommand(ph.idx));
453            e.consume();
454        }
455        if (e.getKeyCode() == KeyEvent.VK_ESCAPE && direction != 0) {
456            direction = 0;
457            MainApplication.getMap().mapView.repaint();
458            e.consume();
459        }
460    }
461
462    @Override
463    public void doKeyReleased(KeyEvent e) {
464        // Do nothing
465    }
466}
Note: See TracBrowser for help on using the repository browser.