package com.horstmann.violet.workspace.editorpart.behavior;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoableEdit;
import com.horstmann.violet.product.diagram.abstracts.IGraph;
import com.horstmann.violet.product.diagram.abstracts.node.INode;
import com.horstmann.violet.workspace.editorpart.IEditorPart;
import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler;
/**
* Undo/Redo behavior triggered when node and edges are dragged
*
* @author Alexandre de Pellegrin
*
*/
public class UndoRedoOnDragBehavior extends AbstractEditorPartBehavior
{
/**
* The concerned workspace
*/
private IEditorPart editorPart;
/**
* The global undo/redo behavior which contains all individual undo/redo behaviors
*/
private UndoRedoCompoundBehavior compoundBehavior;
/**
* To retreive selected elements
*/
private IEditorPartSelectionHandler selectionHandler;
/**
* Keeps node_old locations before dragging event
*/
private Map<INode, Point2D> nodesLocationsBeforeDrag = new HashMap<INode, Point2D>();
/**
* Used on node_old's drag'n drop
*/
private boolean isDragInProgress = false;
/**
* Default constructor
* @param editorPart
* @param compoundBehavior
*/
public UndoRedoOnDragBehavior(IEditorPart editorPart, UndoRedoCompoundBehavior compoundBehavior)
{
this.editorPart = editorPart;
this.selectionHandler = editorPart.getSelectionHandler();
this.compoundBehavior = compoundBehavior;
}
@Override
public void onMousePressed(MouseEvent event)
{
double zoom = editorPart.getZoomFactor();
final Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom);
this.isDragInProgress = false;
if (isMouseOnNode(mousePoint))
{
saveNodesLocationsBeforeDrag();
}
}
@Override
public void onMouseDragged(MouseEvent event)
{
this.isDragInProgress = true;
}
@Override
public void onMouseReleased(MouseEvent event)
{
if (!this.isDragInProgress)
{
return;
}
List<INode> selectedNodes = this.selectionHandler.getSelectedNodes();
List<UndoableEdit> editList = new ArrayList<UndoableEdit>();
for (final INode aSelectedNode : selectedNodes)
{
if (!this.nodesLocationsBeforeDrag.containsKey(aSelectedNode))
{
continue;
}
Point2D lastNodeLocation = this.nodesLocationsBeforeDrag.get(aSelectedNode);
Point2D currentNodeLocation = aSelectedNode.getLocation();
if (currentNodeLocation.equals(lastNodeLocation))
{
continue;
}
final double dx = currentNodeLocation.getX() - lastNodeLocation.getX();
final double dy = currentNodeLocation.getY() - lastNodeLocation.getY();
UndoableEdit edit = new AbstractUndoableEdit()
{
@Override
public void undo() throws CannotUndoException
{
aSelectedNode.translate(-dx, -dy);
super.undo();
}
@Override
public void redo() throws CannotRedoException
{
super.redo();
aSelectedNode.translate(dx, dy);
}
};
editList.add(edit);
}
if (editList.size() > 0)
{
this.compoundBehavior.startHistoryCapture();
CompoundEdit capturedEdit = this.compoundBehavior.getCurrentCapturedEdit();
for (UndoableEdit edit : editList)
{
capturedEdit.addEdit(edit);
}
this.compoundBehavior.stopHistoryCapture();
}
this.nodesLocationsBeforeDrag.clear();
this.isDragInProgress = false;
}
/**
* Saves node locations
*/
private void saveNodesLocationsBeforeDrag()
{
this.nodesLocationsBeforeDrag.clear();
Collection<INode> selectedNodes = this.editorPart.getGraph().getAllNodes();
for (INode aSelectedNode : selectedNodes)
{
Point2D location = aSelectedNode.getLocation();
this.nodesLocationsBeforeDrag.put(aSelectedNode, location);
}
}
private boolean isMouseOnNode(Point2D mouseLocation)
{
IGraph graph = this.editorPart.getGraph();
INode node = graph.findNode(mouseLocation);
if (node == null)
{
return false;
}
return true;
}
}