package maps.gml.editor; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.Color; import java.awt.Point; import java.awt.Insets; import javax.swing.undo.UndoableEdit; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.HashSet; import maps.gml.view.EdgeDecorator; import maps.gml.view.LineEdgeDecorator; import maps.gml.GMLNode; import maps.gml.GMLEdge; import maps.gml.GMLCoordinates; /** A tool for creating shapes. */ public abstract class CreateShapeTool extends AbstractTool { private static final Color HOVER_COLOUR = Color.BLUE; private static final Color SELECTED_COLOUR = Color.GREEN; private static final Color POSSIBLE_COLOUR = Color.WHITE; private Listener listener; private EdgeDecorator hoverHighlight; private EdgeDecorator selectedHighlight; private EdgeDecorator possibleHighlight; private List<GMLEdge> edges; private List<GMLNode> nodes; private Set<GMLEdge> possible; private GMLNode startNode; private GMLNode currentNode; private GMLEdge hover; /** Construct a CreateShapeTool. @param editor The editor instance. */ protected CreateShapeTool(GMLEditor editor) { super(editor); listener = new Listener(); hoverHighlight = new LineEdgeDecorator(HOVER_COLOUR); selectedHighlight = new LineEdgeDecorator(SELECTED_COLOUR); possibleHighlight = new LineEdgeDecorator(POSSIBLE_COLOUR); edges = new ArrayList<GMLEdge>(); nodes = new ArrayList<GMLNode>(); possible = new HashSet<GMLEdge>(); } @Override public void activate() { editor.getViewer().addMouseListener(listener); editor.getViewer().addMouseMotionListener(listener); clearData(); } @Override public void deactivate() { editor.getViewer().removeMouseListener(listener); editor.getViewer().removeMouseMotionListener(listener); editor.getViewer().clearAllEdgeDecorators(); editor.getViewer().repaint(); clearData(); } /** Perform whatever shape creation tasks are needed once the shape has been closed. @param shapeNodes The nodes of the shape. @return An UndoableEdit for the change. */ protected abstract UndoableEdit finished(List<GMLNode> shapeNodes); private void addEdge(GMLEdge edge) { editor.getViewer().clearEdgeDecorator(possible); edges.add(edge); possible.clear(); editor.getViewer().setEdgeDecorator(selectedHighlight, edge); editor.getViewer().repaint(); if (edges.size() == 1) { startNode = edge.getStart(); currentNode = edge.getEnd(); possible.addAll(editor.getMap().getAttachedEdges(startNode)); possible.addAll(editor.getMap().getAttachedEdges(currentNode)); } else if (edges.size() == 2) { // Find the shared node GMLEdge first = edges.get(0); GMLEdge second = edges.get(1); GMLNode shared; if (first.getStart().equals(second.getStart()) || first.getStart().equals(second.getEnd())) { startNode = first.getEnd(); shared = first.getStart(); } else { startNode = first.getStart(); shared = first.getEnd(); } currentNode = shared.equals(second.getStart()) ? second.getEnd() : second.getStart(); nodes.add(startNode); nodes.add(shared); nodes.add(currentNode); possible.addAll(editor.getMap().getAttachedEdges(currentNode)); } else if (edges.size() > 2) { // Update end node currentNode = currentNode.equals(edge.getStart()) ? edge.getEnd() : edge.getStart(); if (currentNode.equals(startNode)) { // We're done editor.addEdit(finished(nodes)); editor.setChanged(); clearData(); editor.getViewer().clearAllEdgeDecorators(); editor.getViewer().repaint(); } else { nodes.add(currentNode); possible.addAll(editor.getMap().getAttachedEdges(currentNode)); } } possible.removeAll(edges); editor.getViewer().setEdgeDecorator(possibleHighlight, possible); if (possible.size() == 1) { addEdge(possible.iterator().next()); } editor.getViewer().repaint(); } private void clearData() { nodes.clear(); edges.clear(); possible.clear(); startNode = null; currentNode = null; hover = null; } private void hover(GMLEdge edge) { if (hover == edge) { return; } if (hover != null) { editor.getViewer().clearEdgeDecorator(hover); if (possible.contains(hover)) { editor.getViewer().setEdgeDecorator(possibleHighlight, hover); } } hover = edge; if (hover != null) { editor.getViewer().setEdgeDecorator(hoverHighlight, hover); } editor.getViewer().repaint(); } private class Listener implements MouseListener, MouseMotionListener { @Override public void mousePressed(MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseDragged(MouseEvent e) { } @Override public void mouseMoved(MouseEvent e) { Point p = fixEventPoint(e.getPoint()); GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y)); if (edges.isEmpty()) { hover(editor.getMap().findNearestEdge(c.getX(), c.getY())); } else { hover(editor.getMap().findNearestEdge(c.getX(), c.getY(), possible)); } } @Override public void mouseClicked(MouseEvent e) { if (hover != null && e.getButton() == MouseEvent.BUTTON1) { GMLEdge edge = hover; hover(null); addEdge(edge); } } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } private Point fixEventPoint(Point p) { Insets insets = editor.getViewer().getInsets(); return new Point(p.x - insets.left, p.y - insets.top); } } }