package pipe.views; import pipe.actions.gui.PipeApplicationModel; import pipe.constants.GUIConstants; import pipe.controllers.PetriNetController; import uk.ac.imperial.pipe.exceptions.PetriNetComponentNotFoundException; 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 uk.ac.imperial.pipe.models.petrinet.Token; import javax.swing.event.MouseInputAdapter; import java.awt.*; import java.awt.geom.AffineTransform; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Collection; import java.util.LinkedList; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * Normal arc view * @param <S> source model * @param <T> target model */ public class NormalArcView<S extends Connectable, T extends Connectable> extends ArcView<S, T> { /** * Class logger */ private static final Logger LOGGER = Logger.getLogger(NormalArcView.class.getName()); /** * Weight labels to display */ private final Collection<TextLabel> weightLabel = new LinkedList<>(); /** * Listens for changes in intermediate points x, y and updates the weights accordingly */ private final WeightLabelListener weightListener = new WeightLabelListener(); /** * Graphical arc head */ private ArcHead arcHead = new NormalHead(); /** * joined if it displays two directional arcs sharing the opposite source and targets */ @Deprecated private boolean joined = false; /** * Constructor * @param model underlying normal arc * @param controller Petri ent controller for the Petri net that houses the arc * @param parent view parent * @param handler mouse event handler * @param applicationModel PIPE main appliaction model */ public NormalArcView(Arc<S, T> model, PetriNetController controller, Container parent, MouseInputAdapter handler, PipeApplicationModel applicationModel) { super(model, controller, parent, handler, applicationModel); addConnectableListener(); addSourceTargetConnectableListener(); for (TextLabel label : weightLabel) { getParent().add(label); } for (ArcPoint arcPoint : model.getArcPoints()) { arcPoint.addPropertyChangeListener(weightListener); } } /** * Listens to the source/target changing position */ private void addSourceTargetConnectableListener() { PropertyChangeListener changeListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (name.equals(Connectable.X_CHANGE_MESSAGE) || name.equals(Connectable.Y_CHANGE_MESSAGE)) { updateWeights(); } } }; model.getSource().addPropertyChangeListener(changeListener); model.getTarget().addPropertyChangeListener(changeListener); } /** * Adds a listener to the the arc listening for a change in the arc weights */ private void addConnectableListener() { PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent propertyChangeEvent) { String name = propertyChangeEvent.getPropertyName(); if (name.equals(Arc.WEIGHT_CHANGE_MESSAGE)) { updateWeights(); } if (name.equals(Arc.NEW_INTERMEDIATE_POINT_CHANGE_MESSAGE)) { ArcPoint point = (ArcPoint) propertyChangeEvent.getNewValue(); point.addPropertyChangeListener(weightListener); updateWeights(); } if (name.equals(Arc.DELETE_INTERMEDIATE_POINT_CHANGE_MESSAGE)) { ArcPoint point = (ArcPoint) propertyChangeEvent.getOldValue(); point.removePropertyChangeListener(weightListener); updateWeights(); } } }; model.addPropertyChangeListener(listener); } /** * Paints the weight label on the parent */ @Override public void componentSpecificDelete() { super.componentSpecificDelete(); for (TextLabel label : weightLabel) { removeLabelFromParentContainer(label); } } /** * Removes the weight labels from the parent * @param label */ private void removeLabelFromParentContainer(TextLabel label) { getParent().remove(label); } /** * When added to the container it updates the paths and weights * @param container to add itself to */ @Override public void addToContainer(Container container) { updatePath(); updateWeights(); } /** * Creates and paints the weights in the center of the arc */ private void updateWeights() { removeCurrentWeights(); createWeightLabels(); setWeightLabelPosition(); addWeightLabelsToContainer(getParent()); } /** * Removes the weights from the arc */ private void removeCurrentWeights() { for (TextLabel name : weightLabel) { removeLabelFromParentContainer(name); } weightLabel.clear(); } /** * Creates token weight labels */ private void createWeightLabels() { Map<String, String> weights = model.getTokenWeights(); for (Map.Entry<String, String> entry : weights.entrySet()) { String weight = entry.getValue(); String tokenId = entry.getKey(); TextLabel label = new TextLabel(weight); try { Token token = petriNetController.getToken(tokenId); label.setColor(token.getColor()); } catch (PetriNetComponentNotFoundException e) { LOGGER.log(Level.SEVERE, e.getMessage()); label.setColor(Color.BLACK); } label.updateSize(); weightLabel.add(label); } } /** * Set the position of the weight labels to the middle of the arc */ protected void setWeightLabelPosition() { int originalX = (int) arcPath.midPoint.x; int originalY = (int) arcPath.midPoint.y - 10; int x = originalX; int y = originalY; int yCount = 0; for (TextLabel label : weightLabel) { if (yCount >= 4) { y = originalY; x += 17; yCount = 0; } label.setPosition(x + label.getWidth() / 2 - 4, y); y += 10; yCount++; } } /** * Add weight labels to the container * @param container */ private void addWeightLabelsToContainer(Container container) { for (TextLabel label : weightLabel) { container.add(label); } } /** * Paints the arc * @param g graphics */ @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; AffineTransform reset = g2.getTransform(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.translate(getComponentDrawOffset() + ZOOM_GROW - arcPath.getBounds().getX(), getComponentDrawOffset() + ZOOM_GROW - arcPath.getBounds().getY()); if (isSelected() && !ignoreSelection) { g2.setPaint(GUIConstants.SELECTION_LINE_COLOUR); } else { g2.setPaint(GUIConstants.ELEMENT_LINE_COLOUR); } if (joined) { g2.translate(arcPath.getPoint(0).getX(), arcPath.getPoint(0).getY()); g2.rotate(arcPath.getStartAngle() + Math.PI); g2.setTransform(reset); } g2.setStroke(new BasicStroke(1f)); g2.draw(arcPath); g2.translate(arcPath.getPoint(arcPath.getEndIndex()).getX(), arcPath.getPoint(arcPath.getEndIndex()).getY()); g2.rotate(model.getEndAngle()); g2.setColor(java.awt.Color.WHITE); if (isSelected() && !ignoreSelection) { g2.setPaint(GUIConstants.SELECTION_LINE_COLOUR); } else { g2.setPaint(GUIConstants.ELEMENT_LINE_COLOUR); } arcHead.draw(g2); g2.transform(reset); } /** * Weight label listener for updating the arc weight label location if arc points change */ private class WeightLabelListener implements PropertyChangeListener { /** * If the event fired is an arc point (x,y) location change then the * weights label location is recalculated * @param evt */ @Override public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (name.equals(ArcPoint.UPDATE_LOCATION_CHANGE_MESSAGE)) { updateWeights(); } } } }