package pipe.views;
import pipe.controllers.AbstractConnectableController;
import pipe.controllers.PetriNetController;
import pipe.handlers.LabelHandler;
import uk.ac.imperial.pipe.models.petrinet.Connectable;
import javax.swing.*;
import java.awt.Component;
import java.awt.Container;
import java.awt.Rectangle;
import java.awt.Shape;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
/**
* Abstract connectable view for connectable models
* @param <T> Connectable model
*/
public abstract class ConnectableView<T extends Connectable> extends AbstractPetriNetViewComponent<T>
implements Cloneable, Serializable {
/**
* Name label for the connectable item
*/
protected TextLabel textLabel;
private final AbstractConnectableController<T> componentController;
/**
* Shape of the place on the Petri net
*/
protected Shape shape;
/**
* Constructor
* @param id
* @param model
* @param controller
* @param parent
* @param shape
*/
ConnectableView(String id, T model, PetriNetController controller, AbstractConnectableController<T> componentController, Container parent, Shape shape) {
super(id, model, controller, parent);
this.componentController = componentController;
this.shape = shape;
setLocation(model.getX(), model.getY());
int x = (int) (model.getX() + model.getNameXOffset());
int y = (int) (model.getX() + model.getNameXOffset());
textLabel = new TextLabel(model.getId(), x, y);
addChangeListener();
updateBounds();
}
/**
* Adds x, y and id change listeners to the underlying model which redraw the connectable when changed
*/
private void addChangeListener() {
model.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
String name = propertyChangeEvent.getPropertyName();
if (name.equals(Connectable.X_CHANGE_MESSAGE) || name.equals(Connectable.Y_CHANGE_MESSAGE)) {
updateBounds();
updateLabelLocation();
} else if (name.equals(Connectable.NAME_X_OFFSET_CHANGE_MESSAGE) || name.equals(
Connectable.NAME_Y_OFFSET_CHANGE_MESSAGE)) {
updateLabelLocation();
} else if (name.equals(Connectable.ID_CHANGE_MESSAGE) || name.equals(Connectable.NAME_CHANGE_MESSAGE)) {
//TODO: NAMELABEL SHOULD LISTEN?
String newName = (String) propertyChangeEvent.getNewValue();
textLabel.setText(newName);
textLabel.repaint();
}
}
});
}
/**
* Updates label position according to the Connectable location
*/
private final void updateLabelLocation() {
textLabel.setPosition(model.getX() + model.getNameXOffset(), model.getY() + model.getNameYOffset());
}
/**
* Changes the displayed bounds of the object relative to its x,y width and height
*
* Implemented because the canvas has no layout manager
*
*/
protected final void updateBounds() {
Rectangle bounds = shape.getBounds();
Rectangle newBounds = new Rectangle((int)(model.getCentre().getX() + bounds.getX()), (int)(model.getCentre().getY() + bounds.getY()), (int) bounds.getWidth() + getComponentDrawOffset(), (int)bounds.getHeight() + getComponentDrawOffset()) ;
setBounds(newBounds);
// setBounds(model.getX(), model.getY(), model.getWidth() + getComponentDrawOffset(), model.getHeight() + getComponentDrawOffset());
//TODO: THIS IS A DIRTY HACK IN ORDER TO GET DRAGGIGN WHEN ZOOMED WORKING
Component root = SwingUtilities.getRoot(this);
if (root != null) {
root.repaint();
}
}
/**
* Performs a connectable specific delete by removing the name label from the parent
*/
@Override
public final void componentSpecificDelete() {
if (getParent() != null) {
getParent().remove(textLabel);
}
}
/**
* Adds the name label to the container
* @param container to add label to
*/
protected final void addLabelToContainer(Container container) {
container.add(textLabel);
textLabel.setPosition(model.getX() + model.getNameXOffset(), model.getY() + model.getNameYOffset());
LabelHandler<T> labelHandler = new LabelHandler<>(textLabel, this, componentController);
textLabel.addMouseListener(labelHandler);
textLabel.addMouseMotionListener(labelHandler);
textLabel.addMouseWheelListener(labelHandler);
}
}