package com.horstmann.violet.workspace.editorpart.behavior;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
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.edge.IEdge;
/**
* Undo/Redo behavior triggered when transition points change on free path edge
*
* @author Alexandre de Pellegrin
*
*/
public class UndoRedoOnTransitionPointChangeBehavior extends AbstractEditorPartBehavior {
/**
* The global undo/redo behavior which contains all individual undo/redo
* behaviors
*/
private UndoRedoCompoundBehavior compoundBehavior;
private List<Point2D> transitionPointsBeforeChanges = new ArrayList<Point2D>();
private List<Point2D> transitionPointsAfterChanges = new ArrayList<Point2D>();
/**
* Default constructor
*
* @param editorPart
*/
public UndoRedoOnTransitionPointChangeBehavior(UndoRedoCompoundBehavior compoundBehavior) {
this.compoundBehavior = compoundBehavior;
}
@Override
public void beforeChangingTransitionPointsOnEdge(IEdge edge) {
this.transitionPointsBeforeChanges.clear();
for (Point2D aTransitionPoint : edge.getTransitionPoints()) {
this.transitionPointsBeforeChanges.add(new Point2D.Double(aTransitionPoint.getX(), aTransitionPoint.getY()));
}
}
@Override
public void afterChangingTransitionPointsOnEdge(final IEdge edge) {
this.transitionPointsAfterChanges.clear();
for (Point2D aTransitionPoint : edge.getTransitionPoints()) {
this.transitionPointsAfterChanges.add(new Point2D.Double(aTransitionPoint.getX(), aTransitionPoint.getY()));
}
this.compoundBehavior.startHistoryCapture();
CompoundEdit capturedEdit = this.compoundBehavior.getCurrentCapturedEdit();
captureAddedPoints(edge, capturedEdit);
captureDraggedPoints(edge, capturedEdit);
this.compoundBehavior.stopHistoryCapture();
}
/* before, after after
* else {
System.out.println("before changes skip occured.");
}*/
private static final String LOG_AFTER_EVENT = "after changes skip occured (before=%d > after=%d).";
private static final String LOG_BEFORE_EVENT = "before changes skip occured (after=%d > before=%d).";
private void captureDraggedPoints(final IEdge edge, CompoundEdit capturedEdit) {
int beforeSize = this.transitionPointsBeforeChanges.size();
int afterSize = this.transitionPointsAfterChanges.size();
boolean isSameQuantity = (beforeSize == afterSize);
boolean isSameLocation = true;
/*
* debugging: previously only beforeSize was tested, so log when beforeSize is greater than afterSize,
* which would mean that indexing into transitionPointsAfterChanges would become invalid.
*/
if (beforeSize > afterSize) {
System.out.println(String.format(LOG_AFTER_EVENT, beforeSize, afterSize));
}
for (int i = 0; ((i < beforeSize) && (i < afterSize)); i++) {
Point2D beforeDragPoint = this.transitionPointsBeforeChanges.get(i);
Point2D afterDragPoint = this.transitionPointsAfterChanges.get(i);
isSameLocation = isSameLocation && beforeDragPoint.equals(afterDragPoint);
}
boolean isDragged = isSameQuantity && !isSameLocation;
if (!isDragged) {
return;
}
final List<Point2D> transitionPointsBeforeChangesCopy = new ArrayList<Point2D>(transitionPointsBeforeChanges);
final List<Point2D> transitionPointsAfterChangesCopy = new ArrayList<Point2D>(transitionPointsAfterChanges);
UndoableEdit edit = new AbstractUndoableEdit() {
@Override
public void undo() throws CannotUndoException {
int beforeCopySize = transitionPointsBeforeChangesCopy.size();
int afterCopySize = transitionPointsAfterChangesCopy.size();
/*
* debugging: previously only afterCopySize was tested, so log when afterCopySize is greater than beforeCopySize,
* which would mean that indexing into transitionPointsBeforeChangesCopy would become invalid.
*/
if (afterCopySize > beforeCopySize) {
System.err.println(String.format(LOG_BEFORE_EVENT, afterCopySize, beforeCopySize));
}
for (int i = 0; ((i < beforeCopySize) && (i < afterCopySize)); i++) {
Point2D beforeDragPoint = transitionPointsBeforeChangesCopy.get(i);
Point2D afterDragPoint = transitionPointsAfterChangesCopy.get(i);
if (afterDragPoint.getX() != beforeDragPoint.getX() || afterDragPoint.getY() != beforeDragPoint.getY()) {
Point2D[] transitionPoints = edge.getTransitionPoints();
transitionPoints[i].setLocation(beforeDragPoint.getX(), beforeDragPoint.getY());
}
}
}
@Override
public void redo() throws CannotRedoException {
int beforeCopySize = transitionPointsBeforeChangesCopy.size();
int afterCopySize = transitionPointsAfterChangesCopy.size();
/*
* debugging: previously only afterCopySize was tested, so log when afterCopySize is greater than beforeCopySize,
* which would mean that indexing into transitionPointsBeforeChangesCopy would become invalid.
*/
if (afterCopySize > beforeCopySize) {
System.err.println(String.format(LOG_BEFORE_EVENT, afterCopySize, beforeCopySize));
}
for (int i = 0; ((i < beforeCopySize) && (i < afterCopySize)); i++) {
Point2D beforeDragPoint = transitionPointsBeforeChangesCopy.get(i);
Point2D afterDragPoint = transitionPointsAfterChangesCopy.get(i);
if (afterDragPoint.getX() != beforeDragPoint.getX() || afterDragPoint.getY() != beforeDragPoint.getY()) {
Point2D[] transitionPoints = edge.getTransitionPoints();
transitionPoints[i].setLocation(afterDragPoint.getX(), afterDragPoint.getY());
}
}
}
};
capturedEdit.addEdit(edit);
}
private void captureAddedPoints(final IEdge edge, CompoundEdit capturedEdit) {
boolean isAdded = (this.transitionPointsBeforeChanges.size() != this.transitionPointsAfterChanges.size());
if (!isAdded) {
return;
}
final Map<Integer, Point2D> pointsAndPosition = new HashMap<Integer, Point2D>();
for (int i = 0; i < transitionPointsAfterChanges.size(); i++) {
Point2D aPoint = transitionPointsAfterChanges.get(i);
if (!transitionPointsBeforeChanges.contains(aPoint)) {
pointsAndPosition.put(i, aPoint);
}
}
UndoableEdit edit = new AbstractUndoableEdit() {
@Override
public void undo() throws CannotUndoException {
boolean isOKToRemove = true;
List<Point2D> transitionPoints = new ArrayList<Point2D>(Arrays.asList(edge.getTransitionPoints()));
for (Integer i : pointsAndPosition.keySet()) {
isOKToRemove = isOKToRemove && (transitionPoints.size() >= i);
if (!isOKToRemove) {
break;
}
isOKToRemove = isOKToRemove && (pointsAndPosition.get(i).equals(transitionPoints.get(i)));
}
if (!isOKToRemove) {
return;
}
for (Integer i : pointsAndPosition.keySet()) {
Point2D pointToRemove = pointsAndPosition.get(i);
transitionPoints.remove(pointToRemove);
}
Point2D[] transitionPointsAsArray = transitionPoints.toArray(new Point2D.Double[transitionPoints.size()]);
edge.setTransitionPoints(transitionPointsAsArray);
}
@Override
public void redo() throws CannotRedoException {
List<Point2D> transitionPoints = new ArrayList<Point2D>(Arrays.asList(edge.getTransitionPoints()));
for (Integer i : pointsAndPosition.keySet()) {
Point2D pointToAdd = pointsAndPosition.get(i);
if (transitionPoints.size() >= i) {
transitionPoints.add(i, pointToAdd);
} else {
transitionPoints.add(i, pointToAdd);
}
}
Point2D[] transitionPointsAsArray = transitionPoints.toArray(new Point2D.Double[transitionPoints.size()]);
edge.setTransitionPoints(transitionPointsAsArray);
}
};
capturedEdit.addEdit(edit);
}
}