http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/T2FlowFileType.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/T2FlowFileType.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/T2FlowFileType.java
new file mode 100644
index 0000000..bfd170b
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/T2FlowFileType.java
@@ -0,0 +1,41 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl;
+
+import org.apache.taverna.workbench.file.FileType;
+
+public class T2FlowFileType extends FileType {
+       public static final String APPLICATION_VND_TAVERNA_T2FLOW_XML = 
"application/vnd.taverna.t2flow+xml";
+
+       @Override
+       public String getDescription() {
+               return "Taverna 2 workflow";
+       }
+
+       @Override
+       public String getExtension() {
+               return "t2flow";
+       }
+
+       @Override
+       public String getMimeType() {
+               return APPLICATION_VND_TAVERNA_T2FLOW_XML;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleFileFilter.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleFileFilter.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleFileFilter.java
new file mode 100644
index 0000000..0442589
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleFileFilter.java
@@ -0,0 +1,36 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+public class WorkflowBundleFileFilter extends FileFilter {
+       @Override
+       public boolean accept(final File file) {
+               return file.getName().toLowerCase().endsWith(".wfbundle");
+       }
+
+       @Override
+       public String getDescription() {
+               return "Taverna 3 workflows";
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleFileType.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleFileType.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleFileType.java
new file mode 100644
index 0000000..a64fb0d
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleFileType.java
@@ -0,0 +1,41 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl;
+
+import org.apache.taverna.workbench.file.FileType;
+
+public class WorkflowBundleFileType extends FileType {
+       public static final String 
APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE = 
"application/vnd.taverna.scufl2.workflow-bundle";
+
+       @Override
+       public String getDescription() {
+               return "Taverna 3 workflow";
+       }
+
+       @Override
+       public String getExtension() {
+               return "wfbundle";
+       }
+
+       @Override
+       public String getMimeType() {
+               return APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleOpener.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleOpener.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleOpener.java
new file mode 100644
index 0000000..d85e4ad
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleOpener.java
@@ -0,0 +1,142 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.taverna.workbench.file.AbstractDataflowPersistenceHandler;
+import org.apache.taverna.workbench.file.DataflowInfo;
+import org.apache.taverna.workbench.file.DataflowPersistenceHandler;
+import org.apache.taverna.workbench.file.FileType;
+import org.apache.taverna.workbench.file.exceptions.OpenException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.io.ReaderException;
+import org.apache.taverna.scufl2.api.io.WorkflowBundleIO;
+
+public class WorkflowBundleOpener extends AbstractDataflowPersistenceHandler
+               implements DataflowPersistenceHandler {
+       private static final WorkflowBundleFileType WF_BUNDLE_FILE_TYPE = new 
WorkflowBundleFileType();
+       private static Logger logger = 
Logger.getLogger(WorkflowBundleOpener.class);
+       private WorkflowBundleIO workflowBundleIO;
+
+       @SuppressWarnings("resource")
+       @Override
+       public DataflowInfo openDataflow(FileType fileType, Object source)
+                       throws OpenException {
+               if (!getOpenFileTypes().contains(fileType))
+                       throw new OpenException("Unsupported file type " + 
fileType);
+               InputStream inputStream;
+               Date lastModified = null;
+               Object canonicalSource = source;
+               if (source instanceof InputStream) {
+                       inputStream = (InputStream) source;
+               } else if (source instanceof File) {
+                       try {
+                               inputStream = new FileInputStream((File) 
source);
+                       } catch (FileNotFoundException e) {
+                               throw new OpenException("Could not open file " 
+ source + ":\n"
+                                               + e.getLocalizedMessage(), e);
+                       }
+               } else if (source instanceof URL) {
+                       URL url = ((URL) source);
+                       try {
+                               URLConnection connection = url.openConnection();
+                               connection.setRequestProperty("Accept", 
"application/zip");
+                               inputStream = connection.getInputStream();
+                               if (connection.getLastModified() != 0)
+                                       lastModified = new 
Date(connection.getLastModified());
+                       } catch (IOException e) {
+                               throw new OpenException("Could not open 
connection to URL "
+                                               + source + ":\n" + 
e.getLocalizedMessage(), e);
+                       }
+                       try {
+                               if (url.getProtocol().equalsIgnoreCase("file"))
+                                       canonicalSource = new File(url.toURI());
+                       } catch (URISyntaxException e) {
+                               logger.warn("Invalid file URI created from " + 
url);
+                       }
+               } else
+                       throw new OpenException("Unsupported source type "
+                                       + source.getClass());
+
+               final WorkflowBundle workflowBundle;
+               try {
+                       workflowBundle = openDataflowStream(inputStream);
+               } finally {
+                       // We created the stream, we'll close it
+                       try {
+                               if (!(source instanceof InputStream))
+                                       inputStream.close();
+                       } catch (IOException ex) {
+                               logger.warn("Could not close inputstream " + 
inputStream, ex);
+                       }
+               }
+               if (canonicalSource instanceof File)
+                       return new FileDataflowInfo(WF_BUNDLE_FILE_TYPE,
+                                       (File) canonicalSource, workflowBundle);
+               return new DataflowInfo(WF_BUNDLE_FILE_TYPE, canonicalSource,
+                               workflowBundle, lastModified);
+       }
+
+       protected WorkflowBundle openDataflowStream(InputStream inputStream)
+                       throws OpenException {
+               WorkflowBundle workflowBundle;
+               try {
+                       workflowBundle = 
workflowBundleIO.readBundle(inputStream, null);
+               } catch (ReaderException e) {
+                       throw new OpenException("Could not read the workflow", 
e);
+               } catch (IOException e) {
+                       throw new OpenException("Could not open the workflow 
for parsing",
+                                       e);
+               } catch (Exception e) {
+                       throw new OpenException("Error while opening workflow", 
e);
+               }
+
+               return workflowBundle;
+       }
+
+       @Override
+       public List<FileType> getOpenFileTypes() {
+               return Arrays.<FileType> asList(WF_BUNDLE_FILE_TYPE);
+       }
+
+       @Override
+       public List<Class<?>> getOpenSourceTypes() {
+               return Arrays.<Class<?>> asList(InputStream.class, URL.class,
+                               File.class);
+       }
+
+       public void setWorkflowBundleIO(WorkflowBundleIO workflowBundleIO) {
+               this.workflowBundleIO = workflowBundleIO;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleSaver.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleSaver.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleSaver.java
new file mode 100644
index 0000000..89043be
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/WorkflowBundleSaver.java
@@ -0,0 +1,144 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl;
+
+import static 
org.apache.taverna.workbench.file.impl.WorkflowBundleFileType.APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.taverna.workbench.file.AbstractDataflowPersistenceHandler;
+import org.apache.taverna.workbench.file.DataflowInfo;
+import org.apache.taverna.workbench.file.DataflowPersistenceHandler;
+import org.apache.taverna.workbench.file.FileType;
+import org.apache.taverna.workbench.file.exceptions.SaveException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.io.WorkflowBundleIO;
+
+public class WorkflowBundleSaver extends AbstractDataflowPersistenceHandler
+               implements DataflowPersistenceHandler {
+       private static final WorkflowBundleFileType WF_BUNDLE_FILE_TYPE = new 
WorkflowBundleFileType();
+       private static Logger logger = 
Logger.getLogger(WorkflowBundleSaver.class);
+       private WorkflowBundleIO workflowBundleIO;
+
+       @Override
+       public DataflowInfo saveDataflow(WorkflowBundle workflowBundle, 
FileType fileType,
+                       Object destination) throws SaveException {
+               if (!getSaveFileTypes().contains(fileType))
+                       throw new IllegalArgumentException("Unsupported file 
type "
+                                       + fileType);
+               OutputStream outStream;
+               if (destination instanceof File)
+                       try {
+                               outStream = new FileOutputStream((File) 
destination);
+                       } catch (FileNotFoundException e) {
+                               throw new SaveException("Can't create workflow 
file "
+                                               + destination + ":\n" + 
e.getLocalizedMessage(), e);
+                       }
+               else if (destination instanceof OutputStream)
+                       outStream = (OutputStream) destination;
+               else
+                       throw new SaveException("Unsupported destination type "
+                                       + destination.getClass());
+
+               try {
+                       saveDataflowToStream(workflowBundle, outStream);
+               } finally {
+                       try {
+                               // Only close if we opened the stream
+                               if (!(destination instanceof OutputStream))
+                                       outStream.close();
+                       } catch (IOException e) {
+                               logger.warn("Could not close stream", e);
+                       }
+               }
+
+               if (destination instanceof File)
+                       return new FileDataflowInfo(WF_BUNDLE_FILE_TYPE, (File) 
destination,
+                                       workflowBundle);
+               return new DataflowInfo(WF_BUNDLE_FILE_TYPE, destination, 
workflowBundle);
+       }
+
+       protected void saveDataflowToStream(WorkflowBundle workflowBundle,
+                       OutputStream fileOutStream) throws SaveException {
+               try {
+                       workflowBundleIO.writeBundle(workflowBundle, 
fileOutStream,
+                                       
APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE);
+               } catch (Exception e) {
+                       throw new SaveException("Can't write workflow:\n"
+                                       + e.getLocalizedMessage(), e);
+               }
+       }
+
+       @Override
+       public List<FileType> getSaveFileTypes() {
+               return Arrays.<FileType> asList(WF_BUNDLE_FILE_TYPE);
+       }
+
+       @Override
+       public List<Class<?>> getSaveDestinationTypes() {
+               return Arrays.<Class<?>> asList(File.class, OutputStream.class);
+       }
+
+       @Override
+       public boolean wouldOverwriteDataflow(WorkflowBundle workflowBundle, 
FileType fileType,
+                       Object destination, DataflowInfo lastDataflowInfo) {
+               if (!getSaveFileTypes().contains(fileType))
+                       throw new IllegalArgumentException("Unsupported file 
type "
+                                       + fileType);
+               if (!(destination instanceof File))
+                       return false;
+
+               File file;
+               try {
+                       file = ((File) destination).getCanonicalFile();
+               } catch (IOException e) {
+                       return false;
+               }
+               if (!file.exists())
+                       return false;
+               if (lastDataflowInfo == null)
+                       return true;
+               Object lastDestination = lastDataflowInfo.getCanonicalSource();
+               if (!(lastDestination instanceof File))
+                       return true;
+               File lastFile = (File) lastDestination;
+               if (!lastFile.getAbsoluteFile().equals(file))
+                       return true;
+
+               Date lastModified = new Date(file.lastModified());
+               if (lastModified.equals(lastDataflowInfo.getLastModified()))
+                       return false;
+               return true;
+       }
+
+       public void setWorkflowBundleIO(WorkflowBundleIO workflowBundleIO) {
+               this.workflowBundleIO = workflowBundleIO;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/CloseAllWorkflowsAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/CloseAllWorkflowsAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/CloseAllWorkflowsAction.java
new file mode 100644
index 0000000..eb88068
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/CloseAllWorkflowsAction.java
@@ -0,0 +1,84 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
+import static java.awt.event.KeyEvent.VK_L;
+import static java.awt.event.KeyEvent.VK_W;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.closeAllIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.file.FileManager;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class CloseAllWorkflowsAction extends AbstractAction {
+       @SuppressWarnings("unused")
+       private static Logger logger = 
Logger.getLogger(CloseWorkflowAction.class);
+       private static final String CLOSE_ALL_WORKFLOWS = "Close all workflows";
+       private FileManager fileManager;
+       private CloseWorkflowAction closeWorkflowAction;
+
+       public CloseAllWorkflowsAction(EditManager editManager, FileManager 
fileManager) {
+               super(CLOSE_ALL_WORKFLOWS, closeAllIcon);
+               this.fileManager = fileManager;
+               closeWorkflowAction = new CloseWorkflowAction(editManager, 
fileManager);
+               putValue(
+                               ACCELERATOR_KEY,
+                               getKeyStroke(VK_W, 
getDefaultToolkit().getMenuShortcutKeyMask()
+                                               | SHIFT_DOWN_MASK));
+               putValue(MNEMONIC_KEY, VK_L);
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent event) {
+               Component parentComponent = null;
+               if (event.getSource() instanceof Component)
+                       parentComponent = (Component) event.getSource();
+               closeAllWorkflows(parentComponent);
+       }
+
+       public boolean closeAllWorkflows(Component parentComponent) {
+               // Close in reverse so we can save nested workflows first
+               List<WorkflowBundle> workflowBundles = 
fileManager.getOpenDataflows();
+
+               Collections.reverse(workflowBundles);
+
+               for (WorkflowBundle workflowBundle : workflowBundles) {
+                       boolean success = closeWorkflowAction.closeWorkflow(
+                                       parentComponent, workflowBundle);
+                       if (!success)
+                               return false;
+               }
+               return true;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/CloseWorkflowAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/CloseWorkflowAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/CloseWorkflowAction.java
new file mode 100644
index 0000000..091a652
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/CloseWorkflowAction.java
@@ -0,0 +1,106 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_C;
+import static java.awt.event.KeyEvent.VK_W;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.closeIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.exceptions.UnsavedException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class CloseWorkflowAction extends AbstractAction {
+       private static Logger logger = 
Logger.getLogger(CloseWorkflowAction.class);
+       private static final String CLOSE_WORKFLOW = "Close workflow";
+       private final SaveWorkflowAction saveWorkflowAction;
+       private FileManager fileManager;
+
+       public CloseWorkflowAction(EditManager editManager, FileManager 
fileManager) {
+               super(CLOSE_WORKFLOW, closeIcon);
+               this.fileManager = fileManager;
+               saveWorkflowAction = new SaveWorkflowAction(editManager, 
fileManager);
+               putValue(
+                               ACCELERATOR_KEY,
+                               getKeyStroke(VK_W, 
getDefaultToolkit().getMenuShortcutKeyMask()));
+               putValue(MNEMONIC_KEY, VK_C);
+
+       }
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               Component parentComponent = null;
+               if (e.getSource() instanceof Component)
+                       parentComponent = (Component) e.getSource();
+               closeWorkflow(parentComponent, 
fileManager.getCurrentDataflow());
+       }
+
+       public boolean closeWorkflow(Component parentComponent, WorkflowBundle 
workflowBundle) {
+               if (workflowBundle == null) {
+                       logger.warn("Attempted to close a null workflow");
+                       return false;
+               }
+
+               try {
+                       return fileManager.closeDataflow(workflowBundle, true);
+               } catch (UnsavedException e1) {
+                       fileManager.setCurrentDataflow(workflowBundle);
+                       String msg = "Do you want to save changes before 
closing the workflow "
+                                       + 
fileManager.getDataflowName(workflowBundle) + "?";
+                       switch (showConfirmDialog(parentComponent, msg, "Save 
workflow?",
+                                       YES_NO_CANCEL_OPTION)) {
+                       case NO_OPTION:
+                               try {
+                                       
fileManager.closeDataflow(workflowBundle, false);
+                                       return true;
+                               } catch (UnsavedException e2) {
+                                       logger.error("Unexpected 
UnsavedException while "
+                                                       + "closing workflow", 
e2);
+                                       return false;
+                               }
+                       case YES_OPTION:
+                               boolean saved = saveWorkflowAction.saveDataflow(
+                                               parentComponent, 
workflowBundle);
+                               if (!saved)
+                                       return false;
+                               return closeWorkflow(parentComponent, 
workflowBundle);
+                       case CANCEL_OPTION:
+                       default:
+                               return false;
+                       }
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/NewWorkflowAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/NewWorkflowAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/NewWorkflowAction.java
new file mode 100644
index 0000000..deb0926
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/NewWorkflowAction.java
@@ -0,0 +1,57 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_N;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.newIcon;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.AbstractAction;
+
+import org.apache.taverna.workbench.file.FileManager;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+public class NewWorkflowAction extends AbstractAction {
+       @SuppressWarnings("unused")
+       private static Logger logger = 
Logger.getLogger(NewWorkflowAction.class);
+       private static final String NEW_WORKFLOW = "New workflow";
+       private FileManager fileManager;
+
+       public NewWorkflowAction(FileManager fileManager) {
+               super(NEW_WORKFLOW, newIcon);
+               this.fileManager = fileManager;
+               putValue(SHORT_DESCRIPTION, NEW_WORKFLOW);
+               putValue(MNEMONIC_KEY, KeyEvent.VK_N);
+               putValue(
+                               ACCELERATOR_KEY,
+                               getKeyStroke(VK_N, 
getDefaultToolkit().getMenuShortcutKeyMask()));
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               fileManager.newDataflow();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenNestedWorkflowAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenNestedWorkflowAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenNestedWorkflowAction.java
new file mode 100644
index 0000000..2e2124d
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenNestedWorkflowAction.java
@@ -0,0 +1,75 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import java.awt.Component;
+import java.io.File;
+
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.FileType;
+import org.apache.taverna.workbench.file.exceptions.OpenException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * An action for opening a nested workflow from a file.
+ * 
+ * @author Alex Nenadic
+ */
+public class OpenNestedWorkflowAction extends OpenWorkflowAction {
+       private static final long serialVersionUID = -5398423684000142379L;
+       private static Logger logger = Logger
+                       .getLogger(OpenNestedWorkflowAction.class);
+
+       public OpenNestedWorkflowAction(FileManager fileManager) {
+               super(fileManager);
+       }
+
+       /**
+        * Opens a nested workflow from a file (should be one file even though 
the
+        * method takes a list of files - this is because it overrides the
+        * {@link OpenWorkflowAction#openWorkflows(Component, File[], FileType, 
OpenCallback)
+        * openWorkflows(...)} method).
+        */
+       @Override
+       public void openWorkflows(final Component parentComponent, File[] files,
+                       FileType fileType, OpenCallback openCallback) {
+               ErrorLoggingOpenCallbackWrapper callback = new 
ErrorLoggingOpenCallbackWrapper(
+                               openCallback);
+               for (File file : files)
+                       try {
+                               callback.aboutToOpenDataflow(file);
+                               WorkflowBundle workflowBundle = 
fileManager.openDataflow(
+                                               fileType, file);
+                               callback.openedDataflow(file, workflowBundle);
+                       } catch (final RuntimeException ex) {
+                               logger.warn("Could not open workflow from " + 
file, ex);
+                               if (!callback.couldNotOpenDataflow(file, ex))
+                                       showErrorMessage(parentComponent, file, 
ex);
+                       } catch (final OpenException ex) {
+                               logger.warn("Could not open workflow from " + 
file, ex);
+                               if (!callback.couldNotOpenDataflow(file, ex))
+                                       showErrorMessage(parentComponent, file, 
ex);
+                               return;
+                       }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenWorkflowAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenWorkflowAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenWorkflowAction.java
new file mode 100644
index 0000000..3a7560a
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenWorkflowAction.java
@@ -0,0 +1,394 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_O;
+import static java.util.prefs.Preferences.userNodeForPackage;
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JOptionPane.showOptionDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static javax.swing.SwingUtilities.invokeLater;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.openIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.FileType;
+import org.apache.taverna.workbench.file.exceptions.OpenException;
+import org.apache.taverna.workbench.file.impl.FileTypeFileFilter;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * An action for opening a workflow from a file. All file types exposed by the
+ * {@link FileManager} as compatible with the {@link File} type are supported.
+ *
+ * @author Stian Soiland-Reyes
+ */
+public class OpenWorkflowAction extends AbstractAction {
+       private static final long serialVersionUID = 103237694130052153L;
+       private static Logger logger = 
Logger.getLogger(OpenWorkflowAction.class);
+       private static final String OPEN_WORKFLOW = "Open workflow...";
+
+       public final OpenCallback DUMMY_OPEN_CALLBACK = new 
OpenCallbackAdapter();
+       protected FileManager fileManager;
+
+       public OpenWorkflowAction(FileManager fileManager) {
+               super(OPEN_WORKFLOW, openIcon);
+               this.fileManager = fileManager;
+               putValue(
+                               ACCELERATOR_KEY,
+                               getKeyStroke(VK_O, 
getDefaultToolkit().getMenuShortcutKeyMask()));
+               putValue(MNEMONIC_KEY, VK_O);
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               final Component parentComponent;
+               if (e.getSource() instanceof Component)
+                       parentComponent = (Component) e.getSource();
+               else
+                       parentComponent = null;
+               openWorkflows(parentComponent);
+       }
+
+       /**
+        * Pop up an Open-dialogue to select one or more workflow files to open.
+        * <p>
+        * Note that the file opening occurs in a separate thread. If you want 
to
+        * check if the file was opened or not, which workflow was opened, etc, 
use
+        * {@link #openWorkflows(Component, OpenCallback)} instead.
+        *
+        * @see #openWorkflows(Component, OpenCallback)
+        * @param parentComponent
+        *            The UI parent component to use for pop up dialogues
+        *
+        * @return <code>false</code> if no files were selected or the dialogue 
was
+        *         cancelled, or <code>true</code> if the process of opening 
one or
+        *         more files has been started.
+        */
+       public void openWorkflows(Component parentComponent) {
+               openWorkflows(parentComponent, DUMMY_OPEN_CALLBACK);
+       }
+
+       /**
+        * Open an array of workflow files.
+        *
+        * @param parentComponent
+        *            Parent component for UI dialogues
+        * @param files
+        *            Array of files to be opened
+        * @param fileType
+        *            {@link FileType} of the files that are to be opened, for
+        *            instance
+        *            {@link 
org.apache.taverna.workbench.file.impl.T2FlowFileType},
+        *            or <code>null</code> to guess.
+        * @param openCallback
+        *            An {@link OpenCallback} to be invoked during and after 
opening
+        *            the file. Use {@link 
OpenWorkflowAction#DUMMY_OPEN_CALLBACK}
+        *            if no callback is needed.
+        */
+       public void openWorkflows(final Component parentComponent, File[] files,
+                       FileType fileType, OpenCallback openCallback) {
+               ErrorLoggingOpenCallbackWrapper callback = new 
ErrorLoggingOpenCallbackWrapper(
+                               openCallback);
+               for (File file : files)
+                       try {
+                               Object canonicalSource = 
fileManager.getCanonical(file);
+                               WorkflowBundle alreadyOpen = 
fileManager.getDataflowBySource(canonicalSource);
+                               if (alreadyOpen != null) {
+                                       /*
+                                        * The workflow from the same source is 
already opened - ask
+                                        * the user if they want to switch to 
it or open another
+                                        * copy...
+                                        */
+
+                                       Object[] options = { "Switch to 
opened", "Open new copy",
+                                                       "Cancel" };
+                                       switch (showOptionDialog(
+                                                       null,
+                                                       "The workflow from the 
same location is already opened.\n"
+                                                                       + "Do 
you want to switch to it or open a new copy?",
+                                                       "File Manager Alert", 
YES_NO_CANCEL_OPTION,
+                                                       QUESTION_MESSAGE, null, 
options, // the titles of buttons
+                                                       options[0])) { // 
default button title
+                                       case YES_OPTION:
+                                               
fileManager.setCurrentDataflow(alreadyOpen);
+                                               return;
+                                       case CANCEL_OPTION:
+                                               // do nothing
+                                               return;
+                                       }
+                                       // else open the workflow as usual
+                               }
+
+                               callback.aboutToOpenDataflow(file);
+                               WorkflowBundle workflowBundle = 
fileManager.openDataflow(fileType, file);
+                               callback.openedDataflow(file, workflowBundle);
+                       } catch (RuntimeException ex) {
+                               logger.warn("Failed to open workflow from " + 
file, ex);
+                               if (!callback.couldNotOpenDataflow(file, ex))
+                                       showErrorMessage(parentComponent, file, 
ex);
+                       } catch (Exception ex) {
+                               logger.warn("Failed to open workflow from " + 
file, ex);
+                               if (!callback.couldNotOpenDataflow(file, ex))
+                                       showErrorMessage(parentComponent, file, 
ex);
+                               return;
+                       }
+       }
+
+       /**
+        * Pop up an Open-dialogue to select one or more workflow files to open.
+        *
+        * @param parentComponent
+        *            The UI parent component to use for pop up dialogues
+        * @param openCallback
+        *            An {@link OpenCallback} to be called during the file 
opening.
+        *            The callback will be invoked for each file that has been
+        *            opened, as file opening happens in a separate thread that
+        *            might execute after the return of this method.
+        * @return <code>false</code> if no files were selected or the dialogue 
was
+        *         cancelled, or <code>true</code> if the process of opening 
one or
+        *         more files has been started.
+        */
+       public boolean openWorkflows(final Component parentComponent,
+                       OpenCallback openCallback) {
+               JFileChooser fileChooser = new JFileChooser();
+               Preferences prefs = userNodeForPackage(getClass());
+               String curDir = prefs
+                               .get("currentDir", 
System.getProperty("user.home"));
+               fileChooser.setDialogTitle(OPEN_WORKFLOW);
+
+               fileChooser.resetChoosableFileFilters();
+               fileChooser.setAcceptAllFileFilterUsed(false);
+               List<FileFilter> fileFilters = fileManager.getOpenFileFilters();
+               if (fileFilters.isEmpty()) {
+                       logger.warn("No file types found for opening workflow");
+                       showMessageDialog(parentComponent,
+                                       "No file types found for opening 
workflow.", "Error",
+                                       ERROR_MESSAGE);
+                       return false;
+               }
+               for (FileFilter fileFilter : fileFilters)
+                       fileChooser.addChoosableFileFilter(fileFilter);
+               fileChooser.setFileFilter(fileFilters.get(0));
+               fileChooser.setCurrentDirectory(new File(curDir));
+               fileChooser.setMultiSelectionEnabled(true);
+
+               int returnVal = fileChooser.showOpenDialog(parentComponent);
+               if (returnVal == APPROVE_OPTION) {
+                       prefs.put("currentDir", 
fileChooser.getCurrentDirectory()
+                                       .toString());
+                       final File[] selectedFiles = 
fileChooser.getSelectedFiles();
+                       if (selectedFiles.length == 0) {
+                               logger.warn("No files selected");
+                               return false;
+                       }
+                       FileFilter fileFilter = fileChooser.getFileFilter();
+                       FileType fileType;
+                       if (fileFilter instanceof FileTypeFileFilter)
+                               fileType = ((FileTypeFileFilter) 
fileChooser.getFileFilter())
+                                               .getFileType();
+                       else
+                               // Unknown filetype, try all of them
+                               fileType = null;
+                       new FileOpenerThread(parentComponent, selectedFiles, 
fileType,
+                                       openCallback).start();
+                       return true;
+               }
+               return false;
+       }
+
+       /**
+        * Show an error message if a file could not be opened
+        * 
+        * @param parentComponent
+        * @param file
+        * @param throwable
+        */
+       protected void showErrorMessage(final Component parentComponent,
+                       final File file, final Throwable throwable) {
+               invokeLater(new Runnable() {
+                       @Override
+                       public void run() {
+                               Throwable cause = throwable;
+                               while (cause.getCause() != null)
+                                       cause = cause.getCause();
+                               showMessageDialog(
+                                               parentComponent,
+                                               "Failed to open workflow from " 
+ file + ": \n"
+                                                               + 
cause.getMessage(), "Warning",
+                                               WARNING_MESSAGE);
+                       }
+               });
+
+       }
+
+       /**
+        * Callback interface for openWorkflows().
+        * <p>
+        * The callback will be invoked during the invocation of
+        * {@link OpenWorkflowAction#openWorkflows(Component, OpenCallback)} and
+        * {@link OpenWorkflowAction#openWorkflows(Component, File[], FileType, 
OpenCallback)}
+        * as file opening happens in a separate thread.
+        *
+        * @author Stian Soiland-Reyes
+        */
+       public interface OpenCallback {
+               /**
+                * Called before a workflowBundle is to be opened from the 
given file
+                *
+                * @param file
+                *            File which workflowBundle is to be opened
+                */
+               void aboutToOpenDataflow(File file);
+
+               /**
+                * Called if an exception happened while attempting to open the
+                * workflowBundle.
+                *
+                * @param file
+                *            File which was attempted to be opened
+                * @param ex
+                *            An {@link OpenException} or a {@link 
RuntimeException}.
+                * @return <code>true</code> if the error has been handled, or
+                *         <code>false</code>3 if a UI warning dialogue is to 
be opened.
+                */
+               boolean couldNotOpenDataflow(File file, Exception ex);
+
+               /**
+                * Called when a workflowBundle has been successfully opened. 
The workflowBundle
+                * will be registered in {@link FileManager#getOpenDataflows()}.
+                *
+                * @param file
+                *            File from which workflowBundle was opened
+                * @param workflowBundle
+                *            WorkflowBundle that was opened
+                */
+               void openedDataflow(File file, WorkflowBundle workflowBundle);
+       }
+
+       /**
+        * Adapter for {@link OpenCallback}
+        *
+        * @author Stian Soiland-Reyes
+        */
+       public static class OpenCallbackAdapter implements OpenCallback {
+               @Override
+               public void aboutToOpenDataflow(File file) {
+               }
+
+               @Override
+               public boolean couldNotOpenDataflow(File file, Exception ex) {
+                       return false;
+               }
+
+               @Override
+               public void openedDataflow(File file, WorkflowBundle 
workflowBundle) {
+               }
+       }
+
+       private final class FileOpenerThread extends Thread {
+               private final File[] files;
+               private final FileType fileType;
+               private final OpenCallback openCallback;
+               private final Component parentComponent;
+
+               private FileOpenerThread(Component parentComponent,
+                               File[] selectedFiles, FileType fileType,
+                               OpenCallback openCallback) {
+                       super("Opening workflows(s) " + 
Arrays.asList(selectedFiles));
+                       this.parentComponent = parentComponent;
+                       this.files = selectedFiles;
+                       this.fileType = fileType;
+                       this.openCallback = openCallback;
+               }
+
+               @Override
+               public void run() {
+                       openWorkflows(parentComponent, files, fileType, 
openCallback);
+               }
+       }
+
+       /**
+        * A wrapper for {@link OpenCallback} implementations that logs 
exceptions
+        * thrown without disrupting the caller of the callback.
+        *
+        * @author Stian Soiland-Reyes
+        */
+       protected class ErrorLoggingOpenCallbackWrapper implements OpenCallback 
{
+               private final OpenCallback wrapped;
+
+               public ErrorLoggingOpenCallbackWrapper(OpenCallback wrapped) {
+                       this.wrapped = wrapped;
+               }
+
+               @Override
+               public void aboutToOpenDataflow(File file) {
+                       try {
+                               wrapped.aboutToOpenDataflow(file);
+                       } catch (RuntimeException wrapperEx) {
+                               logger.warn("Failed OpenCallback " + wrapped
+                                               + ".aboutToOpenDataflow(File)", 
wrapperEx);
+                       }
+               }
+
+               @Override
+               public boolean couldNotOpenDataflow(File file, Exception ex) {
+                       try {
+                               return wrapped.couldNotOpenDataflow(file, ex);
+                       } catch (RuntimeException wrapperEx) {
+                               logger.warn("Failed OpenCallback " + wrapped
+                                               + ".couldNotOpenDataflow(File, 
Exception)", wrapperEx);
+                               return false;
+                       }
+               }
+
+               @Override
+               public void openedDataflow(File file, WorkflowBundle 
workflowBundle) {
+                       try {
+                               wrapped.openedDataflow(file, workflowBundle);
+                       } catch (RuntimeException wrapperEx) {
+                               logger.warn("Failed OpenCallback " + wrapped
+                                               + ".openedDataflow(File, 
Dataflow)", wrapperEx);
+                       }
+               }
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenWorkflowFromURLAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenWorkflowFromURLAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenWorkflowFromURLAction.java
new file mode 100644
index 0000000..3c1a4cc
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/OpenWorkflowFromURLAction.java
@@ -0,0 +1,138 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_L;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showInputDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JOptionPane.showOptionDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.openurlIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.net.URL;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+
+import org.apache.taverna.workbench.file.FileManager;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * An action for opening a workflow from a url.
+ * 
+ * @author David Withers
+ */
+public class OpenWorkflowFromURLAction extends AbstractAction {
+       private static final long serialVersionUID = 1474356457949961974L;
+       private static Logger logger = Logger
+                       .getLogger(OpenWorkflowFromURLAction.class);
+       private static Preferences prefs = Preferences
+                       .userNodeForPackage(OpenWorkflowFromURLAction.class);
+       private static final String PREF_CURRENT_URL = "currentUrl";
+       private static final String ACTION_NAME = "Open workflow location...";
+       private static final String ACTION_DESCRIPTION = "Open a workflow from 
the web into a new workflow";
+
+       private Component component;
+       private FileManager fileManager;
+
+       public OpenWorkflowFromURLAction(final Component component,
+                       FileManager fileManager) {
+               this.component = component;
+               this.fileManager = fileManager;
+               putValue(SMALL_ICON, openurlIcon);
+               putValue(NAME, ACTION_NAME);
+               putValue(SHORT_DESCRIPTION, ACTION_DESCRIPTION);
+               putValue(MNEMONIC_KEY, VK_L);
+               putValue(
+                               ACCELERATOR_KEY,
+                               getKeyStroke(VK_L, 
getDefaultToolkit().getMenuShortcutKeyMask()));
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               String currentUrl = prefs.get(PREF_CURRENT_URL, "http://";);
+
+               final String url = (String) showInputDialog(component,
+                               "Enter the URL of a workflow definition to 
load",
+                               "Workflow URL", QUESTION_MESSAGE, null, null, 
currentUrl);
+               if (url != null)
+                       new Thread("OpenWorkflowFromURLAction") {
+                               @Override
+                               public void run() {
+                                       openFromURL(url);
+                               }
+                       }.start();
+       }
+
+       private void openFromURL(String urlString) {
+               try {
+                       URL url = new URL(urlString);
+
+                       Object canonicalSource = fileManager.getCanonical(url);
+                       WorkflowBundle alreadyOpen = fileManager
+                                       .getDataflowBySource(canonicalSource);
+                       if (alreadyOpen != null) {
+                               /*
+                                * The workflow from the same source is already 
opened - ask the
+                                * user if they want to switch to it or open 
another copy.
+                                */
+
+                               Object[] options = { "Switch to opened", "Open 
new copy",
+                                               "Cancel" };
+                               int iSelected = showOptionDialog(
+                                               null,
+                                               "The workflow from the same 
location is already opened.\n"
+                                                               + "Do you want 
to switch to it or open a new copy?",
+                                               "File Manager Alert", 
YES_NO_CANCEL_OPTION,
+                                               QUESTION_MESSAGE, null, 
options, // the titles of buttons
+                                               options[0]); // default button 
title
+
+                               if (iSelected == YES_OPTION) {
+                                       
fileManager.setCurrentDataflow(alreadyOpen);
+                                       return;
+                               } else if (iSelected == CANCEL_OPTION) {
+                                       // do nothing
+                                       return;
+                               }
+                               // else open the workflow as usual
+                       }
+
+                       fileManager.openDataflow(null, url);
+                       prefs.put(PREF_CURRENT_URL, urlString);
+               } catch (Exception ex) {
+                       logger.warn("Failed to open the workflow from url " + 
urlString
+                                       + " \n", ex);
+                       showMessageDialog(component,
+                                       "Failed to open the workflow from url " 
+ urlString + " \n"
+                                                       + ex.getMessage(), 
"Error!", ERROR_MESSAGE);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/PasswordInput.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/PasswordInput.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/PasswordInput.java
new file mode 100644
index 0000000..61e80e6
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/PasswordInput.java
@@ -0,0 +1,220 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.EventQueue.invokeLater;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+
+import org.apache.taverna.workbench.helper.HelpEnabledDialog;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Logger;
+
+/**
+ * Simple dialogue to handle username/password input for workflow URL requiring
+ * http authentication.
+ * 
+ * @author Stuart Owen
+ * @author Stian Soiland-Reyes
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public class PasswordInput extends HelpEnabledDialog {
+       private static Logger logger = Logger.getLogger(PasswordInput.class);
+
+       private String password = null;
+       private String username = null;
+       private URL url = null;
+       private int tryCount = 0;
+       private final static int MAX_TRIES = 3;
+
+       private JButton cancelButton;
+       private JLabel jLabel1;
+       private JLabel jLabel2;
+       private JLabel messageLabel;
+       private JButton okButton;
+       private JPasswordField passwordTextField;
+       private JLabel urlLabel;
+       private JTextField usernameTextField;
+
+       public void setUrl(URL url) {
+               this.url = url;
+               urlLabel.setText(url.toExternalForm());
+       }
+
+       public String getPassword() {
+               return password;
+       }
+
+       public String getUsername() {
+               return username;
+       }
+
+       public PasswordInput(JFrame parent) {
+               super(parent, "Authorization", true, null);
+               initComponents();
+       }
+
+       /** Creates new form PasswordInput */
+       public PasswordInput() {
+               super((JFrame) null, "Authorization", true, null);
+               initComponents();
+       }
+
+       /**
+        * This method is called from within the constructor to initialize the 
form.
+        * WARNING: Do NOT modify this code. The content of this method is 
always
+        * regenerated by the Form Editor.
+        */
+       private void initComponents() {
+               usernameTextField = new javax.swing.JTextField();
+               cancelButton = new javax.swing.JButton();
+               okButton = new javax.swing.JButton();
+               passwordTextField = new javax.swing.JPasswordField();
+               jLabel1 = new javax.swing.JLabel();
+               jLabel2 = new javax.swing.JLabel();
+               messageLabel = new javax.swing.JLabel();
+               urlLabel = new javax.swing.JLabel();
+
+               getContentPane().setLayout(null);
+
+               setModal(true);
+               // setResizable(false);
+               getContentPane().add(usernameTextField);
+               usernameTextField.setBounds(20, 80, 280, 22);
+
+               cancelButton.setText("Cancel");
+               cancelButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent evt) {
+                               cancelButtonActionPerformed(evt);
+                       }
+               });
+
+               getContentPane().add(cancelButton);
+               cancelButton.setBounds(230, 160, 75, 29);
+
+               okButton.setText("OK");
+               okButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent evt) {
+                               okButtonActionPerformed(evt);
+                       }
+               });
+
+               getContentPane().add(okButton);
+               okButton.setBounds(150, 160, 75, 29);
+
+               getContentPane().add(passwordTextField);
+               passwordTextField.setBounds(20, 130, 280, 22);
+
+               jLabel1.setText("Username");
+               getContentPane().add(jLabel1);
+               jLabel1.setBounds(20, 60, 70, 16);
+
+               jLabel2.setText("Password");
+               getContentPane().add(jLabel2);
+               jLabel2.setBounds(20, 110, 70, 16);
+
+               messageLabel.setText("A username and password is required 
for:");
+               getContentPane().add(messageLabel);
+               messageLabel.setBounds(20, 10, 270, 20);
+
+               urlLabel.setText("service");
+               getContentPane().add(urlLabel);
+               urlLabel.setBounds(20, 30, 270, 16);
+
+               pack();
+       }
+
+       private void okButtonActionPerformed(ActionEvent evt) 
{//GEN-FIRST:event_okButtonActionPerformed
+               String password = 
String.valueOf(passwordTextField.getPassword());
+               String username = usernameTextField.getText();
+               HttpURLConnection connection;
+               try {
+                       connection = (HttpURLConnection) url.openConnection();
+                       String userPassword = username + ":" + password;
+                       /*
+                        * Note: non-latin1 support for basic auth is 
fragile/unsupported
+                        * and must be MIME-encoded (RFC2047) according to
+                        * https://bugzilla.mozilla.org/show_bug.cgi?id=41489
+                        */
+                       byte[] encoded = Base64.encodeBase64(userPassword
+                                       .getBytes("latin1"));
+                       connection.setRequestProperty("Authorization", "Basic "
+                                       + new String(encoded, "ascii"));
+                       connection.setRequestProperty("Accept", "text/xml");
+                       int code = connection.getResponseCode();
+
+                       /*
+                        * NB: myExperiment gives a 500 response for an invalid
+                        * username/password
+                        */
+                       if (code == 401 || code == 500) {
+                               tryCount++;
+                               showMessageDialog(this, "The username and 
password failed",
+                                               "Invalid username or password", 
ERROR_MESSAGE);
+                               if (tryCount >= MAX_TRIES) { // close after 3 
attempts.
+                                       this.password = null;
+                                       this.username = null;
+                                       this.setVisible(false);
+                               }
+                       } else {
+                               this.username = username;
+                               this.password = password;
+                               this.setVisible(false);
+                       }
+               } catch (IOException ex) {
+                       logger.error("Could not get password", ex);
+               }
+       }
+
+       private void cancelButtonActionPerformed(ActionEvent evt) {
+               this.password = null;
+               this.username = null;
+               this.setVisible(false);
+       }
+
+       /**
+        * @param args
+        *            the command line arguments
+        */
+       public static void main(String args[]) {
+               invokeLater(new Runnable() {
+                       @Override
+                       public void run() {
+                               new PasswordInput().setVisible(true);
+                       }
+               });
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveAllWorkflowsAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveAllWorkflowsAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveAllWorkflowsAction.java
new file mode 100644
index 0000000..b58b99f
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveAllWorkflowsAction.java
@@ -0,0 +1,103 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
+import static java.awt.event.KeyEvent.VK_A;
+import static java.awt.event.KeyEvent.VK_S;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.saveAllIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.events.FileManagerEvent;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class SaveAllWorkflowsAction extends AbstractAction {
+       private final class FileManagerObserver implements
+                       Observer<FileManagerEvent> {
+               @Override
+               public void notify(Observable<FileManagerEvent> sender,
+                               FileManagerEvent message) throws Exception {
+                       updateEnabled();
+               }
+       }
+
+       @SuppressWarnings("unused")
+       private static Logger logger = Logger
+                       .getLogger(SaveAllWorkflowsAction.class);
+       private static final String SAVE_ALL_WORKFLOWS = "Save all workflows";
+
+       private final SaveWorkflowAction saveWorkflowAction;
+       private FileManager fileManager;
+       private FileManagerObserver fileManagerObserver = new 
FileManagerObserver();
+
+       public SaveAllWorkflowsAction(EditManager editManager,
+                       FileManager fileManager) {
+               super(SAVE_ALL_WORKFLOWS, saveAllIcon);
+               this.fileManager = fileManager;
+               saveWorkflowAction = new SaveWorkflowAction(editManager, 
fileManager);
+               putValue(
+                               ACCELERATOR_KEY,
+                               getKeyStroke(VK_S, 
getDefaultToolkit().getMenuShortcutKeyMask()
+                                               | SHIFT_DOWN_MASK));
+               putValue(MNEMONIC_KEY, VK_A);
+
+               fileManager.addObserver(fileManagerObserver);
+               updateEnabled();
+       }
+
+       public void updateEnabled() {
+               setEnabled(!(fileManager.getOpenDataflows().isEmpty()));
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent ev) {
+               Component parentComponent = null;
+               if (ev.getSource() instanceof Component)
+                       parentComponent = (Component) ev.getSource();
+               saveAllDataflows(parentComponent);
+       }
+
+       public void saveAllDataflows(Component parentComponent) {
+               // Save in reverse so we save nested workflows first
+               List<WorkflowBundle> workflowBundles = 
fileManager.getOpenDataflows();
+               Collections.reverse(workflowBundles);
+
+               for (WorkflowBundle workflowBundle : workflowBundles)
+                       if (!saveWorkflowAction.saveDataflow(parentComponent,
+                                       workflowBundle))
+                               break;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveWorkflowAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveWorkflowAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveWorkflowAction.java
new file mode 100644
index 0000000..2cf2775
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveWorkflowAction.java
@@ -0,0 +1,174 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_S;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.saveIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+import org.apache.taverna.workbench.edits.EditManager;
+import 
org.apache.taverna.workbench.edits.EditManager.AbstractDataflowEditEvent;
+import org.apache.taverna.workbench.edits.EditManager.EditManagerEvent;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.events.FileManagerEvent;
+import org.apache.taverna.workbench.file.events.SavedDataflowEvent;
+import org.apache.taverna.workbench.file.events.SetCurrentDataflowEvent;
+import org.apache.taverna.workbench.file.exceptions.OverwriteException;
+import org.apache.taverna.workbench.file.exceptions.SaveException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class SaveWorkflowAction extends AbstractAction {
+       private static Logger logger = 
Logger.getLogger(SaveWorkflowAction.class);
+       private static final String SAVE_WORKFLOW = "Save workflow";
+
+       private final SaveWorkflowAsAction saveWorkflowAsAction;
+       private EditManagerObserver editManagerObserver = new 
EditManagerObserver();
+       private FileManager fileManager;
+       private FileManagerObserver fileManagerObserver = new 
FileManagerObserver();
+
+       public SaveWorkflowAction(EditManager editManager, FileManager 
fileManager) {
+               super(SAVE_WORKFLOW, saveIcon);
+               this.fileManager = fileManager;
+               saveWorkflowAsAction = new SaveWorkflowAsAction(fileManager);
+               putValue(
+                               ACCELERATOR_KEY,
+                               getKeyStroke(VK_S, 
getDefaultToolkit().getMenuShortcutKeyMask()));
+               putValue(MNEMONIC_KEY, VK_S);
+               editManager.addObserver(editManagerObserver);
+               fileManager.addObserver(fileManagerObserver);
+               updateEnabledStatus(fileManager.getCurrentDataflow());
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent ev) {
+               Component parentComponent = null;
+               if (ev.getSource() instanceof Component)
+                       parentComponent = (Component) ev.getSource();
+               saveCurrentDataflow(parentComponent);
+       }
+
+       public boolean saveCurrentDataflow(Component parentComponent) {
+               WorkflowBundle workflowBundle = 
fileManager.getCurrentDataflow();
+               return saveDataflow(parentComponent, workflowBundle);
+       }
+
+       public boolean saveDataflow(Component parentComponent,
+                       WorkflowBundle workflowBundle) {
+               if (!fileManager.canSaveWithoutDestination(workflowBundle))
+                       return 
saveWorkflowAsAction.saveDataflow(parentComponent,
+                                       workflowBundle);
+
+               try {
+                       try {
+                               fileManager.saveDataflow(workflowBundle, true);
+                               Object workflowBundleSource = fileManager
+                                               
.getDataflowSource(workflowBundle);
+                               logger.info("Saved workflow " + workflowBundle 
+ " to "
+                                               + workflowBundleSource);
+                               return true;
+                       } catch (OverwriteException ex) {
+                               Object workflowBundleSource = fileManager
+                                               
.getDataflowSource(workflowBundle);
+                               logger.info("Workflow was changed on source: "
+                                               + workflowBundleSource);
+                               fileManager.setCurrentDataflow(workflowBundle);
+                               String msg = "Workflow destination " + 
workflowBundleSource
+                                               + " has been changed from 
elsewhere, "
+                                               + "are you sure you want to 
overwrite?";
+                               int ret = showConfirmDialog(parentComponent, 
msg,
+                                               "Workflow changed", 
YES_NO_CANCEL_OPTION);
+                               if (ret == YES_OPTION) {
+                                       
fileManager.saveDataflow(workflowBundle, false);
+                                       logger.info("Saved workflow " + 
workflowBundle
+                                                       + " by overwriting " + 
workflowBundleSource);
+                                       return true;
+                               } else if (ret == NO_OPTION) {
+                                       // Pop up Save As instead to choose 
another name
+                                       return 
saveWorkflowAsAction.saveDataflow(parentComponent,
+                                                       workflowBundle);
+                               } else {
+                                       logger.info("Aborted overwrite of " + 
workflowBundleSource);
+                                       return false;
+                               }
+                       }
+               } catch (SaveException ex) {
+                       logger.warn("Could not save workflow " + 
workflowBundle, ex);
+                       showMessageDialog(parentComponent, "Could not save 
workflow: \n\n"
+                                       + ex.getMessage(), "Warning", 
WARNING_MESSAGE);
+                       return false;
+               } catch (RuntimeException ex) {
+                       logger.warn("Could not save workflow " + 
workflowBundle, ex);
+                       showMessageDialog(parentComponent, "Could not save 
workflow: \n\n"
+                                       + ex.getMessage(), "Warning", 
WARNING_MESSAGE);
+                       return false;
+               }
+       }
+
+       protected void updateEnabledStatus(WorkflowBundle workflowBundle) {
+               setEnabled(workflowBundle != null
+                               && 
fileManager.isDataflowChanged(workflowBundle));
+       }
+
+       private final class EditManagerObserver implements
+                       Observer<EditManagerEvent> {
+               @Override
+               public void notify(Observable<EditManagerEvent> sender,
+                               EditManagerEvent message) throws Exception {
+                       if (message instanceof AbstractDataflowEditEvent) {
+                               WorkflowBundle workflowBundle = 
((AbstractDataflowEditEvent) message)
+                                               .getDataFlow();
+                               if (workflowBundle == 
fileManager.getCurrentDataflow())
+                                       updateEnabledStatus(workflowBundle);
+                       }
+               }
+       }
+
+       private final class FileManagerObserver implements
+                       Observer<FileManagerEvent> {
+               @Override
+               public void notify(Observable<FileManagerEvent> sender,
+                               FileManagerEvent message) throws Exception {
+                       if (message instanceof SavedDataflowEvent)
+                               updateEnabledStatus(((SavedDataflowEvent) 
message)
+                                               .getDataflow());
+                       else if (message instanceof SetCurrentDataflowEvent)
+                               updateEnabledStatus(((SetCurrentDataflowEvent) 
message)
+                                               .getDataflow());
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveWorkflowAsAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveWorkflowAsAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveWorkflowAsAction.java
new file mode 100644
index 0000000..b42fe1c
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/actions/SaveWorkflowAsAction.java
@@ -0,0 +1,218 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.actions;
+
+import static java.awt.event.KeyEvent.VK_F6;
+import static java.awt.event.KeyEvent.VK_S;
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.saveAsIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.net.URL;
+import java.util.List;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.FileType;
+import org.apache.taverna.workbench.file.events.FileManagerEvent;
+import org.apache.taverna.workbench.file.events.SetCurrentDataflowEvent;
+import org.apache.taverna.workbench.file.exceptions.OverwriteException;
+import org.apache.taverna.workbench.file.exceptions.SaveException;
+import org.apache.taverna.workbench.file.impl.FileTypeFileFilter;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.Workflow;
+
+@SuppressWarnings("serial")
+public class SaveWorkflowAsAction extends AbstractAction {
+       private static final String SAVE_WORKFLOW_AS = "Save workflow as...";
+       private static final String PREF_CURRENT_DIR = "currentDir";
+       private static Logger logger = 
Logger.getLogger(SaveWorkflowAsAction.class);
+       private FileManager fileManager;
+
+       public SaveWorkflowAsAction(FileManager fileManager) {
+               super(SAVE_WORKFLOW_AS, saveAsIcon);
+               this.fileManager = fileManager;
+               fileManager.addObserver(new FileManagerObserver());
+               putValue(ACCELERATOR_KEY, getKeyStroke(VK_F6, 0));
+               putValue(MNEMONIC_KEY, VK_S);
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               Component parentComponent = null;
+               if (e.getSource() instanceof Component)
+                       parentComponent = (Component) e.getSource();
+               WorkflowBundle workflowBundle = 
fileManager.getCurrentDataflow();
+               if (workflowBundle == null) {
+                       showMessageDialog(parentComponent, "No workflow open 
yet",
+                                       "No workflow to save", ERROR_MESSAGE);
+                       return;
+               }
+               saveCurrentDataflow(parentComponent);
+       }
+
+       public boolean saveCurrentDataflow(Component parentComponent) {
+               WorkflowBundle workflowBundle = 
fileManager.getCurrentDataflow();
+               return saveDataflow(parentComponent, workflowBundle);
+       }
+
+       private String determineFileName(final WorkflowBundle workflowBundle) {
+               String result;
+               Object source = fileManager.getDataflowSource(workflowBundle);
+               String fileName = null;
+               if (source instanceof File)
+                       fileName = ((File) source).getName();
+               else if (source instanceof URL)
+                       fileName = ((URL) source).getPath();
+
+               if (fileName != null) {
+                       int lastIndex = fileName.lastIndexOf(".");
+                       if (lastIndex > 0)
+                               fileName = fileName.substring(0, 
fileName.lastIndexOf("."));
+                       result = fileName;
+               } else {
+                       Workflow mainWorkflow = 
workflowBundle.getMainWorkflow();
+                       if (mainWorkflow != null)
+                               result = mainWorkflow.getName();
+                       else
+                               result = workflowBundle.getName();
+               }
+               return result;
+       }
+
+       public boolean saveDataflow(Component parentComponent, WorkflowBundle 
workflowBundle) {
+               fileManager.setCurrentDataflow(workflowBundle);
+               JFileChooser fileChooser = new JFileChooser();
+               Preferences prefs = Preferences.userNodeForPackage(getClass());
+               String curDir = prefs
+                               .get(PREF_CURRENT_DIR, 
System.getProperty("user.home"));
+               fileChooser.setDialogTitle(SAVE_WORKFLOW_AS);
+
+               fileChooser.resetChoosableFileFilters();
+               fileChooser.setAcceptAllFileFilterUsed(false);
+
+               List<FileFilter> fileFilters = fileManager
+                               .getSaveFileFilters(File.class);
+               if (fileFilters.isEmpty()) {
+                       logger.warn("No file types found for saving workflow "
+                                       + workflowBundle);
+                       showMessageDialog(parentComponent,
+                                       "No file types found for saving 
workflow.", "Error",
+                                       ERROR_MESSAGE);
+                       return false;
+               }
+               for (FileFilter fileFilter : fileFilters)
+                       fileChooser.addChoosableFileFilter(fileFilter);
+               fileChooser.setFileFilter(fileFilters.get(0));
+               fileChooser.setCurrentDirectory(new File(curDir));
+
+               File possibleName = new File(determineFileName(workflowBundle));
+               boolean tryAgain = true;
+               while (tryAgain) {
+                       tryAgain = false;
+                       fileChooser.setSelectedFile(possibleName);
+                       int returnVal = 
fileChooser.showSaveDialog(parentComponent);
+                       if (returnVal == APPROVE_OPTION) {
+                               prefs.put(PREF_CURRENT_DIR, 
fileChooser.getCurrentDirectory()
+                                               .toString());
+                               File file = fileChooser.getSelectedFile();
+                               FileTypeFileFilter fileFilter = 
(FileTypeFileFilter) fileChooser
+                                               .getFileFilter();
+                               FileType fileType = fileFilter.getFileType();
+                               String extension = "." + 
fileType.getExtension();
+                               if 
(!file.getName().toLowerCase().endsWith(extension)) {
+                                       String newName = file.getName() + 
extension;
+                                       file = new File(file.getParentFile(), 
newName);
+                               }
+
+                               // TODO: Open in separate thread to avoid 
hanging UI
+                               try {
+                                       try {
+                                               
fileManager.saveDataflow(workflowBundle, fileType,
+                                                               file, true);
+                                               logger.info("Saved workflow " + 
workflowBundle + " to "
+                                                               + file);
+                                               return true;
+                                       } catch (OverwriteException ex) {
+                                               logger.info("File already 
exists: " + file);
+                                               String msg = "Are you sure you 
want to overwrite existing file "
+                                                               + file + "?";
+                                               int ret = 
showConfirmDialog(parentComponent, msg,
+                                                               "File already 
exists", YES_NO_CANCEL_OPTION);
+                                               if (ret == YES_OPTION) {
+                                                       
fileManager.saveDataflow(workflowBundle, fileType,
+                                                                       file, 
false);
+                                                       logger.info("Saved 
workflow " + workflowBundle
+                                                                       + " by 
overwriting " + file);
+                                                       return true;
+                                               } else if (ret == NO_OPTION) {
+                                                       tryAgain = true;
+                                                       continue;
+                                               } else {
+                                                       logger.info("Aborted 
overwrite of " + file);
+                                                       return false;
+                                               }
+                                       }
+                               } catch (SaveException ex) {
+                                       logger.warn("Could not save workflow to 
" + file, ex);
+                                       showMessageDialog(parentComponent,
+                                                       "Could not save 
workflow to " + file + ": \n\n"
+                                                                       + 
ex.getMessage(), "Warning",
+                                                       WARNING_MESSAGE);
+                                       return false;
+                               }
+                       }
+               }
+               return false;
+       }
+
+       protected void updateEnabledStatus(WorkflowBundle workflowBundle) {
+               setEnabled(workflowBundle != null);
+       }
+
+       private final class FileManagerObserver implements 
Observer<FileManagerEvent> {
+               @Override
+               public void notify(Observable<FileManagerEvent> sender,
+                               FileManagerEvent message) throws Exception {
+                       if (message instanceof SetCurrentDataflowEvent)
+                               updateEnabledStatus(((SetCurrentDataflowEvent) 
message)
+                                               .getDataflow());
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java
new file mode 100644
index 0000000..4e495a2
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java
@@ -0,0 +1,55 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.hooks;
+
+import org.apache.taverna.workbench.ShutdownSPI;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.impl.actions.CloseAllWorkflowsAction;
+
+/**
+ * Close open workflows (and ask the user if she wants to save changes) on
+ * shutdown.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class CloseWorkflowsOnShutdown implements ShutdownSPI {
+       private CloseAllWorkflowsAction closeAllWorkflowsAction;
+
+       public CloseWorkflowsOnShutdown(EditManager editManager,
+                       FileManager fileManager) {
+               closeAllWorkflowsAction = new 
CloseAllWorkflowsAction(editManager,
+                               fileManager);
+       }
+
+       @Override
+       public int positionHint() {
+               /*
+                * Quite early, we don't want to do various clean-up in case 
the user
+                * clicks Cancel
+                */
+               return 50;
+       }
+
+       @Override
+       public boolean shutdown() {
+               return closeAllWorkflowsAction.closeAllWorkflows(null);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileCloseAllMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileCloseAllMenuAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileCloseAllMenuAction.java
new file mode 100644
index 0000000..6449ba7
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileCloseAllMenuAction.java
@@ -0,0 +1,50 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.menu;
+
+import static 
org.apache.taverna.workbench.file.impl.menu.FileOpenMenuSection.FILE_URI;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.ui.menu.AbstractMenuAction;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.impl.actions.CloseAllWorkflowsAction;
+
+public class FileCloseAllMenuAction extends AbstractMenuAction {
+       private static final URI FILE_CLOSE_URI = URI
+                       
.create("http://taverna.sf.net/2008/t2workbench/menu#fileCloseAll";);
+       private final EditManager editManager;
+       private final FileManager fileManager;
+
+       public FileCloseAllMenuAction(EditManager editManager,
+                       FileManager fileManager) {
+               super(FILE_URI, 39, FILE_CLOSE_URI);
+               this.editManager = editManager;
+               this.fileManager = fileManager;
+       }
+
+       @Override
+       protected Action createAction() {
+               return new CloseAllWorkflowsAction(editManager, fileManager);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileCloseMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileCloseMenuAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileCloseMenuAction.java
new file mode 100644
index 0000000..8579c2e
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileCloseMenuAction.java
@@ -0,0 +1,49 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.menu;
+
+import static 
org.apache.taverna.workbench.file.impl.menu.FileOpenMenuSection.FILE_URI;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.ui.menu.AbstractMenuAction;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.impl.actions.CloseWorkflowAction;
+
+public class FileCloseMenuAction extends AbstractMenuAction {
+       private static final URI FILE_CLOSE_URI = URI
+                       
.create("http://taverna.sf.net/2008/t2workbench/menu#fileClose";);
+       private final EditManager editManager;
+       private final FileManager fileManager;
+
+       public FileCloseMenuAction(EditManager editManager, FileManager 
fileManager) {
+               super(FILE_URI, 30, FILE_CLOSE_URI);
+               this.editManager = editManager;
+               this.fileManager = fileManager;
+       }
+
+       @Override
+       protected Action createAction() {
+               return new CloseWorkflowAction(editManager, fileManager);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/bf8a7ea2/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileNewMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileNewMenuAction.java
 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileNewMenuAction.java
new file mode 100644
index 0000000..691729b
--- /dev/null
+++ 
b/taverna-file-impl/src/main/java/org/apache/taverna/workbench/file/impl/menu/FileNewMenuAction.java
@@ -0,0 +1,46 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.file.impl.menu;
+
+import static 
org.apache.taverna.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.ui.menu.AbstractMenuAction;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.impl.actions.NewWorkflowAction;
+
+public class FileNewMenuAction extends AbstractMenuAction {
+       private static final URI FILE_NEW_URI = URI
+                       
.create("http://taverna.sf.net/2008/t2workbench/menu#fileNew";);
+       private final FileManager fileManager;
+
+       public FileNewMenuAction(FileManager fileManager) {
+               super(FILE_OPEN_SECTION_URI, 10, FILE_NEW_URI);
+               this.fileManager = fileManager;
+       }
+
+       @Override
+       protected Action createAction() {
+               return new NewWorkflowAction(fileManager);
+       }
+}

Reply via email to