package nl.fontys.sofa.limo.view.widget;
import java.awt.BasicStroke;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JPopupMenu;
import javax.swing.event.UndoableEditEvent;
import javax.swing.undo.UndoManager;
import nl.fontys.sofa.limo.domain.component.leg.Leg;
import nl.fontys.sofa.limo.domain.component.leg.MultiModeLeg;
import nl.fontys.sofa.limo.domain.component.leg.ScheduledLeg;
import nl.fontys.sofa.limo.view.action.DeleteAction;
import nl.fontys.sofa.limo.view.chain.ChainGraphScene;
import nl.fontys.sofa.limo.view.node.bean.AbstractBeanNode;
import nl.fontys.sofa.limo.view.node.bean.HubNode;
import nl.fontys.sofa.limo.view.node.bean.LegNode;
import static nl.fontys.sofa.limo.view.util.IconUtil.getScaledImageFromIcon;
import nl.fontys.sofa.limo.view.util.LIMOResourceBundle;
import nl.fontys.sofa.limo.view.util.undoable.widget.leg.DeleteLegWidgetUndoableEdit;
import nl.fontys.sofa.limo.view.wizard.leg.multimode.MultimodeLegWizardAction;
import nl.fontys.sofa.limo.view.wizard.leg.normal.NormalLegWizardAction;
import nl.fontys.sofa.limo.view.wizard.leg.scheduled.ScheduledLegWizardAction;
import org.netbeans.api.visual.action.ActionFactory;
import org.netbeans.api.visual.action.PopupMenuProvider;
import org.netbeans.api.visual.anchor.AnchorShape;
import org.netbeans.api.visual.anchor.PointShape;
import org.netbeans.api.visual.layout.LayoutFactory;
import org.netbeans.api.visual.widget.ConnectionWidget;
import org.netbeans.api.visual.widget.ImageWidget;
import org.netbeans.api.visual.widget.LabelWidget;
import org.netbeans.api.visual.widget.Scene;
import org.netbeans.api.visual.widget.Widget;
/**
* Widget responsible for displaying a Leg in a GraphScene. LegWidgets can be
* added to a connection widget.
*
* @author Sebastiaan Heijmann
*/
public class LegWidget extends ConnectionWidget implements BasicWidget {
private Map<Leg, Double> legs;
private final LegNode legNode;
private LabelWidget eventLabelWidget, procedureLabelWidget;
private Scene scene;
/**
* Constructor creates a new LegWidget.
*
* @param scene the scene to draw this widget on.
* @param legNode the node this widget belongs to.
*/
public LegWidget(Scene scene, AbstractBeanNode legNode) {
super(scene);
this.legNode = (LegNode) legNode;
this.scene = scene;
setChildLegWidgets();
setTargetAnchorShape(AnchorShape.TRIANGLE_FILLED);
setStroke(new BasicStroke(3.0f));
setEndPointShape(PointShape.SQUARE_FILLED_BIG);
legNode.addPropertyChangeListener((PropertyChangeEvent evt) -> {
listeners.forEach((PropertyChangeListener t) -> {
t.propertyChange(evt);
updateLabels();
});
});
legNode.getLookup().lookup(Leg.class).addPropertyChangeListener((evt) -> {
updateLabels();
});
}
private List<PropertyChangeListener> listeners = new ArrayList<>();
public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.add(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
listeners.remove(listener);
}
@Override
public void addActions(ChainGraphScene scene) {
getActions().addAction(scene.createObjectHoverAction());
getActions().addAction(scene.getSelectAction());
getActions().addAction(ActionFactory.createPopupMenuAction(new LegWidget.WidgetPopupMenu()));
}
/**
* Set the children of this widget.
*/
private void setChildLegWidgets() {
Leg leg = getLeg();
if (leg instanceof MultiModeLeg) {
setMultiModeLegWidgets(leg);
ImageWidget iw = new ImageWidget(getScene());
iw.setImage(new ImageIcon(getClass().getClassLoader().getResource("icons/multimode_smaller.png")).getImage().getScaledInstance(32, 32, java.awt.Image.SCALE_SMOOTH));
iw.getActions().addAction(ActionFactory.createPopupMenuAction(new LegWidget.WidgetPopupMenu()));
this.setConstraint(iw, LayoutFactory.ConnectionWidgetLayoutAlignment.BOTTOM_LEFT, 1);
this.addChild(iw);
} else if (leg instanceof ScheduledLeg) {
setScheduledLegWidgets(leg);
ImageWidget iw = new ImageWidget(getScene());
iw.setImage(new ImageIcon(getClass().getClassLoader().getResource("icons/scheduled_smaller.png")).getImage().getScaledInstance(32, 32, java.awt.Image.SCALE_SMOOTH));
iw.getActions().addAction(ActionFactory.createPopupMenuAction(new LegWidget.WidgetPopupMenu()));
this.setConstraint(iw, LayoutFactory.ConnectionWidgetLayoutAlignment.BOTTOM_LEFT, 1);
this.addChild(iw);
} else {
setNormalLegWidgets(leg);
}
if (!(leg instanceof MultiModeLeg)) {
if (getLeg().getProcedures() != null && !getLeg().getProcedures().isEmpty()) {
procedureLabelWidget = new LabelWidget(getScene(), "Procedures: " + getLeg().getProcedures().size());
procedureLabelWidget.getActions().addAction(ActionFactory.createPopupMenuAction(new LegWidget.WidgetPopupMenu()));
this.setConstraint(procedureLabelWidget, LayoutFactory.ConnectionWidgetLayoutAlignment.BOTTOM_RIGHT, 40);
this.addChild(procedureLabelWidget);
}
if (getLeg().getEvents() != null) {
if (getLeg().getEvents().isEmpty()) {
eventLabelWidget = new LabelWidget(getScene(), "");
} else {
eventLabelWidget = new LabelWidget(getScene(), "Events: " + getLeg().getEvents().size());
}
eventLabelWidget.getActions().addAction(ActionFactory.createPopupMenuAction(new LegWidget.WidgetPopupMenu()));
this.setConstraint(eventLabelWidget, LayoutFactory.ConnectionWidgetLayoutAlignment.BOTTOM_RIGHT, 40);
this.addChild(eventLabelWidget);
}
}
}
public void updateLabels() {
procedureLabelWidget.setLabel("Procedures: " + getLeg().getProcedures().size());
if (getLeg().getEvents().isEmpty()) {
eventLabelWidget.setLabel("");
} else {
eventLabelWidget.setLabel("Events: " + getLeg().getEvents().size());
}
scene.validate();
}
/**
* Attach the normal leg widgets to this widget.
*
* @param leg
*/
private void setNormalLegWidgets(Leg leg) {
ImageWidget iw = new ImageWidget(getScene());
iw.setImage(getScaledImageFromIcon(leg.getIcon()));
iw.getActions().addAction(ActionFactory.createPopupMenuAction(new LegWidget.WidgetPopupMenu()));
this.setConstraint(iw, LayoutFactory.ConnectionWidgetLayoutAlignment.TOP_RIGHT, 10);
this.addChild(iw);
}
/**
* Attach the scheduled leg widgets to this widget.
*
* @param leg
*/
private void setScheduledLegWidgets(Leg leg) {
ScheduledLeg sl = (ScheduledLeg) leg;
setNormalLegWidgets(sl);
setNormalLegWidgets(sl.getAlternative());
}
/**
* Attach the multimode leg widgets to this widget.
*
* @param leg
*/
private void setMultiModeLegWidgets(Leg leg) {
MultiModeLeg mml = (MultiModeLeg) leg;
legs = mml.getLegs();
legs.entrySet().stream().forEach((entry) -> {
setNormalLegWidgets(entry.getKey());
});
}
@Override
public boolean drop(ChainGraphScene scene, Widget widget, Point point) {
throw new UnsupportedOperationException(LIMOResourceBundle.getString("NOT_DROPPABLE"));
}
/**
* Get the leg that belongs to this widget.
*
* @return
*/
public Leg getLeg() {
return legNode.getLookup().lookup(Leg.class);
}
protected LegWidget getLegWidget() {
return this;
}
@Override
public void delete() {
ChainGraphScene scene = (ChainGraphScene) getScene();
UndoManager undoManager = scene.getLookup().lookup(UndoManager.class);
// add a new UndoableEditEvent to the undoManager of the ChainGraphScene when the undoManager exists
if (undoManager != null) {
HubNode source = (HubNode) scene.getEdgeSource(legNode);
HubNode target = (HubNode) scene.getEdgeTarget(legNode);
undoManager.undoableEditHappened(new UndoableEditEvent(getLegWidget(), new DeleteLegWidgetUndoableEdit(getLegWidget(), source, target, scene)));
}
scene.removeEdge(legNode);
scene.disconnectLegWidget(getLegWidget());
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
}
/**
* The popup menu when right clicked on this widget.
*/
private class WidgetPopupMenu implements PopupMenuProvider {
@Override
public JPopupMenu getPopupMenu(Widget widget, Point localLocation) {
JPopupMenu popup = new JPopupMenu();
popup.add(new DeleteAction());
popup.add(new AbstractAction(LIMOResourceBundle.getString("EDIT")) {
@Override
public void actionPerformed(ActionEvent ae) {
Leg leg = getLeg();
if (leg instanceof MultiModeLeg) {
MultimodeLegWizardAction wizard = new MultimodeLegWizardAction();
wizard.setUpdate((MultiModeLeg) leg);
wizard.actionPerformed(ae);
} else if (leg instanceof ScheduledLeg) {
ScheduledLegWizardAction wizard = new ScheduledLegWizardAction();
wizard.setUpdate((ScheduledLeg) leg);
wizard.actionPerformed(ae);
} else {
NormalLegWizardAction wizard = new NormalLegWizardAction();
wizard.setUpdate(leg);
wizard.actionPerformed(ae);
}
propertyChange(null);
legNode.refresh();
}
});
return popup;
}
}
}