blob: 6d80004391c48e3c1dc41802774199bb23efd1e3 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
******************************************************************************/
/**
* This file is a component of the Taverna project,
* and is licensed under the GNU LGPL.
* Copyright Tom Oinn, EMBL-EBI
*/
package net.sf.taverna.t2.workbench.iterationstrategy.editor;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
import net.sf.taverna.t2.workbench.iterationstrategy.IterationStrategyIcons;
import org.apache.taverna.workflowmodel.processor.iteration.CrossProduct;
import org.apache.taverna.workflowmodel.processor.iteration.DotProduct;
import org.apache.taverna.workflowmodel.processor.iteration.IterationStrategy;
import org.apache.taverna.workflowmodel.processor.iteration.IterationStrategyNode;
import org.apache.taverna.workflowmodel.processor.iteration.TerminalNode;
import org.apache.log4j.Logger;
/**
* A control panel for the iteration tree editor allowing the user to manipulate
* the tree, removing and adding nodes into the tree based on the context.
*
* @author Tom Oinn
* @author Stian Soiland-Reyes
*
*/
@SuppressWarnings("serial")
public class IterationStrategyEditorControl extends JPanel {
protected static Set<IterationStrategyNode> descendentsOfNode(
IterationStrategyNode node) {
Set<IterationStrategyNode> descendants = new HashSet<IterationStrategyNode>();
Set<IterationStrategyNode> nodesToVisit = new HashSet<IterationStrategyNode>();
Set<IterationStrategyNode> visitedNodes = new HashSet<IterationStrategyNode>();
// Note: Not added to descendants
nodesToVisit.add(node);
while (!nodesToVisit.isEmpty()) {
// pick the first one
IterationStrategyNode visiting = nodesToVisit.iterator().next();
visitedNodes.add(visiting);
nodesToVisit.remove(visiting);
// Find new and interesting children
List<IterationStrategyNode> children = visiting.getChildren();
Set<IterationStrategyNode> newNodes = new HashSet<IterationStrategyNode>(
children);
newNodes.removeAll(visitedNodes);
descendants.addAll(newNodes);
nodesToVisit.addAll(newNodes);
}
return descendants;
}
private static Logger logger = Logger
.getLogger(IterationStrategyEditorControl.class);
private IterationStrategyNode selectedNode = null;
private IterationStrategyTree tree;
protected AddCrossAction addCross = new AddCrossAction();
protected AddDotAction addDot = new AddDotAction();
protected ChangeAction change = new ChangeAction();
protected NormalizeAction normalize = new NormalizeAction();
protected RemoveAction remove = new RemoveAction();
protected MoveUpAction moveUp = new MoveUpAction();
//private static final int ICON_SIZE = 15;
protected ImageIcon arrowUpIcon = WorkbenchIcons.upArrowIcon;
protected ImageIcon arrowDownIcon = WorkbenchIcons.downArrowIcon;
//protected ImageIcon arrowLeft = WorkbenchIcons.leftArrowIcon;
//protected ImageIcon arrowRight = WorkbenchIcons.rightArrowIcon;
protected ImageIcon normalizeIcon = WorkbenchIcons.normalizeIcon;
private final IterationStrategy strategy;
/**
* Create a new panel from the supplied iteration strategy
*/
public IterationStrategyEditorControl(IterationStrategy strategy) {
this.strategy = strategy;
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
// Create the components
tree = new IterationStrategyEditor(strategy);
JButton addCrossButton = new JButton(addCross);
addCrossButton.setHorizontalAlignment(SwingConstants.LEFT);
JButton addDotButton = new JButton(addDot);
addDotButton.setHorizontalAlignment(SwingConstants.LEFT);
JButton normalizeButton = new JButton(normalize);
normalizeButton.setHorizontalAlignment(SwingConstants.LEFT);
normalizeButton.setIcon(normalizeIcon);
JButton removeButton = new JButton(remove);
removeButton.setHorizontalAlignment(SwingConstants.LEFT);
JButton changeButton = new JButton(change);
changeButton.setHorizontalAlignment(SwingConstants.LEFT);
JButton moveUpButton = new JButton(moveUp);
moveUpButton.setIcon(arrowUpIcon);
moveUpButton.setHorizontalAlignment(SwingConstants.LEFT);
// Set the default enabled state to off on all buttons other than the
// normalizeButton
// one.
disableButtons();
// Create a layout with the tree on the right and the buttons in a grid
// layout on the left
JToolBar toolbar = new JToolBar();
toolbar.setFloatable(false);
toolbar.setRollover(true);
// toolbar.setLayout(new GridLayout(2,2));
toolbar.add(normalizeButton);
toolbar.add(addCrossButton);
toolbar.add(addDotButton);
toolbar.add(removeButton);
toolbar.add(changeButton);
toolbar.add(moveUpButton);
toolbar.setAlignmentX(LEFT_ALIGNMENT);
// Listen to tree selection events and enable buttons appropriately
tree.addTreeSelectionListener(new ButtonEnabler());
// Add components to the control panel
add(toolbar);
JScrollPane treePane = new JScrollPane(tree);
//treePane.setPreferredSize(new Dimension(0, 0));
add(treePane);
}
public void setIterationStrategy(IterationStrategy iterationStrategy) {
tree.setIterationStrategy(iterationStrategy);
disableButtons();
selectNode(null);
}
private void disableButtons() {
remove.setEnabled(false);
addCross.setEnabled(false);
addDot.setEnabled(false);
change.setEnabled(false);
}
private IterationStrategyNode findRoot() {
IterationStrategyNode root = (IterationStrategyNode) tree.getModel()
.getRoot();
if (root.getChildCount() > 0) {
return root.getChildAt(0);
}
return root;
}
protected void selectNode(TreeNode newNode) {
DefaultTreeModel model = tree.getModel();
if (newNode == null) {
newNode = (TreeNode) model.getRoot();
}
TreeNode[] pathToRoot = model.getPathToRoot(newNode);
tree.setSelectionPath(new TreePath(pathToRoot));
}
/**
* Add a cross product node as a child of the selected node
*/
protected class AddCrossAction extends AbstractAction {
public AddCrossAction() {
super("Add Cross", IterationStrategyIcons.joinIteratorIcon);
}
public void actionPerformed(ActionEvent e) {
CrossProduct newNode = new CrossProduct();
newNode.setParent(selectedNode);
tree.refreshModel();
}
}
/**
* Add a dot product node as a child of the selected node
*
* @author Stian Soiland-Reyes
*
*/
protected class AddDotAction extends AbstractAction {
public AddDotAction() {
super("Add Dot", IterationStrategyIcons.lockStepIteratorIcon);
}
public void actionPerformed(ActionEvent e) {
DotProduct newNode = new DotProduct();
newNode.setParent(selectedNode);
tree.refreshModel();
}
}
protected class ButtonEnabler implements TreeSelectionListener {
public void valueChanged(TreeSelectionEvent e) {
TreePath selectedPath = e.getPath();
IterationStrategyNode selectedObject = (IterationStrategyNode) selectedPath
.getLastPathComponent();
selectedNode = selectedObject;
if (selectedObject instanceof CrossProduct
|| selectedObject instanceof DotProduct) {
if ((selectedObject.getParent() == null) || (selectedObject.getParent() instanceof TerminalNode)) {
remove.setEnabled(false);
} else {
remove.setEnabled(true);
}
if (selectedObject instanceof CrossProduct) {
change.putValue(Action.NAME, "Change to Dot Product");
change.putValue(Action.SMALL_ICON,
IterationStrategyIcons.lockStepIteratorIcon);
} else {
change.putValue(Action.NAME, "Change to Cross Product");
change.putValue(Action.SMALL_ICON,
IterationStrategyIcons.joinIteratorIcon);
}
addCross.setEnabled(true);
addDot.setEnabled(true);
change.setEnabled(true);
} else {
// Top- or leaf node
remove.setEnabled(false);
addCross.setEnabled(false);
addDot.setEnabled(false);
change.setEnabled(false);
}
}
}
/**
* Add a cross product node as a child of the selected node
*/
protected class ChangeAction extends AbstractAction {
public ChangeAction() {
super("Switch to...", IterationStrategyIcons.joinIteratorIcon);
}
public void actionPerformed(ActionEvent e) {
IterationStrategyNode newNode;
if (selectedNode instanceof CrossProduct) {
newNode = new DotProduct();
} else {
newNode = new CrossProduct();
}
List<IterationStrategyNode> children = new ArrayList<IterationStrategyNode>(
selectedNode.getChildren());
for (IterationStrategyNode child : children) {
child.setParent(newNode);
}
DefaultTreeModel model = tree.getModel();
if (selectedNode.getParent() == null) {
model.setRoot(newNode);
tree.refreshModel();
newNode.setParent(null);
} else {
IterationStrategyNode parent = selectedNode.getParent();
int index = parent.getIndex(selectedNode);
selectedNode.setParent(null);
parent.insert(newNode, index);
tree.refreshModel();
}
selectNode(newNode);
}
}
/**
* Normalize the tree when the button is pressed
*
*/
protected class NormalizeAction extends AbstractAction {
public NormalizeAction() {
super("Normalize", normalizeIcon);
}
public void actionPerformed(ActionEvent e) {
strategy.normalize();
// Expand all the nodes in the tree
//DefaultTreeModel model = tree.getModel();
tree.refreshModel();
}
}
/**
* Remove the selected node, moving any descendant leaf nodes to the parent
* to prevent them getting lost
*/
protected class RemoveAction extends AbstractAction {
public RemoveAction() {
super("Remove node", WorkbenchIcons.deleteIcon);
}
public void actionPerformed(ActionEvent e) {
IterationStrategyNode nodeToBeRemoved = selectedNode;
//DefaultTreeModel model = tree.getModel();
// Now removeButton the candidate nodes from their parents and
// put them back into the root node
IterationStrategyNode root = findRoot();
if (root == selectedNode) {
return;
}
IterationStrategyNode oldParent = nodeToBeRemoved.getParent();
for (IterationStrategyNode nodeToMove : descendentsOfNode(nodeToBeRemoved)) {
nodeToMove.setParent(oldParent);
}
nodeToBeRemoved.setParent(null);
tree.refreshModel();
// Disable the various buttons, as the current selection
// is now invalid.
remove.setEnabled(false);
addCross.setEnabled(false);
addDot.setEnabled(false);
change.setEnabled(false);
selectNode(oldParent);
}
}
protected class MoveUpAction extends AbstractAction {
public MoveUpAction() {
super("Move up", arrowUpIcon);
}
public void actionPerformed(ActionEvent e) {
//DefaultTreeModel model = tree.getModel();
IterationStrategyNode aboveNode = aboveSelectedNode();
if ((aboveNode == null) || ((aboveNode instanceof TerminalNode) && (aboveNode.getChildCount() > 0))) {
logger.warn("Can't move above top");
return;
}
IterationStrategyNode selectedParent = selectedNode.getParent();
IterationStrategyNode aboveParent = aboveNode.getParent();
if (selectedParent != null && selectedParent.equals(aboveParent)) {
// Siblings
int aboveChildIndex = selectedParent.getIndex(aboveNode);
selectedParent.insert(selectedNode, aboveChildIndex);
tree.refreshModel();
selectNode(selectedNode);
} else if (aboveNode.equals(selectedParent)) {
if (aboveParent instanceof TerminalNode
&& selectedNode.getAllowsChildren()) {
aboveNode.setParent(selectedNode);
selectedNode.setParent(aboveParent);
tree.refreshModel();
selectNode(selectedNode);
} else if (!(aboveParent instanceof TerminalNode)){
int aboveChildIndex = aboveParent.getIndex(aboveNode);
aboveParent.insert(selectedNode, aboveChildIndex);
tree.refreshModel();
selectNode(selectedNode);
}
} else {
}
}
}
protected IterationStrategyNode belowSelectedNode() {
return offsetFromSelectedNode(1);
}
protected IterationStrategyNode offsetFromSelectedNode(int offset) {
int currentRow = tree.getRowForPath(tree.getSelectionPath());
int offsetRow = currentRow + offset;
TreePath offsetPath = tree.getPathForRow(offsetRow);
if (offsetPath == null) {
return null;
}
IterationStrategyNode offsetNode = (IterationStrategyNode) offsetPath
.getLastPathComponent();
if (offsetNode == tree.getModel().getRoot()) {
return null;
}
return offsetNode;
}
protected IterationStrategyNode aboveSelectedNode() {
return offsetFromSelectedNode(-1);
}
}