package pipe.controllers;
import pipe.historyActions.MultipleEdit;
import pipe.historyActions.component.MovePetriNetObject;
import pipe.utilities.gui.GuiUtils;
import uk.ac.imperial.pipe.exceptions.PetriNetComponentException;
import uk.ac.imperial.pipe.models.petrinet.*;
import javax.swing.event.UndoableEditEvent;
import javax.swing.undo.UndoableEdit;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Handles dragging of objects around when selected
*/
public class DragManager {
/**
* Petri net controller for which components will be dragged
*/
private PetriNetController petriNetController;
/**
* Starting coordinate of the drag
*/
private Point2D.Double dragStart = new Point2D.Double(0, 0);
/**
* All selected items locations at the start of a drag
* Mapping of id -> location
*/
private Map<String, Point2D> startingCoordinates = new HashMap<>();
/**
* Constructor
* @param petriNetController controller for which components will be dragged
*/
public DragManager(PetriNetController petriNetController) {
this.petriNetController = petriNetController;
}
/**
*
* @param dragStart the start location from which components are dragged
*/
public void setDragStart(Point2D.Double dragStart) {
this.dragStart = dragStart;
}
/**
* Drag items to location
*
* @param location location of mouse to drag items to
*/
public void drag(Point location) {
int x = (int) (location.getX() - dragStart.getX());
int y = (int) (location.getY() - dragStart.getY());
dragStart = new Point2D.Double(location.x, location.y);
try {
petriNetController.translateSelected(new Point(x, y));
} catch (PetriNetComponentException e) {
GuiUtils.displayErrorMessage(null, e.getMessage());
}
}
/**
* Saves the starting coordinates of all the selected items.
* This means that their undo drag item can reference their starting location
*/
public void saveStartingDragCoordinates() {
startingCoordinates.clear();
Map<PlaceablePetriNetComponent, Point2D> selectedPoints = getSelectedCoordinates();
for (Map.Entry<PlaceablePetriNetComponent, Point2D> entry : selectedPoints.entrySet()) {
startingCoordinates.put(entry.getKey().getId(), entry.getValue());
}
}
/**
* Method to call after finishing a drag,
* ensures undoable edit is created
*/
public void finishDrag() {
Map<PlaceablePetriNetComponent, Point2D> translatedCoordinates = getSelectedCoordinates();
createMovedUndoItem(startingCoordinates, translatedCoordinates);
}
/**
* Loops through each PlaceablePetriNetComponents start and ending coordinates (i.e. before and after translation)
* and creates a {@link pipe.historyActions.component.MovePetriNetObject} undoEdit for each event
*
* It then creates an {@link pipe.historyActions.MultipleEdit} with all these undoEdits in and
* registers this with the undoListener.
* @param startingCoordinates of selected items before translation
* @param translatedCoordinates of selected items after translation
*/
private void createMovedUndoItem(Map<String, Point2D> startingCoordinates,
Map<PlaceablePetriNetComponent, Point2D> translatedCoordinates) {
List<UndoableEdit> undoableEdits = new LinkedList<>();
for (Map.Entry<PlaceablePetriNetComponent, Point2D> entry : translatedCoordinates.entrySet()) {
PlaceablePetriNetComponent component = entry.getKey();
Point2D starting = startingCoordinates.get(component.getId());
Point2D translated = entry.getValue();
if (!starting.equals(translated)) {
undoableEdits.add(new MovePetriNetObject(component, starting, translated));
}
}
if (!undoableEdits.isEmpty()) {
petriNetController.getUndoListener().undoableEditHappened(new UndoableEditEvent(this, new MultipleEdit(undoableEdits)));
}
}
/**
*
* @return the coordinates of all selected items
*/
private Map<PlaceablePetriNetComponent, Point2D> getSelectedCoordinates() {
CoordinateSaver saver = new CoordinateSaver();
for (PetriNetComponent component : petriNetController.getSelectedComponents()) {
if (component.isDraggable()) {
try {
component.accept(saver);
} catch (PetriNetComponentException e) {
GuiUtils.displayErrorMessage(null, e.toString());
}
}
}
return saver.savedCoordinates;
}
/**
* Saves the coordinates of selectable items
*/
private static class CoordinateSaver
implements ArcVisitor, ArcPointVisitor, PlaceVisitor, TransitionVisitor, AnnotationVisitor {
/**
* Map containing a component to its coordinates
*/
private Map<PlaceablePetriNetComponent, Point2D> savedCoordinates = new HashMap<>();
/**
*
* Saves the annotaiton coordinates
*
* @param annotation
*/
@Override
public void visit(Annotation annotation) {
savedCoordinates.put(annotation, new Point2D.Double(annotation.getX(), annotation.getY()));
}
/**
* Arc point coordinates
* @param arcPoint
*/
@Override
public void visit(ArcPoint arcPoint) {
savedCoordinates.put(arcPoint, arcPoint.getPoint());
}
/**
* Saves the place coordinates
* @param place
*/
@Override
public void visit(Place place) {
savedCoordinates.put(place, new Point2D.Double(place.getX(), place.getY()));
}
/**
*
* Saves the transition coordinates
* @param transition
*/
@Override
public void visit(Transition transition) {
savedCoordinates.put(transition, new Point2D.Double(transition.getX(), transition.getY()));
}
/**
* Noop action
* @param inboundArc
*/
@Override
public void visit(InboundArc inboundArc) {
//TODO: Arc arc points covered by the above?
}
/**
* Noop action
* @param outboundArc
*/
@Override
public void visit(OutboundArc outboundArc) {
//TODO: Arc arc points covered by the above?
}
}
}