blob: 55bcf3fc05f5e00bda0a6b0c3f2d543f25978e45 [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
******************************************************************************/
package net.sf.taverna.t2.workbench.views.graph;
import static java.awt.BorderLayout.CENTER;
import static java.awt.BorderLayout.NORTH;
import static javax.swing.Action.SHORT_DESCRIPTION;
import static javax.swing.Action.SMALL_ICON;
import static javax.swing.BoxLayout.PAGE_AXIS;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.allportIcon;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.blobIcon;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.expandNestedIcon;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.horizontalIcon;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.noportIcon;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.refreshIcon;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.verticalIcon;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomInIcon;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomOutIcon;
import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ALIGNMENT;
import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_ENABLED;
import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_SPEED;
import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.PORT_STYLE;
import static org.apache.batik.swing.svg.AbstractJSVGComponent.ALWAYS_DYNAMIC;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
import net.sf.taverna.t2.lang.observer.Observable;
import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
import net.sf.taverna.t2.ui.menu.MenuManager;
import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
import net.sf.taverna.t2.workbench.edits.EditManager;
import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent;
import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
import net.sf.taverna.t2.workbench.file.FileManager;
import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment;
import net.sf.taverna.t2.workbench.models.graph.GraphController;
import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle;
import net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphController;
import net.sf.taverna.t2.workbench.selection.SelectionManager;
import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
import net.sf.taverna.t2.workbench.selection.events.WorkflowSelectionEvent;
import net.sf.taverna.t2.workbench.ui.dndhandler.ServiceTransferHandler;
import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
import net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration;
import net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramAction;
import net.sf.taverna.t2.workbench.views.graph.menu.ZoomInAction;
import net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutAction;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.JSVGScrollPane;
import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
import org.apache.log4j.Logger;
import uk.org.taverna.commons.services.ServiceRegistry;
import uk.org.taverna.scufl2.api.container.WorkflowBundle;
import uk.org.taverna.scufl2.api.core.Workflow;
/**
* @author David Withers
* @author Alex Nenadic
* @author Tom Oinn
*/
public class GraphViewComponent extends JPanel implements UIComponentSPI {
private static final long serialVersionUID = 7404937056378331528L;
private static final Logger logger = Logger.getLogger(GraphViewComponent.class);
private Workflow workflow;
private SVGGraphController graphController;
private JPanel diagramPanel;
private Map<WorkflowBundle, Set<Workflow>> workflowsMap = new IdentityHashMap<>();
private Map<Workflow, SVGGraphController> graphControllerMap = new IdentityHashMap<>();
private Map<Workflow, JPanel> diagramPanelMap = new IdentityHashMap<>();
private Map<Workflow, Action[]> diagramActionsMap = new IdentityHashMap<>();
private Timer timer;
private CardLayout cardLayout;
private final ColourManager colourManager;
private final EditManager editManager;
private final MenuManager menuManager;
private final GraphViewConfiguration graphViewConfiguration;
private final WorkbenchConfiguration workbenchConfiguration;
private final SelectionManager selectionManager;
private final ServiceRegistry serviceRegistry;
public GraphViewComponent(ColourManager colourManager,
EditManager editManager, FileManager fileManager,
MenuManager menuManager,
GraphViewConfiguration graphViewConfiguration,
WorkbenchConfiguration workbenchConfiguration,
SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
this.colourManager = colourManager;
this.editManager = editManager;
this.menuManager = menuManager;
this.graphViewConfiguration = graphViewConfiguration;
this.workbenchConfiguration = workbenchConfiguration;
this.selectionManager = selectionManager;
this.serviceRegistry = serviceRegistry;
cardLayout = new CardLayout();
setLayout(cardLayout);
ActionListener taskPerformer = new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
if (graphController != null)
graphController.redraw();
timer.stop();
}
};
timer = new Timer(100, taskPerformer);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
if (timer.isRunning())
timer.restart();
else
timer.start();
}
});
editManager.addObserver(new EditManagerObserver());
selectionManager.addObserver(new SelectionManagerObserver());
fileManager.addObserver(new FileManagerObserver());
}
@Override
protected void finalize() throws Throwable {
if (timer != null)
timer.stop();
}
@Override
public String getName() {
return "Graph View Component";
}
@Override
public ImageIcon getIcon() {
return null;
}
@Override
public void onDisplay() {
}
@Override
public void onDispose() {
if (timer != null)
timer.stop();
}
private JPanel createDiagramPanel(Workflow workflow) {
final JPanel diagramPanel = new JPanel(new BorderLayout());
// get the default diagram settings
Alignment alignment = Alignment.valueOf(graphViewConfiguration
.getProperty(ALIGNMENT));
PortStyle portStyle = PortStyle.valueOf(graphViewConfiguration
.getProperty(PORT_STYLE));
boolean animationEnabled = Boolean.parseBoolean(graphViewConfiguration
.getProperty(ANIMATION_ENABLED));
int animationSpeed = Integer.parseInt(graphViewConfiguration
.getProperty(ANIMATION_SPEED));
// create an SVG canvas
final JSVGCanvas svgCanvas = new JSVGCanvas(null, true, false);
svgCanvas.setEnableZoomInteractor(false);
svgCanvas.setEnableRotateInteractor(false);
svgCanvas.setDocumentState(ALWAYS_DYNAMIC);
svgCanvas.setTransferHandler(new ServiceTransferHandler(editManager,
menuManager, selectionManager, serviceRegistry));
AutoScrollInteractor asi = new AutoScrollInteractor(svgCanvas);
svgCanvas.addMouseListener(asi);
svgCanvas.addMouseMotionListener(asi);
final JSVGScrollPane svgScrollPane = new MySvgScrollPane(svgCanvas);
GVTTreeRendererAdapter gvtTreeRendererAdapter = new GVTTreeRendererAdapter() {
@Override
public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
logger.info("Rendered svg");
svgScrollPane.reset();
diagramPanel.revalidate();
}
};
svgCanvas.addGVTTreeRendererListener(gvtTreeRendererAdapter);
// create a graph controller
SVGGraphController svgGraphController = new SVGGraphController(
workflow, selectionManager.getSelectedProfile(), false,
svgCanvas, alignment, portStyle, editManager, menuManager,
colourManager, workbenchConfiguration);
svgGraphController.setDataflowSelectionModel(selectionManager
.getDataflowSelectionModel(workflow.getParent()));
svgGraphController.setAnimationSpeed(animationEnabled ? animationSpeed
: 0);
graphControllerMap.put(workflow, svgGraphController);
// Toolbar with actions related to graph
JToolBar graphActionsToolbar = graphActionsToolbar(workflow,
svgGraphController, svgCanvas, alignment, portStyle);
graphActionsToolbar.setAlignmentX(LEFT_ALIGNMENT);
graphActionsToolbar.setFloatable(false);
// Panel to hold the toolbars
JPanel toolbarPanel = new JPanel();
toolbarPanel.setLayout(new BoxLayout(toolbarPanel, PAGE_AXIS));
toolbarPanel.add(graphActionsToolbar);
diagramPanel.add(toolbarPanel, NORTH);
diagramPanel.add(svgScrollPane, CENTER);
// JTextField workflowHierarchy = new JTextField(workflow.getName());
// diagramPanel.add(workflowHierarchy, BorderLayout.SOUTH);
return diagramPanel;
}
@SuppressWarnings("serial")
private JToolBar graphActionsToolbar(Workflow workflow,
final SVGGraphController graphController, JSVGCanvas svgCanvas,
Alignment alignment, PortStyle portStyle) {
JToolBar toolBar = new JToolBar();
JButton resetDiagramButton = new JButton();
resetDiagramButton.setBorder(new EmptyBorder(0, 2, 0, 2));
JButton zoomInButton = new JButton();
zoomInButton.setBorder(new EmptyBorder(0, 2, 0, 2));
JButton zoomOutButton = new JButton();
zoomOutButton.setBorder(new EmptyBorder(0, 2, 0, 2));
Action resetDiagramAction = svgCanvas.new ResetTransformAction();
ResetDiagramAction.setDesignAction(resetDiagramAction);
resetDiagramAction.putValue(SHORT_DESCRIPTION, "Reset Diagram");
resetDiagramAction.putValue(SMALL_ICON, refreshIcon);
resetDiagramButton.setAction(resetDiagramAction);
Action zoomInAction = svgCanvas.new ZoomAction(1.2);
ZoomInAction.setDesignAction(zoomInAction);
zoomInAction.putValue(SHORT_DESCRIPTION, "Zoom In");
zoomInAction.putValue(SMALL_ICON, zoomInIcon);
zoomInButton.setAction(zoomInAction);
Action zoomOutAction = svgCanvas.new ZoomAction(1 / 1.2);
ZoomOutAction.setDesignAction(zoomOutAction);
zoomOutAction.putValue(SHORT_DESCRIPTION, "Zoom Out");
zoomOutAction.putValue(SMALL_ICON, zoomOutIcon);
zoomOutButton.setAction(zoomOutAction);
diagramActionsMap.put(workflow, new Action[] { resetDiagramAction,
zoomInAction, zoomOutAction });
toolBar.add(resetDiagramButton);
toolBar.add(zoomInButton);
toolBar.add(zoomOutButton);
toolBar.addSeparator();
ButtonGroup nodeTypeGroup = new ButtonGroup();
JToggleButton noPorts = new JToggleButton();
JToggleButton allPorts = new JToggleButton();
JToggleButton blobs = new JToggleButton();
nodeTypeGroup.add(noPorts);
nodeTypeGroup.add(allPorts);
nodeTypeGroup.add(blobs);
if (portStyle.equals(PortStyle.NONE))
noPorts.setSelected(true);
else if (portStyle.equals(PortStyle.ALL))
allPorts.setSelected(true);
else
blobs.setSelected(true);
noPorts.setAction(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent arg0) {
graphController.setPortStyle(PortStyle.NONE);
graphController.redraw();
}
});
noPorts.getAction().putValue(SHORT_DESCRIPTION,
"Display no service ports");
noPorts.getAction().putValue(SMALL_ICON, noportIcon);
noPorts.setFocusPainted(false);
allPorts.setAction(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent arg0) {
graphController.setPortStyle(PortStyle.ALL);
graphController.redraw();
}
});
allPorts.getAction().putValue(SHORT_DESCRIPTION,
"Display all service ports");
allPorts.getAction().putValue(SMALL_ICON, allportIcon);
allPorts.setFocusPainted(false);
blobs.setAction(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent arg0) {
graphController.setPortStyle(PortStyle.BLOB);
graphController.redraw();
}
});
blobs.getAction().putValue(SHORT_DESCRIPTION,
"Display services as circles");
blobs.getAction().putValue(SMALL_ICON, blobIcon);
blobs.setFocusPainted(false);
toolBar.add(noPorts);
toolBar.add(allPorts);
toolBar.add(blobs);
toolBar.addSeparator();
ButtonGroup alignmentGroup = new ButtonGroup();
JToggleButton vertical = new JToggleButton();
JToggleButton horizontal = new JToggleButton();
alignmentGroup.add(vertical);
alignmentGroup.add(horizontal);
if (alignment.equals(Alignment.VERTICAL)) {
vertical.setSelected(true);
} else {
horizontal.setSelected(true);
}
vertical.setAction(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent arg0) {
graphController.setAlignment(Alignment.VERTICAL);
graphController.redraw();
}
});
vertical.getAction().putValue(SHORT_DESCRIPTION,
"Align services vertically");
vertical.getAction().putValue(SMALL_ICON, verticalIcon);
vertical.setFocusPainted(false);
horizontal.setAction(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent arg0) {
graphController.setAlignment(Alignment.HORIZONTAL);
graphController.redraw();
}
});
horizontal.getAction().putValue(SHORT_DESCRIPTION,
"Align services horizontally");
horizontal.getAction().putValue(SMALL_ICON, horizontalIcon);
horizontal.setFocusPainted(false);
toolBar.add(vertical);
toolBar.add(horizontal);
toolBar.addSeparator();
JToggleButton expandNested = new JToggleButton();
expandNested.setSelected(true);
expandNested.setAction(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent arg0) {
graphController.setExpandNestedDataflows(!graphController
.expandNestedDataflows());
graphController.redraw();
}
});
expandNested.getAction().putValue(SHORT_DESCRIPTION,
"Expand Nested Workflows");
expandNested.getAction().putValue(SMALL_ICON, expandNestedIcon);
expandNested.setFocusPainted(false);
toolBar.add(expandNested);
return toolBar;
}
/**
* Sets the Workflow to display in the graph view.
*
* @param workflow
*/
private void setWorkflow(Workflow workflow) {
this.workflow = workflow;
if (!diagramPanelMap.containsKey(workflow))
addWorkflow(workflow);
graphController = graphControllerMap.get(workflow);
diagramPanel = diagramPanelMap.get(workflow);
Action[] actions = diagramActionsMap.get(workflow);
if (actions != null && actions.length == 3) {
ResetDiagramAction.setDesignAction(actions[0]);
ZoomInAction.setDesignAction(actions[1]);
ZoomOutAction.setDesignAction(actions[2]);
}
cardLayout.show(this, String.valueOf(diagramPanel.hashCode()));
graphController.redraw();
}
private void addWorkflow(Workflow workflow) {
JPanel newDiagramPanel = createDiagramPanel(workflow);
add(newDiagramPanel, String.valueOf(newDiagramPanel.hashCode()));
diagramPanelMap.put(workflow, newDiagramPanel);
if (!workflowsMap.containsKey(workflow.getParent()))
workflowsMap.put(workflow.getParent(), new HashSet<Workflow>());
workflowsMap.get(workflow.getParent()).add(workflow);
}
private void removeWorkflow(Workflow workflow) {
JPanel panel = diagramPanelMap.remove(workflow);
if (panel != null)
remove(panel);
SVGGraphController removedController = graphControllerMap.remove(workflow);
if (removedController != null)
removedController.shutdown();
diagramActionsMap.remove(workflow);
Set<Workflow> workflows = workflowsMap.get(workflow.getParent());
if (workflows != null)
workflows.remove(workflow);
}
public GraphController getGraphController(Workflow workflow) {
return graphControllerMap.get(workflow);
}
private class EditManagerObserver extends
SwingAwareObserver<EditManagerEvent> {
@Override
public void notifySwing(Observable<EditManagerEvent> sender,
EditManagerEvent message) {
if (!(message instanceof AbstractDataflowEditEvent))
return;
AbstractDataflowEditEvent dataflowEditEvent = (AbstractDataflowEditEvent) message;
if (dataflowEditEvent.getDataFlow() != workflow.getParent())
return;
boolean animationEnabled = Boolean
.parseBoolean(graphViewConfiguration
.getProperty(ANIMATION_ENABLED));
int animationSpeed = (animationEnabled ? Integer
.parseInt(graphViewConfiguration
.getProperty(ANIMATION_SPEED)) : 0);
boolean animationSettingChanged = (animationEnabled != (graphController
.getAnimationSpeed() != 0));
if (graphController.isDotMissing() || animationSettingChanged) {
removeWorkflow(workflow);
setWorkflow(workflow);
} else {
if (animationSpeed != graphController.getAnimationSpeed())
graphController.setAnimationSpeed(animationSpeed);
graphController.redraw();
}
}
}
private class FileManagerObserver extends SwingAwareObserver<FileManagerEvent> {
@Override
public void notifySwing(Observable<FileManagerEvent> sender, final FileManagerEvent message) {
if (!(message instanceof ClosedDataflowEvent))
return;
ClosedDataflowEvent closedDataflowEvent = (ClosedDataflowEvent) message;
WorkflowBundle workflowBundle = closedDataflowEvent.getDataflow();
if (workflowsMap.containsKey(workflowBundle))
for (Workflow workflow : workflowsMap.remove(workflowBundle))
removeWorkflow(workflow);
}
}
private class SelectionManagerObserver extends
SwingAwareObserver<SelectionManagerEvent> {
@Override
public void notifySwing(Observable<SelectionManagerEvent> sender,
SelectionManagerEvent message) {
if (message instanceof WorkflowSelectionEvent)
setWorkflow(selectionManager.getSelectedWorkflow());
else if (message instanceof WorkflowBundleSelectionEvent)
setWorkflow(selectionManager.getSelectedWorkflow());
}
}
private class MySvgScrollPane extends JSVGScrollPane {
private static final long serialVersionUID = -1539947450704269879L;
public MySvgScrollPane(JSVGCanvas canvas) {
super(canvas);
}
@Override
public void reset() {
super.resizeScrollBars();
super.reset();
}
}
}