package pipe.views; import pipe.actions.gui.PipeApplicationModel; import pipe.controllers.PetriNetController; import uk.ac.imperial.pipe.models.petrinet.Arc; import uk.ac.imperial.pipe.models.petrinet.ArcPoint; import uk.ac.imperial.pipe.models.petrinet.Connectable; import javax.swing.event.MouseInputAdapter; import java.awt.Container; import java.awt.geom.Point2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; /** * This class contains the common methods for different arc types. * <p> * At present when arc points are modified the whole arc is redrawn. At some point * in the future it would be good if they're more dynamic than this. * </p> * @param <S> Model source type * @param <T> Model target type */ @SuppressWarnings("serial") public abstract class ArcView<S extends Connectable, T extends Connectable> extends AbstractPetriNetViewComponent<Arc<S, T>> { /** * Bounds of arc need to be grown in order to avoid clipping problems. * This value achieves it. */ protected static final int ZOOM_GROW = 10; /** * Actual visible path */ protected final ArcPath arcPath; public ArcView(Arc<S, T> model, PetriNetController controller, Container parent, MouseInputAdapter arcHandler, PipeApplicationModel applicationModel) { super(model.getId(), model, controller, parent); arcPath = new ArcPath(this, controller, applicationModel); updatePath(); updateBounds(); registerModelListeners(); setMouseListener(arcHandler); } /** * Repopulates the path with the models points */ protected final void updatePath() { addIntermediatePoints(); arcPath.createPath(); arcPath.addPointsToGui(getParent()); repaint(); } /** * Updates the bounding box of the arc component based on the arcs bounds */ public final void updateBounds() { bounds = arcPath.getBounds(); bounds.grow(getComponentDrawOffset() + ZOOM_GROW, getComponentDrawOffset() + ZOOM_GROW); setBounds(bounds); } /** * Registers listeners for the arc model and it's source and target models */ private void registerModelListeners() { addArcChangeListener(); } /** * Register the mouse handler to this view * @param arcHandler handler to determine what the arc does on mouse events */ public final void setMouseListener(MouseInputAdapter arcHandler) { addMouseListener(arcHandler); addMouseWheelListener(arcHandler); addMouseMotionListener(arcHandler); } /** * Loops through points adding them to the path if they don't already * exist */ private void addIntermediatePoints() { int index = 0; for (ArcPoint arcPoint : model.getArcPoints()) { if (!arcPath.contains(arcPoint)) { arcPath.insertIntermediatePoint(arcPoint, index); } index++; } } /** * Listens for intermediate points being added/deleted * Will call a redraw of the existing points */ private void addArcChangeListener() { PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent propertyChangeEvent) { String name = propertyChangeEvent.getPropertyName(); if (name.equals(Arc.NEW_INTERMEDIATE_POINT_CHANGE_MESSAGE)) { updateAllPoints(); } else if (name.equals(Arc.DELETE_INTERMEDIATE_POINT_CHANGE_MESSAGE)) { ArcPoint point = (ArcPoint) propertyChangeEvent.getOldValue(); arcPath.deletePoint(point); updateAllPoints(); } } }; model.addPropertyChangeListener(listener); } /** * Updates all arc points displayed based on their positions * in the model */ private void updateAllPoints() { updatePath(); updateBounds(); } /** * * @param x coordinate * @param y coordinate * @return true if (x,y) intersect the arc path */ @Override public final boolean contains(int x, int y) { Point2D.Double point = new Point2D.Double(x + arcPath.getBounds().getX() - getComponentDrawOffset() - ZOOM_GROW, y + arcPath.getBounds().getY() - getComponentDrawOffset() - ZOOM_GROW); if (!petriNetController.isInAnimationMode()) { if (arcPath.proximityContains(point) || isSelected()) { // show also if Arc itself selected arcPath.showPoints(); } else { arcPath.hidePoints(); } } return arcPath.contains(point); } @Override public void componentSpecificDelete() { arcPath.delete(); } /** * * @return the graphical arc path which displays the arc and its points */ public final ArcPath getArcPath() { return arcPath; } // Accessor function to check whether or not the Arc is tagged /** * * This should return if the arc is tagged for the tagging module but * the functionality has not been implemented * * @return false */ public final boolean isTagged() { return false; } }