// License: GPL. For details, see LICENSE file. package CommandLine; import static org.openstreetmap.josm.tools.I18n.marktr; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.AWTEvent; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; import java.awt.EventQueue; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.geom.GeneralPath; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.mapmode.MapMode; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.preferences.ColorProperty; import org.openstreetmap.josm.gui.MapView; import org.openstreetmap.josm.gui.layer.Layer; import org.openstreetmap.josm.gui.layer.MapViewPaintable; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.tools.ImageProvider; public class LengthAction extends MapMode implements MapViewPaintable, AWTEventListener { private final CommandLine parentPlugin; private final Cursor cursorCrosshair; private final Cursor cursorJoinNode; private Cursor currentCursor; private final Color selectedColor; private Point drawStartPos; private Point drawEndPos; private LatLon startCoor; private LatLon endCoor; private Point mousePos; private Node nearestNode; private boolean drawing; public LengthAction(CommandLine parentPlugin) { super(null, "addsegment.png", null, ImageProvider.getCursor("crosshair", null)); this.parentPlugin = parentPlugin; selectedColor = new ColorProperty(marktr("selected"), Color.red).get(); cursorCrosshair = ImageProvider.getCursor("crosshair", null); cursorJoinNode = ImageProvider.getCursor("crosshair", "joinnode"); currentCursor = cursorCrosshair; nearestNode = null; } @Override public void enterMode() { super.enterMode(); Main.map.mapView.addMouseListener(this); Main.map.mapView.addMouseMotionListener(this); Main.map.mapView.addTemporaryLayer(this); try { Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK); } catch (SecurityException ex) { Main.warn(ex); } } @Override public void exitMode() { super.exitMode(); Main.map.mapView.removeMouseListener(this); Main.map.mapView.removeMouseMotionListener(this); Main.map.mapView.removeTemporaryLayer(this); try { Toolkit.getDefaultToolkit().removeAWTEventListener(this); } catch (SecurityException ex) { Main.warn(ex); } if (drawing) Main.map.mapView.repaint(); } public void cancelDrawing() { if (Main.map == null || Main.map.mapView == null) return; Main.map.statusLine.setHeading(-1); Main.map.statusLine.setAngle(-1); updateStatusLine(); parentPlugin.abortInput(); } @Override public void eventDispatched(AWTEvent arg0) { if (!(arg0 instanceof KeyEvent)) return; KeyEvent ev = (KeyEvent) arg0; if (ev.getKeyCode() == KeyEvent.VK_ESCAPE && ev.getID() == KeyEvent.KEY_PRESSED) { if (drawing) ev.consume(); cancelDrawing(); } } private void processMouseEvent(MouseEvent e) { if (e != null) { mousePos = e.getPoint(); } } @Override public void paint(Graphics2D g, MapView mv, Bounds bbox) { if (!drawing) return; g.setColor(selectedColor); g.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); GeneralPath b = new GeneralPath(); Point pp1 = drawStartPos; Point pp2 = drawEndPos; b.moveTo(pp1.x, pp1.y); b.lineTo(pp2.x, pp2.y); g.draw(b); g.setStroke(new BasicStroke(1)); } private void drawingStart(MouseEvent e) { mousePos = e.getPoint(); if (nearestNode != null) { drawStartPos = Main.map.mapView.getPoint(nearestNode.getCoor()); } else { drawStartPos = mousePos; } drawEndPos = drawStartPos; startCoor = Main.map.mapView.getLatLon(drawStartPos.x, drawStartPos.y); endCoor = startCoor; drawing = true; updateStatusLine(); } private void drawingFinish() { parentPlugin.loadParameter(String.valueOf(startCoor.greatCircleDistance(endCoor)), true); drawStartPos = null; drawing = false; exitMode(); } @Override public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { if (!Main.map.mapView.isActiveLayerDrawable()) return; requestFocusInMapView(); drawingStart(e); } else drawing = false; } @Override public void mouseReleased(MouseEvent e) { if (e.getButton() != MouseEvent.BUTTON1) return; if (!Main.map.mapView.isActiveLayerDrawable()) return; boolean dragged = true; if (drawStartPos != null) dragged = drawEndPos.distance(drawStartPos) > 10; if (drawing && dragged) drawingFinish(); drawing = false; } @Override public void mouseDragged(MouseEvent e) { processMouseEvent(e); updCursor(); if (nearestNode != null) drawEndPos = Main.map.mapView.getPoint(nearestNode.getCoor()); else drawEndPos = mousePos; endCoor = Main.map.mapView.getLatLon(drawEndPos.x, drawEndPos.y); if (drawing) { Main.map.statusLine.setDist(startCoor.greatCircleDistance(endCoor)); Main.map.mapView.repaint(); } } @Override public void mouseMoved(MouseEvent e) { if (!Main.map.mapView.isActiveLayerDrawable()) return; processMouseEvent(e); updCursor(); if (drawing) Main.map.mapView.repaint(); } @Override public String getModeHelpText() { if (drawing) return tr("Point on the start"); else return tr("Point on the end"); } @Override public boolean layerIsSupported(Layer l) { return l instanceof OsmDataLayer; } private void updCursor() { if (mousePos != null) { if (!Main.isDisplayingMapView()) return; nearestNode = Main.map.mapView.getNearestNode(mousePos, OsmPrimitive::isUsable); if (nearestNode != null) { setCursor(cursorJoinNode); } else { setCursor(cursorCrosshair); } } } private void setCursor(final Cursor c) { if (currentCursor.equals(c)) return; try { // We invoke this to prevent strange things from happening EventQueue.invokeLater(new Runnable() { @Override public void run() { // Don't change cursor when mode has changed already if (!(Main.map.mapMode instanceof LengthAction)) return; Main.map.mapView.setCursor(c); } }); currentCursor = c; } catch (Exception e) { Main.warn(e); } } }