package pipe.controllers; import pipe.gui.PetriNetTab; import pipe.historyActions.component.DeletePetriNetObject; import uk.ac.imperial.pipe.exceptions.PetriNetComponentException; import uk.ac.imperial.pipe.exceptions.PetriNetComponentNotFoundException; import uk.ac.imperial.pipe.models.petrinet.*; import uk.ac.imperial.pipe.naming.PlaceNamer; import uk.ac.imperial.pipe.naming.TransitionNamer; import uk.ac.imperial.pipe.naming.UniqueNamer; import uk.ac.imperial.pipe.parsers.FunctionalResults; import uk.ac.imperial.pipe.visitor.ClonePetriNet; import uk.ac.imperial.pipe.visitor.TranslationVisitor; import uk.ac.imperial.pipe.visitor.component.PetriNetComponentVisitor; import javax.swing.event.UndoableEditListener; import javax.swing.undo.UndoManager; import javax.swing.undo.UndoableEdit; import java.awt.Color; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.io.Serializable; import java.util.*; @SuppressWarnings("serial") public class PetriNetController implements Serializable { /** * Responsible for zooming of the current Petri net */ private final ZoomController zoomController; /** * Responsible for handling undo/redo */ private final UndoManager undoManager = new UndoManager(); /** * Petri net being displayed */ private final PetriNet petriNet; /** * Listener for tool bar actions that create undoable actions */ private final UndoableEditListener undoListener; /** * Tab that the Petri net is shown on */ private final PetriNetTab petriNetTab; /** * Selected components in the Petri net */ private final Set<PetriNetComponent> selectedComponents = new HashSet<>(); /** * Responsible for copy and pasting of selected components */ private final CopyPasteManager copyPasteManager; /** * Responsible for naming places */ private final UniqueNamer placeNamer; /** * Responsible for creating unique transition names */ private final UniqueNamer transitionNamer; /** * Token id that is currently selected in the drop down */ private String selectedToken; /** * Animator class for animating tokens in the petri net */ private GUIAnimator animator; /** * Drag manager for dragging selected objects */ private DragManager dragManager = new DragManager(this); /** * Name of file the Petri net is saved to. Empty string if it has not yet been saved/loaded * from file */ private String fileName = ""; /** * Copy of the last saved version of the Petri net */ private PetriNet lastSavedNet; /** * Set to true if the Petri net is in animation mode */ private boolean animateMode = false; /** * Selection manager for selecting petri net components */ private SelectionManager selectionManager; /** * Constructor * @param model underlying Petri net * @param undoListener undo listener for tool bar buttons undo actions * @param animator Petri net animator * @param copyPasteManager copy paste manager for the Petri net * @param zoomController zoom controller for the Petri net * @param petriNetTab tab this Petri net is displayed on */ public PetriNetController(PetriNet model, UndoableEditListener undoListener, GUIAnimator animator, CopyPasteManager copyPasteManager, ZoomController zoomController, PetriNetTab petriNetTab) { petriNet = model; this.undoListener = undoListener; this.petriNetTab = petriNetTab; selectionManager = new SelectionManager(this); lastSavedNet = ClonePetriNet.clone(model); this.zoomController = zoomController; this.animator = animator; this.copyPasteManager = copyPasteManager; if (model.getTokens().size() > 0) { selectedToken = model.getTokens().iterator().next().getId(); } placeNamer = new PlaceNamer(model); transitionNamer = new TransitionNamer(model); } /** * @return Tab this controller is associated with */ public PetriNetTab getPetriNetTab() { return petriNetTab; } /** * @return A unique name for a place in the current petri net */ public String getUniquePlaceName() { return placeNamer.getName(); } /** * @return A unique name for a transition in the current petri net */ public String getUniqueTransitionName() { return transitionNamer.getName(); } /** * * @param component to check for selection * @return true if this component is selected on the canvas */ public boolean isSelected(PetriNetComponent component) { return selectedComponents.contains(component); } /** * unselect the component on the canvas * @param component to unselect */ public void deselect(PetriNetComponent component) { selectedComponents.remove(component); } /** * Deselect all canvas componentns */ public void deselectAll() { selectedComponents.clear(); } /** * Translates any components that are selected using a TranslationVisitor * * @param translation translation distance * @throws PetriNetComponentException if component is not found */ public void translateSelected(Point translation) throws PetriNetComponentException { PetriNetComponentVisitor translationVisitor = new TranslationVisitor(translation, selectedComponents); for (PetriNetComponent component : selectedComponents) { if (component.isDraggable()) { component.accept(translationVisitor); } } } /** * Selects all components within this rectangle * * @param selectionRectangle bounds for selection */ public void select(Rectangle selectionRectangle) { for (Place place : petriNet.getPlaces()) { selectPlaceable(place, selectionRectangle); } for (Transition transition : petriNet.getTransitions()) { selectPlaceable(transition, selectionRectangle); } for (Arc<? extends Connectable, ? extends Connectable> arc : petriNet.getArcs()) { if (isArcSelected(arc, selectionRectangle)) { select(arc); for (ArcPoint arcPoint : arc.getArcPoints()) { select(arcPoint); } } else if (selectedComponents.contains(arc.getSource()) || selectedComponents.contains(arc.getTarget())) { select(arc); } } for (Annotation annotation : petriNet.getAnnotations()) { selectPlaceable(annotation, selectionRectangle); } } /** * A crude method for selecting arcs, does not take into account bezier curves * * @param arc arc to test to see if it is selected * @param selectionRectangle bounds of selection on screen * @return if selectionRectangle intersects the path */ private boolean isArcSelected(Arc<? extends Connectable, ? extends Connectable> arc, Rectangle selectionRectangle) { GeneralPath path = createStraightPath(arc); return path.intersects(selectionRectangle); } /** * Creates an arc with a straight path arc * * @param arc * @return Straight path for arc, ignoring Bezier curves */ private GeneralPath createStraightPath(Arc<? extends Connectable, ? extends Connectable> arc) { GeneralPath path = new GeneralPath(); Collection<ArcPoint> arcPoints = arc.getArcPoints(); int index = 0; for (ArcPoint arcPoint : arcPoints) { if (index == 0) { path.moveTo(arcPoint.getX(), arcPoint.getY()); } else { Point2D point = arcPoint.getPoint(); path.lineTo(point.getX(), point.getY()); } index++; } return path; } /** * Select the Petri net component on the canvas * @param component to select */ public void select(PetriNetComponent component) { selectedComponents.add(component); } /** * Tests to see if the object is in the selection rectangle * If it is it selects in * * @param placeable object to see if it is selectable * @param selectionRectangle bounds for selection */ private void selectPlaceable(PlaceablePetriNetComponent placeable, Rectangle selectionRectangle) { int x = placeable.getX(); int y = placeable.getY(); Rectangle rectangle = new Rectangle(x, y, placeable.getHeight(), placeable.getWidth()); if (selectionRectangle.intersects(rectangle)) { select(placeable); } } /** * Deletes selection and adds to history manager * @return list of edits performed * @throws PetriNetComponentException if component not found */ public List<UndoableEdit> deleteSelection() throws PetriNetComponentException { List<UndoableEdit> edits = new LinkedList<>(); for (PetriNetComponent component : selectedComponents) { edits.add(deleteComponent(component)); } selectedComponents.clear(); return edits; } /** * Deletes a component and returns the AbstractUndoableEdit in order * to redo the action * * @param component * @return AbstractUndoableEdit created for deleting the component * @throws PetriNetComponentException if component not found */ private UndoableEdit deleteComponent(PetriNetComponent component) throws PetriNetComponentException { petriNet.remove(component); return new DeletePetriNetObject(component, petriNet); } /** * Deletes single component, starts a newEdit for history manager * * @param component to delete * @return AbstractUndableEdit created * @throws PetriNetComponentException if component not found */ public UndoableEdit delete(PetriNetComponent component) throws PetriNetComponentException { return deleteComponent(component); } /** * Adds a new token to the petrinet * * @param name of the token * @param color of the token */ public void createNewToken(String name, Color color) { Token token = new ColoredToken(name, color); petriNet.addToken(token); } /** * * @return all tokens in the Petri net */ public Collection<Token> getNetTokens() { return petriNet.getTokens(); } /** * Update the token with the specified name and color * @param currentTokenName current name * @param name new token name * @param color of the token * @throws PetriNetComponentNotFoundException if token not found */ public void updateToken(String currentTokenName, String name, Color color) throws PetriNetComponentNotFoundException { Token token = petriNet.getComponent(currentTokenName, Token.class); if (!token.getId().equals(name)) { token.setId(name); } if (!token.getColor().equals(color)) { token.setColor(color); } } /** * * @return underlying Petri net model */ //TODO: Shouldnt expose this! public PetriNet getPetriNet() { return petriNet; } /** * @param arc arc * @param <S> source * @param <T> target * @return controller for the model */ public <S extends Connectable, T extends Connectable> ArcController<S, T> getArcController(Arc<S, T> arc) { return new ArcController<>(arc, this, undoListener); } /** * * @param place current place * @return controller for the place */ public PlaceController getPlaceController(Place place) { return new PlaceController(place, undoListener); } /** * * @param annotation current annotation * @return controller for the annotation */ public AnnotationController getAnnotationController(Annotation annotation) { return new AnnotationController(annotation, undoListener); } /** * * @param transition current transition * @return controller for the transition */ public TransitionController getTransitionController(final Transition transition) { return new TransitionController(transition, undoListener); } /** * * @param rateParameter current rate parameter * @return controller for the rate parameter * @throws PetriNetComponentNotFoundException if not found */ public RateParameterController getRateParameterController(final String rateParameter) throws PetriNetComponentNotFoundException { RateParameter parameter = petriNet.getComponent(rateParameter, RateParameter.class); return new RateParameterController(parameter, petriNet, undoListener); } /** * Selected token on the drop down menu * @param tokenName selected * @throws PetriNetComponentNotFoundException if not found */ public void selectToken(String tokenName) throws PetriNetComponentNotFoundException { selectedToken = tokenName; } /** * @param name token name to find * @return Token from PetriNet * @throws PetriNetComponentNotFoundException if the token does not exist */ public Token getToken(String name) throws PetriNetComponentNotFoundException { return petriNet.getComponent(name, Token.class); } /** * Copy all components that are selected */ public void copySelection() { copyPasteManager.copy(selectedComponents); } /** * * @return true if a paste has been enabled */ public boolean isCopyInProgress() { return copyPasteManager.pasteEnabled(); } /** * Cancels the current paste */ public void cancelPaste() { copyPasteManager.cancelPaste(); } /** * * The selected token can then be used to add tokens to places * * @return the current token on the drop down menu */ public String getSelectedToken() { return selectedToken; } /** * * @return the animator of the Petri net */ public GUIAnimator getAnimator() { return animator; } /** * * @return the zoom controller of the Petri net */ public ZoomController getZoomController() { return zoomController; } /** * Paste the copied items onto the Petri net */ public void paste() { copyPasteManager.showPasteRectangle(); } /** * * @return Petri net drag manager */ public DragManager getDragManager() { return dragManager; } /** * * @return rate parameters in the Petri net */ public Collection<RateParameter> getRateParameters() { return petriNet.getRateParameters(); } /** * * @param id of the component * @return true if this id does not exist in the Petri net */ public boolean isUniqueName(String id) { return placeNamer.isUniqueName(id) && transitionNamer.isUniqueName(id); } /** * * @return true if the Petri net has changed since it was last saved/loaded */ public boolean hasChanged() { return !petriNet.equals(lastSavedNet); } /** * Take a clone of the Petri net */ public void save() { lastSavedNet = ClonePetriNet.clone(petriNet); } /** * * @param expr functional expression to parse * @return parsed functional expression in relation to the Petri nets current state */ public FunctionalResults<Double> parseFunctionalExpression(String expr) { return petriNet.parseExpression(expr); } /** * * @return Petri nets undo manager */ public UndoManager getUndoManager() { return undoManager; } /** * * @return all selected components */ public Set<PetriNetComponent> getSelectedComponents() { return selectedComponents; } /** * * @return Petri net undo listener */ public UndoableEditListener getUndoListener() { return undoListener; } /** * * Toggles animation from false -> true or true -> false * @return new mode */ public boolean toggleAnimation() { animateMode = !animateMode; return animateMode; } /** * * @return if the Petri net should be displayed in animation mode on the canvas */ public boolean isInAnimationMode() { return animateMode; } /** * * @return Petri net selection manager */ public SelectionManager getSelectionManager() { return selectionManager; } }