/**
* Copyright (C) 2008-2010 Daniel Senff
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package de.danielsenff.imageflow.controller;
import java.awt.Point;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;
import javax.swing.JOptionPane;
import javax.swing.event.UndoableEditListener;
import visualap.GPanel;
import visualap.Node;
import de.danielsenff.imageflow.ImageFlow;
import de.danielsenff.imageflow.ImageFlowView;
import de.danielsenff.imageflow.gui.Dashboard;
import de.danielsenff.imageflow.gui.GraphPanel;
import de.danielsenff.imageflow.imagej.MacroFlowRunner;
import de.danielsenff.imageflow.models.Model;
import de.danielsenff.imageflow.models.NodeListener;
import de.danielsenff.imageflow.models.SelectionList;
import de.danielsenff.imageflow.models.connection.Connection;
import de.danielsenff.imageflow.models.connection.ConnectionList;
import de.danielsenff.imageflow.models.connection.Input;
import de.danielsenff.imageflow.models.connection.Output;
import de.danielsenff.imageflow.models.connection.ProxyInput;
import de.danielsenff.imageflow.models.connection.ProxyOutput;
import de.danielsenff.imageflow.models.datatype.ImageDataType;
import de.danielsenff.imageflow.models.delegates.UnitDelegate;
import de.danielsenff.imageflow.models.parameter.ParamChangeListener;
import de.danielsenff.imageflow.models.parameter.Parameter;
import de.danielsenff.imageflow.models.unit.CommentNode;
import de.danielsenff.imageflow.models.unit.GroupUnitElement;
import de.danielsenff.imageflow.models.unit.UnitElement;
import de.danielsenff.imageflow.models.unit.UnitFactory;
import de.danielsenff.imageflow.models.unit.UnitList;
/**
* Controller for Workflows.
* @author Daniel Senff
*
*/
public class GraphController{
private UnitList nodes;
/**
* List which stores copied Nodes.
*/
protected ArrayList<Node> copyNodesList;
/**
* List of selected units
*/
private SelectionList selections;
private UndoableEditListener listener;
private ExecuteWorkflowListener executionListener;
/**
*
*/
public GraphController() {
this.nodes = new UnitList();
this.copyNodesList = new ArrayList<Node>();
this.selections = new SelectionList();
this.executionListener = new ExecuteWorkflowListener(this);
}
/**
* @return the unitElements
*/
public UnitList getUnitElements() {
return this.nodes;
}
/**
* Generates the executable Macro based on the current graph.
* @param extendedMacro defines, if callback functions are put into macro code
* @param silent defines, if any results should be displayed after execution, or if the workflow should be executed silently
* @return
*/
public String generateMacro(boolean extendedMacro, boolean silent) {
final MacroFlowRunner macroFlowRunner = new MacroFlowRunner(this.nodes);
return macroFlowRunner.generateMacro(extendedMacro, silent);
}
/**
* Returns current the {@link ConnectionList}
* @return
*/
public ConnectionList getConnections() {
return this.nodes.getConnections();
}
/**
* Selections
* @return
*/
public SelectionList getSelections() {
return this.selections;
}
/**
* Get the List of copied {@link Node};
* @return
*/
public ArrayList<Node> getCopyNodesList() {
return copyNodesList;
}
/**
* Set the UndoableEditListener.
* @param l
*/
public void addUndoableEditListener(UndoableEditListener l) {
listener = l; // Should ideally throw an exception if listener != null
}
/**
* Remove the UndoableEditListener.
* @param l
*/
public void removeUndoableEditListener(UndoableEditListener l) {
listener = null;
}
/**
* Adds the given {@link Node} to the workspace.
* @param node
* @return
*/
public Node addNode(Node node) {
final ImageFlowView ifView = ((ImageFlowView)ImageFlow.getApplication().getMainView());
final GraphPanel graphPanel = ifView.getGraphPanel();
if (node instanceof UnitElement) {
((UnitElement) node).addModelListener(new NodeListener(graphPanel, ifView));
((UnitElement) node).addParamChangeListerToAllParameters(executionListener);
}
getUnitElements().add(node);
return node;
}
/**
* Creates a new Node based on the given UnitDelegate and
* adds it at the given location to the workspace.
* @param delegate
* @param point
* @return
*/
public Node addNode(UnitDelegate delegate, Point point) {
UnitElement unit = delegate.buildUnit(point);
return this.addNode(unit);
}
/**
* Removes the {@link UnitElement} from the unitList and its Connections.
* @param node
* @return
*/
public boolean removeNode(final Node node) {
// TODO remove from dashboard
return nodes.remove(node);
}
/**
* Ungroup the contents of a GroupUnit
* @param group
*/
public void ungroup(final GroupUnitElement group) {
ungroup(group, getUnitElements());
}
/**
* @param group
* @param units
*/
public static void ungroup(final GroupUnitElement group, final UnitList units) {
int deltaX = group.getOrigin().x - 25;
int deltaY = group.getOrigin().y - 25;
for (Node node : group.getNodes()) {
int x = node.getOrigin().x, y = node.getOrigin().y;
node.getOrigin().setLocation(x+deltaX, y+deltaY);
for (Input input : ((UnitElement)node).getInputs()) {
input.setLocked(false);
}
for (Output output : ((UnitElement)node).getOutputs()) {
output.setLocked(false);
}
}
units.addAll(group.getNodes());
ConnectionList connections = units.getConnections();
/*
* reconnect inputs
*/
for (Input input : group.getInputs()) {
if(input instanceof ProxyInput) {
ProxyInput pInput = (ProxyInput)input;
if(pInput.isConnected()) {
Output connectedOutput = pInput.getFromOutput();
Input originalInput = pInput.getEmbeddedInput();
Connection connection = new Connection(connectedOutput, originalInput);
connections.add(connection);
}
}
}
/*
* reconnect outputs
*/
Collection<Connection> tmpConn = new Vector<Connection>();
for (Output output : group.getOutputs()) {
if(output instanceof ProxyOutput) {
ProxyOutput pOutput = (ProxyOutput)output;
if(pOutput.isConnected()) {
Output originalOutput = pOutput.getEmbeddedOutput();
if(originalOutput.getDataType() instanceof ImageDataType) {
ImageDataType imageDataType = (ImageDataType)originalOutput.getDataType();
imageDataType.setParentUnitElement(originalOutput.getParent());
imageDataType.setParentPin(originalOutput);
}
for (Connection connection : pOutput.getConnections()) {
Connection newConn = new Connection(originalOutput, connection.getInput());
tmpConn.add(newConn);
}
}
}
}
// write connections into actual connectionlist
for (Connection connection : tmpConn) {
connections.add(connection);
}
/*
* reconnect connection within the group
*/
for (Connection connection : group.getInternalConnections()) {
connections.add(connection);
}
units.remove(group);
}
public void group() throws Exception {
if(!getSelections().isEmpty()) {
GroupUnitElement group = new GroupUnitElement(new Point(34, 250), "Group");
group.putUnits(getSelections(), getUnitElements());
getUnitElements().add(group);
selections.clear();
selections.add(group);
}
/*if (listener != null) {
listener.undoableEditHappened(new UndoableEditEvent(this, new UndoableEdit() {
public boolean addEdit(UndoableEdit anEdit) {
// TODO Auto-generated method stub
return false;
}
public boolean canRedo() { return true; }
public boolean canUndo() { return true; }
public void die() {}
public String getPresentationName() {
return null;
}
public String getRedoPresentationName() {
return "Regroup";
}
public String getUndoPresentationName() {
return "Ungroup";
}
public boolean isSignificant() { return true; }
public void redo() throws CannotRedoException {
// TODO Auto-generated method stub
}
public boolean replaceEdit(UndoableEdit anEdit) {
// TODO Auto-generated method stub
return false;
}
public void undo() throws CannotUndoException {
ungroup(group);
}
}));
}*/
}
/**
* Reads the contents of a flow-XML-document.
* @param url The document to load.
*/
public void read(final URL url) {
WorkflowXMLBuilder workflowbuilder = new WorkflowXMLBuilder(this);
workflowbuilder.read(url);
}
/**
* Write the workflow to a XML-file
* @param file
* @throws IOException
*/
public void write(final File file) throws IOException {
WorkflowXMLBuilder workflowbuilder = new WorkflowXMLBuilder(this);
workflowbuilder.write(file);
}
/**
* TODO remove this, update unit tests
*/
public void setupExample1() {
////////////////////////////////////////////////////////
// setup of units
////////////////////////////////////////////////////////
DelegatesController delegatesController = DelegatesController.getInstance();
final UnitElement sourceUnit = delegatesController.getDelegate("Image Source").buildUnit(new Point(30, 100));
final UnitElement blurUnit = delegatesController.getDelegate("Gaussian Blur").buildUnit(new Point(180, 50));
final UnitElement mergeUnit = delegatesController.getDelegate("Image Calculator").buildUnit(new Point(320, 100));
final UnitElement noiseUnit = delegatesController.getDelegate("Add Noise").buildUnit(new Point(450, 100));
noiseUnit.setDisplay(true);
CommentNode comment = UnitFactory.createComment("my usual example", new Point(30, 40));
// some mixing, so they are not in order
nodes.add(noiseUnit);
nodes.add(blurUnit);
nodes.add(sourceUnit);
nodes.add(mergeUnit);
nodes.add(comment);
////////////////////////////////////////////////////////
// setup the connections
////////////////////////////////////////////////////////
// add six connections
// the conn is established on adding
// fromUnit, fromOutputNumber, toUnit, toInputNumber
Connection con;
con = new Connection(sourceUnit,1,blurUnit,1);
nodes.addConnection(con);
con = new Connection(blurUnit,1,mergeUnit,1);
nodes.addConnection(con);
con = new Connection(sourceUnit,1,mergeUnit,2);
nodes.addConnection(con);
con = new Connection(mergeUnit,1,noiseUnit,1);
nodes.addConnection(con);
}
private void showExampleLoadError(final Exception e) {
final int type = JOptionPane.ERROR_MESSAGE;
JOptionPane.showMessageDialog(
ImageFlow.getApplication().getMainFrame(),
"An error occured while loading the example!", "Could not load example", type);
}
/**
* Given the node is part of the workflow, return the subgraph
* hat fulfills all dependencies of this node.
* @param node
*/
public UnitList getSubgraph(UnitElement unit) {
GraphController subgraph = new GraphController();
UnitList subgraphList = new UnitList();
UnitElement dependentUnit;
try {
recursiveAddUnitDependencies(unit, subgraphList);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return subgraphList;
}
private void recursiveAddUnitDependencies(UnitElement unit,
UnitList subgraphList) throws CloneNotSupportedException {
UnitElement dependentUnit;
if (unit.hasRequiredInputsConnected()) {
subgraphList.add(unit.clone());
for (Input input : unit.getInputs()) {
dependentUnit = input.getFromUnit();
recursiveAddUnitDependencies(dependentUnit, subgraphList);
}
} else {
System.err.println("subgraph not complete");
}
}
/**
* Sets the data object as the result output for the Node and Output with the given ID
* @param nodeID
* @param outputId
* @param data
*/
public void setOutputData(int nodeID, int outputId, Object data) {
// get the Node and Output object with the given IDs
Node node = getUnitElements().getNodeByID(nodeID);
if (node instanceof UnitElement) {
System.out.println(node);
Output output = ((UnitElement)node).getOutput(outputId-1);
// write the data into the output object
output.setOutputObject(data);
}
}
private Dashboard dashboardPanel = null;
public Dashboard getDashboard() {
return this.dashboardPanel;
}
public void setDashboard(Dashboard dashboard) {
this.dashboardPanel = dashboard;
}
public boolean addWidget(UnitElement selectedElement) {
if (this.dashboardPanel != null) {
this.dashboardPanel.addWidget(selectedElement);
return true;
} else
return false;
}
public boolean addWidget(UnitElement selectedElement, Point location) {
if (this.dashboardPanel != null) {
this.dashboardPanel.addWidget(selectedElement, location);
return true;
} else
return false;
}
public boolean addPreviewWidget(UnitElement selectedElement) {
if (this.dashboardPanel != null) {
this.dashboardPanel.addPreviewWidget(selectedElement);
return true;
} else
return false;
}
public boolean addPreviewWidget(UnitElement selectedElement, Point location) {
if (this.dashboardPanel != null) {
this.dashboardPanel.addPreviewWidget(selectedElement, location);
return true;
} else
return false;
}
}