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 java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import maps.gml.view.NodeDecorator;
import maps.gml.view.SquareNodeDecorator;
import maps.gml.view.EdgeDecorator;
import maps.gml.view.LineEdgeDecorator;
import maps.gml.view.RectangleOverlay;
import maps.gml.GMLNode;
import maps.gml.GMLEdge;
import maps.gml.GMLCoordinates;
import maps.gml.GMLObject;
import javax.swing.undo.AbstractUndoableEdit;
/**
A tool for deleting nodes.
*/
public class DeleteNodeTool extends AbstractTool {
private static final Color HIGHLIGHT_COLOUR = Color.BLUE;
private static final int HIGHLIGHT_SIZE = 6;
private static final Color OVERLAY_COLOUR = new Color(0, 0, 128, 128);
private Listener listener;
private NodeDecorator nodeHighlight;
private EdgeDecorator edgeHighlight;
private GMLNode selected;
private Collection<GMLEdge> attachedEdges;
private GMLCoordinates pressPoint;
private GMLCoordinates dragPoint;
private RectangleOverlay overlay;
/**
Construct a DeleteNodeTool.
@param editor The editor instance.
*/
public DeleteNodeTool(GMLEditor editor) {
super(editor);
listener = new Listener();
nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE);
edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR);
selected = null;
attachedEdges = new HashSet<GMLEdge>();
overlay = new RectangleOverlay(OVERLAY_COLOUR, true);
}
@Override
public String getName() {
return "Delete node";
}
@Override
public void activate() {
editor.getViewer().addMouseListener(listener);
editor.getViewer().addMouseMotionListener(listener);
selected = null;
attachedEdges.clear();
}
@Override
public void deactivate() {
editor.getViewer().removeMouseListener(listener);
editor.getViewer().removeMouseMotionListener(listener);
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().clearAllEdgeDecorators();
editor.getViewer().removeOverlay(overlay);
editor.getViewer().repaint();
}
private void highlightNode(GMLNode node) {
if (selected == node) {
return;
}
if (selected != null) {
editor.getViewer().clearNodeDecorator(selected);
editor.getViewer().clearEdgeDecorator(attachedEdges);
}
selected = node;
attachedEdges.clear();
if (selected != null) {
attachedEdges.addAll(editor.getMap().getAttachedEdges(selected));
editor.getViewer().setNodeDecorator(nodeHighlight, selected);
editor.getViewer().setEdgeDecorator(edgeHighlight, attachedEdges);
}
editor.getViewer().repaint();
}
private void removeNodes() {
double xMin = Math.min(pressPoint.getX(), dragPoint.getX());
double xMax = Math.max(pressPoint.getX(), dragPoint.getX());
double yMin = Math.min(pressPoint.getY(), dragPoint.getY());
double yMax = Math.max(pressPoint.getY(), dragPoint.getY());
Collection<GMLNode> nodes = editor.getMap().getNodesInRegion(xMin, yMin, xMax, yMax);
Map<GMLNode, Collection<GMLObject>> deleted = new HashMap<GMLNode, Collection<GMLObject>>();
for (GMLNode next : nodes) {
deleted.put(next, editor.getMap().removeNode(next));
}
editor.getViewer().repaint();
editor.setChanged();
editor.addEdit(new DeleteNodesEdit(nodes, deleted));
}
private class Listener implements MouseListener, MouseMotionListener {
@Override
public void mouseMoved(MouseEvent e) {
Point p = fixEventPoint(e.getPoint());
GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY());
highlightNode(node);
}
@Override
public void mouseClicked(MouseEvent e) {
if (selected == null) {
return;
}
if (e.getButton() == MouseEvent.BUTTON1) {
Collection<GMLObject> deleted = editor.getMap().removeNode(selected);
editor.getViewer().repaint();
editor.setChanged();
editor.addEdit(new DeleteNodeEdit(selected, deleted));
}
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
Point p = fixEventPoint(e.getPoint());
pressPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
overlay.setLeft(pressPoint.getX());
overlay.setBottom(pressPoint.getY());
editor.getViewer().addOverlay(overlay);
editor.getViewer().repaint();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
Point p = fixEventPoint(e.getPoint());
dragPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
overlay.setLeft(Double.NaN);
overlay.setRight(Double.NaN);
overlay.setBottom(Double.NaN);
overlay.setTop(Double.NaN);
editor.getViewer().removeOverlay(overlay);
editor.getViewer().repaint();
removeNodes();
pressPoint = null;
dragPoint = null;
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (pressPoint != null) {
Point p = fixEventPoint(e.getPoint());
dragPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
overlay.setRight(dragPoint.getX());
overlay.setTop(dragPoint.getY());
editor.getViewer().repaint();
highlightNode(null);
}
}
@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);
}
}
private class DeleteNodeEdit extends AbstractUndoableEdit {
private GMLNode node;
private Collection<GMLObject> deletedObjects;
public DeleteNodeEdit(GMLNode node, Collection<GMLObject> deletedObjects) {
this.node = node;
this.deletedObjects = deletedObjects;
}
@Override
public void undo() {
super.undo();
editor.getMap().addNode(node);
editor.getMap().add(deletedObjects);
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
editor.getMap().removeNode(node);
editor.getMap().remove(deletedObjects);
editor.getViewer().repaint();
}
}
private class DeleteNodesEdit extends AbstractUndoableEdit {
private Collection<GMLNode> nodes;
private Map<GMLNode, Collection<GMLObject>> deletedObjects;
public DeleteNodesEdit(Collection<GMLNode> nodes, Map<GMLNode, Collection<GMLObject>> deletedObjects) {
this.nodes = nodes;
this.deletedObjects = deletedObjects;
}
@Override
public void undo() {
super.undo();
for (GMLNode next : nodes) {
Collection<GMLObject> deleted = deletedObjects.get(next);
editor.getMap().addNode(next);
editor.getMap().add(deleted);
}
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
for (GMLNode next : nodes) {
Collection<GMLObject> deleted = deletedObjects.get(next);
editor.getMap().removeNode(next);
editor.getMap().remove(deleted);
}
editor.getViewer().repaint();
}
}
}