blob: 49f948aa5f1dddd4f8ecf69a6eb3fe76b8a8ecdb [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.menu;
import static javax.swing.JFileChooser.APPROVE_OPTION;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static javax.swing.JOptionPane.WARNING_MESSAGE;
import static javax.swing.JOptionPane.YES_NO_OPTION;
import static javax.swing.JOptionPane.showConfirmDialog;
import static javax.swing.JOptionPane.showMessageDialog;
import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramSaveMenuSection.DIAGRAM_SAVE_MENU_SECTION;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.net.URL;
import java.util.prefs.Preferences;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import net.sf.taverna.t2.lang.io.StreamCopier;
import net.sf.taverna.t2.lang.io.StreamDevourer;
import net.sf.taverna.t2.lang.observer.Observable;
import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
import net.sf.taverna.t2.lang.ui.ExtensionFileFilter;
import net.sf.taverna.t2.ui.menu.AbstractMenuCustom;
import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
import net.sf.taverna.t2.workbench.file.FileManager;
import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
import net.sf.taverna.t2.workbench.models.graph.DotWriter;
import net.sf.taverna.t2.workbench.models.graph.GraphController;
import net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil;
import net.sf.taverna.t2.workbench.selection.SelectionManager;
import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent;
import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
import net.sf.taverna.t2.workbench.views.graph.GraphViewComponent;
import org.apache.log4j.Logger;
import uk.org.taverna.scufl2.api.core.Workflow;
/**
* An action that saves graph diagram image.
*
* @author Alex Nenadic
* @author Tom Oinn
*/
public class SaveGraphImageSubMenu extends AbstractMenuCustom {
private static final Logger logger = Logger
.getLogger(SaveGraphImageSubMenu.class);
private static final String[] saveTypes = { "dot", "png", "svg", "ps",
"ps2" };
private static final String[] saveExtensions = { "dot", "png", "svg", "ps",
"ps" };
private static final String[] saveTypeNames = { "dot text", "PNG bitmap",
"scalable vector graphics", "postscript", "postscript for PDF" };
public static final URI SAVE_GRAPH_IMAGE_MENU_URI = URI
.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuSaveGraphImage");
private JMenu saveDiagramMenu;
private FileManager fileManager;
private SelectionManager selectionManager;
private WorkbenchConfiguration workbenchConfiguration;
private GraphViewComponent graphViewComponent;
public SaveGraphImageSubMenu() {
super(DIAGRAM_SAVE_MENU_SECTION, 70, SAVE_GRAPH_IMAGE_MENU_URI);
}
@Override
protected Component createCustomComponent() {
saveDiagramMenu = new JMenu("Export diagram");
saveDiagramMenu
.setToolTipText("Open this menu to export the diagram in various formats");
for (int i = 0; i < saveTypes.length; i++) {
String type = saveTypes[i];
String extension = saveExtensions[i];
ImageIcon icon = new ImageIcon(
WorkbenchIcons.class.getResource("graph/saveAs"
+ type.toUpperCase() + ".png"));
JMenuItem item = new JMenuItem(new DotInvoker("Export as "
+ saveTypeNames[i], icon, type, extension));
saveDiagramMenu.add(item);
}
return saveDiagramMenu;
}
@SuppressWarnings("serial")
class DotInvoker extends AbstractAction implements DesignOnlyAction {
String type = "dot";
String extension = "dot";
public DotInvoker(String name, ImageIcon icon, String type,
String extension) {
super(name, icon);
this.type = type;
this.extension = extension;
}
@Override
public void actionPerformed(ActionEvent e) {
Workflow workflow = selectionManager.getSelectedWorkflow();
if (workflow == null) {
showMessageDialog(null, "Cannot export an empty diagram.",
"Warning", WARNING_MESSAGE);
return;
}
File file = saveDialogue(null, workflow, extension,
"Export workflow diagram");
if (file == null)
// User cancelled
return;
try {
GraphController graphController = graphViewComponent
.getGraphController(workflow);
if (type.equals("dot")) {
// Just write out the dot text, no processing required
PrintWriter out = new PrintWriter(new FileWriter(file));
DotWriter dotWriter = new DotWriter(out);
dotWriter.writeGraph(graphController.generateGraph());
out.flush();
out.close();
} else {
String dotLocation = (String) workbenchConfiguration
.getProperty("taverna.dotlocation");
if (dotLocation == null)
dotLocation = "dot";
logger.debug("GraphViewComponent: Invoking dot...");
Process dotProcess = Runtime.getRuntime().exec(
new String[] { dotLocation, "-T" + type });
FileOutputStream fos = new FileOutputStream(file);
StringWriter stringWriter = new StringWriter();
DotWriter dotWriter = new DotWriter(stringWriter);
dotWriter.writeGraph(graphController.generateGraph());
OutputStream dotOut = dotProcess.getOutputStream();
dotOut.write(SVGUtil.getDot(stringWriter.toString(),
workbenchConfiguration).getBytes());
dotOut.flush();
dotOut.close();
new StreamDevourer(dotProcess.getErrorStream()).start();
new StreamCopier(dotProcess.getInputStream(), fos).start();
}
} catch (Exception ex) {
logger.warn("GraphViewComponent: Could not export diagram to " + file, ex);
showMessageDialog(null,
"Problem saving diagram : \n" + ex.getMessage(),
"Error!", ERROR_MESSAGE);
}
}
}
/**
* Pop up a save dialogue relating to the given workflow. This method can be
* used, for example, for saving the workflow diagram as .png, and will use
* the existing workflow title as a base for suggesting a filename.
*
* @param parentComponent
* Parent component for dialogue window
* @param model
* Workflow to save
* @param extension
* Extension for filename, such as "jpg"
* @param windowTitle
* Title for dialogue box, such as "Save workflow diagram"
* @return File instance for the selected abstract filename, or null if the
* dialogue was cancelled.
*/
private File saveDialogue(Component parentComponent, Workflow workflow,
String extension, String windowTitle) {
JFileChooser fc = new JFileChooser();
Preferences prefs = Preferences
.userNodeForPackage(SaveGraphImageSubMenu.class);
String curDir = prefs
.get("currentDir", System.getProperty("user.home"));
String suggestedFileName = "";
// Get the source the workflow was loaded from - can be File, URL, or InputStream
Object source = fileManager.getDataflowSource(workflow.getParent());
if (source instanceof File) {
suggestedFileName = ((File) source).getName();
// remove the file extension
suggestedFileName = suggestedFileName.substring(0,
suggestedFileName.lastIndexOf("."));
} else if (source instanceof URL) {
suggestedFileName = ((URL) source).getPath();
// remove the file extension
suggestedFileName = suggestedFileName.substring(0,
suggestedFileName.lastIndexOf("."));
} else {
// We cannot suggest the file name if workflow was read from an InputStream
}
fc.setDialogTitle(windowTitle);
fc.resetChoosableFileFilters();
fc.setFileFilter(new ExtensionFileFilter(new String[] { extension }));
if (suggestedFileName.isEmpty())
// No file suggestion, just the directory
fc.setCurrentDirectory(new File(curDir));
else
// Suggest a filename from the workflow file name
fc.setSelectedFile(new File(curDir, suggestedFileName + "." + extension));
while (true) {
if (fc.showSaveDialog(parentComponent) != APPROVE_OPTION) {
logger.info("GraphViewComponent: Aborting diagram export to "
+ suggestedFileName);
return null;
}
File file = fixExtension(fc.getSelectedFile(), extension);
logger.debug("GraphViewComponent: Selected " + file + " as export target");
prefs.put("currentDir", fc.getCurrentDirectory().toString());
// If file doesn't exist, we may write it! (Well, probably...)
if (!file.exists())
return file;
// Ask the user if they want to overwrite the file
String msg = file.getAbsolutePath()
+ " already exists. Do you want to overwrite it?";
if (showConfirmDialog(null, msg, "File already exists",
YES_NO_OPTION) == JOptionPane.YES_OPTION)
return file;
}
}
/**
* Make sure given File has the given extension. If it has no extension,
* a new File instance will be returned. Otherwise, the passed instance is
* returned unchanged.
*
* @param file
* File which extension is to be checked
* @param extension
* Extension desired, example: "xml"
* @return file parameter if the extension was OK, or a new File instance
* with the correct extension
*/
private File fixExtension(File file, String extension) {
if (file.getName().endsWith("." + extension))
return file;
// Append the extension (keep the existing one)
String name = file.getName();
return new File(file.getParent(), name + "." + extension);
}
public void setFileManager(FileManager fileManager) {
this.fileManager = fileManager;
}
public void setWorkbenchConfiguration(
WorkbenchConfiguration workbenchConfiguration) {
this.workbenchConfiguration = workbenchConfiguration;
}
public void setSelectionManager(SelectionManager selectionManager) {
this.selectionManager = selectionManager;
}
public void setGraphViewComponent(GraphViewComponent graphViewComponent) {
this.graphViewComponent = graphViewComponent;
}
private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective";
@SuppressWarnings("unused")
private final class SelectionManagerObserver extends
SwingAwareObserver<SelectionManagerEvent> {
@Override
public void notifySwing(Observable<SelectionManagerEvent> sender,
SelectionManagerEvent message) {
if (!(message instanceof PerspectiveSelectionEvent))
return;
PerspectiveSelectionEvent event = (PerspectiveSelectionEvent) message;
saveDiagramMenu.setEnabled((DESIGN_PERSPECTIVE_ID.equals(event
.getSelectedPerspective().getID())));
}
}
}