From 7b26194724278b039125eb483160f20605604838 Mon Sep 17 00:00:00 2001
From: Steve Williams <stephen.williams@stfc.ac.uk>
Date: Fri, 23 Jul 2010 12:02:11 +0000
Subject: [PATCH] There is a new dialog that allows saving multiple workspaces
 in multiple formats. It's accessible from the SANS GUI under "Save Other".
 Other controls have been added to the SANS GUI to aid loading files and the
 interface has been rearranged a little. re #1263

---
 .../PythonAPI/scripts/SANS/SANSInsts.py       |   1 +
 .../CustomInterfaces/inc/SANSRunWindow.h      |  75 +++-
 .../CustomInterfaces/inc/SANSRunWindow.ui     | 400 ++++++++++--------
 .../CustomInterfaces/src/SANSRunWindow.cpp    | 313 +++++++++++---
 .../MantidQt/MantidWidgets/MantidWidgets.pro  |   6 +-
 .../MantidWidgets/inc/SaveWorkspaces.h        |  87 ++++
 .../MantidWidgets/src/SaveWorkspaces.cpp      | 299 +++++++++++++
 7 files changed, 927 insertions(+), 254 deletions(-)
 create mode 100644 Code/qtiplot/MantidQt/MantidWidgets/inc/SaveWorkspaces.h
 create mode 100644 Code/qtiplot/MantidQt/MantidWidgets/src/SaveWorkspaces.cpp

diff --git a/Code/Mantid/PythonAPI/scripts/SANS/SANSInsts.py b/Code/Mantid/PythonAPI/scripts/SANS/SANSInsts.py
index 1c3b03acd35..efe657e692e 100644
--- a/Code/Mantid/PythonAPI/scripts/SANS/SANSInsts.py
+++ b/Code/Mantid/PythonAPI/scripts/SANS/SANSInsts.py
@@ -97,6 +97,7 @@ def setCurInst(name) :
     _curInst = allInsts[name]
 
 def setDetector(detName) :
+    global _curInst
     if _curInst.setDetector(detName) :
         global curDetector
         curDetector = _curInst.curDetector()
diff --git a/Code/qtiplot/MantidQt/CustomInterfaces/inc/SANSRunWindow.h b/Code/qtiplot/MantidQt/CustomInterfaces/inc/SANSRunWindow.h
index 8d264ae7454..ec51383abbe 100644
--- a/Code/qtiplot/MantidQt/CustomInterfaces/inc/SANSRunWindow.h
+++ b/Code/qtiplot/MantidQt/CustomInterfaces/inc/SANSRunWindow.h
@@ -7,8 +7,10 @@
 #include "MantidQtCustomInterfaces/ui_SANSRunWindow.h"
 #include "MantidQtAPI/UserSubWindow.h"
 #include "MantidQtCustomInterfaces/SANSAddFiles.h"
+#include "MantidQtMantidWidgets/SaveWorkspaces.h"
 
 #include <QHash>
+#include <QSettings>
 #include <QStringList>
 #include "Poco/NObserver.h"
 #include "MantidAPI/AnalysisDataService.h"
@@ -26,23 +28,37 @@ namespace Mantid
   }
 }
 
-//---------------------------
-// Qt Forward Declarations
-//---------------------------
-class QLineEdit;
-class QSignalMapper;
-class QLabel;
 class QAction;
 
 namespace MantidQt
 {
 namespace CustomInterfaces
-{
-//-----------------------------
-// Forward declaration
-//-----------------------------
+{
+	/** 
+    Implements the SANS, small angle nuetron scattering, dialog box
+
+    @author Martyn Gigg
+
+    Copyright &copy; 2010 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
+
+    This file is part of Mantid.
+
+    Mantid is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    Mantid is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
 
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+    File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
+    Code Documentation is available at: <http://doxygen.mantidproject.org>    
+    */
 class SANSRunWindow : public MantidQt::API::UserSubWindow
 {
   Q_OBJECT
@@ -69,14 +85,22 @@ private:
   virtual void initLocalPython();
   /**@name Utility functions */
   //@{
+  /// Initialise some of the data and signal connections in the save box
+  void setupSaveBox();
   /// connect the buttons click events to signals
   void connectButtonSignals();
+  /// connect signals from the textChanged() signal from text boxes, index changed on ComboBoxes etc.
+  void connectChangeSignals();
   /// Create the necessary widget maps
   void initWidgetMaps();
   ///Read previous settings
   void readSettings();
+  /// Sets the states of the save box controls to the states read from the QSettings object
+  void readSaveSettings(QSettings & valueStore);
   ///Save settings
   void saveSettings();
+  ///Stores the state of the save box controls in QSettings
+  void saveSaveSettings(QSettings & valueStore);
   /// Run a reduce function
   QString runReduceScriptFunction(const QString & pycode);
   /// Trim python print markers
@@ -116,6 +140,8 @@ private:
   void setLOQGeometry(boost::shared_ptr<Mantid::API::MatrixWorkspace> workspace, int wscode);
   /// Mark an error on a label
   void markError(QLabel* label);
+  /// set the name of the output workspace, empty means there is no output
+  void resetDefaultOutput(const QString & wsName="");
   /// Run an assign command
   bool runAssign(int key, QString & logs);
   /// Get the detectors' names
@@ -153,6 +179,12 @@ private slots:
   void selectUserFile();
   /// Select a CSV file
   void selectCSVFile();
+  /// Raises a browse dialog to select the filename used to save the output
+  void saveFileBrowse();
+  /// Raises a dialog that allows people to save multiple workspaces
+  void saveWorkspacesDialog();
+  ///deals with the save workspaces dialog box closing
+  void saveWorkspacesClosed();
   /// A run number has changed
   void runChanged();
   /// Receive a load button click
@@ -163,16 +195,20 @@ private slots:
   void handlePlotButtonClick();
   /// Find centre button click handler
   void handleRunFindCentre();
-  ///Handle save button click
-  void handleSaveButtonClick();
+  ///Save the output from a single run reduction in the user selected formats
+  void handleDefSaveClick();
   /// A ComboBox option change
   void handleStepComboChange(int new_index);
   /// Called when the show mask button has been clicked
   void handleShowMaskButtonClick();
   ///Handle the change in instrument 
   void handleInstrumentChange(int);
+  ///Record if that user has changed the default filename
+  void setUserFname();
   /// Update the centre finding progress
   void updateCentreFindingStatus(const QString & msg);
+  /// Enable the default save button only if there an output workspace and a filename to save it to
+  void enableOrDisableDefaultSave();
   /// Append log message to log window
   void updateLogWindow(const QString & msg);
   /// Switch mode
@@ -193,6 +229,8 @@ private:
   Ui::SANSRunWindow m_uiForm;
   /// this object holds the functionality in the Add Files tab
   SANSAddFiles *m_addFilesTab;
+  /// this points to a saveWorkspaces, which allows users to save any workspace, when one is opened
+  MantidWidgets::SaveWorkspaces *m_saveWorkspaces;
   /// The data directory (as an absolute path)
   QString m_data_dir;
   /// The instrument definition directory
@@ -201,6 +239,8 @@ private:
   QString m_last_dir;
   /// Is the user file loaded
   bool m_cfg_loaded;
+  ///True if the user cahnged the default filename text, false otherwise
+  bool m_userFname;
   /// The sample that was loaded
   QString m_sample_no;
   /// A map for quickly retrieving the different line edits
@@ -209,12 +249,17 @@ private:
   QHash<int, QLabel*> m_period_lbls;
   /// A list of the full workspace names
   QHash<int, QString> m_workspace_names;
-  // A signal mapper to pick up various button clicks
+  /// Stores the last output workspace from single run mode, should be emptied when run in batch mode
+  QString m_outputWS;
+  /// A signal mapper to pick up various button clicks
   QSignalMapper *m_reducemapper;
-  // A flag to mark that warnings have been issued about geometry issues
+  /// A flag to mark that warnings have been issued about geometry issues
   bool m_warnings_issued;
-  // A flag that causes the reload of the data
+  /// A flag that causes the reload of the data
   bool m_force_reload;
+  /// Holds pointers to the check box for each supported save format with the name of its save algorithm
+  QHash<const QCheckBox * const, QString> m_savFormats;
+  typedef QHash<const QCheckBox * const, QString>::const_iterator SavFormatsConstIt;
   /// A flag indicating there were warning messsages in the log
   bool m_log_warnings;
   // An observer for a delete notification from Mantid
diff --git a/Code/qtiplot/MantidQt/CustomInterfaces/inc/SANSRunWindow.ui b/Code/qtiplot/MantidQt/CustomInterfaces/inc/SANSRunWindow.ui
index e5349a921d7..57cbd609a34 100644
--- a/Code/qtiplot/MantidQt/CustomInterfaces/inc/SANSRunWindow.ui
+++ b/Code/qtiplot/MantidQt/CustomInterfaces/inc/SANSRunWindow.ui
@@ -5,8 +5,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>713</width>
-    <height>415</height>
+    <width>716</width>
+    <height>443</height>
    </rect>
   </property>
   <property name="sizePolicy" >
@@ -73,8 +73,8 @@
       <attribute name="title" >
        <string>Run Numbers</string>
       </attribute>
-      <layout class="QVBoxLayout" name="verticalLayout_4" >
-       <item>
+      <layout class="QGridLayout" name="gridLayout_21" >
+       <item row="0" column="0" colspan="2" >
         <layout class="QHBoxLayout" name="horizontalLayout_12" >
          <item>
           <widget class="QLabel" name="label_7" >
@@ -190,7 +190,7 @@
          </item>
         </layout>
        </item>
-       <item>
+       <item row="1" column="0" colspan="2" >
         <layout class="QHBoxLayout" name="horizontalLayout_8" >
          <item>
           <widget class="QRadioButton" name="single_mode_btn" >
@@ -225,16 +225,122 @@
            </property>
           </spacer>
          </item>
+         <item>
+          <widget class="QGroupBox" name="groupBox_12" >
+           <property name="title" >
+            <string/>
+           </property>
+           <property name="flat" >
+            <bool>false</bool>
+           </property>
+           <layout class="QHBoxLayout" name="horizontalLayout_16" >
+            <item>
+             <widget class="QLabel" name="label_34" >
+              <property name="text" >
+               <string>Instrument:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QComboBox" name="inst_opt" >
+              <item>
+               <property name="text" >
+                <string>LOQ</string>
+               </property>
+              </item>
+              <item>
+               <property name="text" >
+                <string>SANS2D</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_26" >
+              <property name="text" >
+               <string>File type:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QComboBox" name="file_opt" />
+            </item>
+           </layout>
+          </widget>
+         </item>
         </layout>
        </item>
-       <item>
+       <item row="4" column="0" >
+        <widget class="QGroupBox" name="groupBox_4" >
+         <property name="title" >
+          <string>Options</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_20" >
+          <item row="0" column="1" >
+           <widget class="QCheckBox" name="verbose_check" >
+            <property name="text" >
+             <string>Verbose</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="2" >
+           <widget class="QCheckBox" name="log_colette" >
+            <property name="text" >
+             <string>Log Colette cmds</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="0" >
+           <widget class="QCheckBox" name="plot_check" >
+            <property name="text" >
+             <string>Plot result</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="5" column="0" >
+        <widget class="QGroupBox" name="groupBox_7" >
+         <property name="title" >
+          <string>Reduce</string>
+         </property>
+         <property name="flat" >
+          <bool>false</bool>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_19" >
+          <item row="0" column="0" >
+           <widget class="QPushButton" name="load_dataBtn" >
+            <property name="text" >
+             <string>Load Data</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1" >
+           <widget class="QPushButton" name="oneDBtn" >
+            <property name="text" >
+             <string>1D Reduce</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="2" >
+           <widget class="QPushButton" name="twoDBtn" >
+            <property name="text" >
+             <string>2D Reduce</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="2" column="0" colspan="2" >
         <widget class="Line" name="line_2" >
          <property name="orientation" >
           <enum>Qt::Horizontal</enum>
          </property>
         </widget>
        </item>
-       <item>
+       <item row="3" column="0" colspan="2" >
         <widget class="QStackedWidget" name="mode_stack" >
          <property name="currentIndex" >
           <number>1</number>
@@ -927,179 +1033,119 @@
          </widget>
         </widget>
        </item>
-       <item>
-        <layout class="QHBoxLayout" name="horizontalLayout_10" >
-         <item>
-          <widget class="QGroupBox" name="groupBox_4" >
-           <property name="title" >
-            <string>Options</string>
-           </property>
-           <layout class="QHBoxLayout" name="horizontalLayout_2" >
-            <property name="topMargin" >
-             <number>2</number>
-            </property>
-            <item>
-             <widget class="QCheckBox" name="plot_check" >
-              <property name="text" >
-               <string>Plot result</string>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QCheckBox" name="verbose_check" >
-              <property name="text" >
-               <string>Verbose</string>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QCheckBox" name="log_colette" >
-              <property name="text" >
-               <string>Log Colette cmds</string>
-              </property>
-             </widget>
-            </item>
-           </layout>
-          </widget>
-         </item>
-         <item>
-          <spacer name="horizontalSpacer_4" >
-           <property name="orientation" >
-            <enum>Qt::Horizontal</enum>
-           </property>
-           <property name="sizeHint" stdset="0" >
-            <size>
-             <width>40</width>
-             <height>20</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-        </layout>
-       </item>
-       <item>
-        <spacer name="verticalSpacer_5" >
-         <property name="orientation" >
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0" >
-          <size>
-           <width>20</width>
-           <height>40</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-       <item>
-        <layout class="QHBoxLayout" name="horizontalLayout_15" >
-         <property name="spacing" >
-          <number>6</number>
-         </property>
-         <property name="topMargin" >
-          <number>0</number>
+       <item rowspan="2" row="4" column="1" >
+        <widget class="QGroupBox" name="groupBox_5" >
+         <property name="title" >
+          <string>Save</string>
          </property>
-         <item>
-          <widget class="QGroupBox" name="groupBox_7" >
-           <property name="title" >
-            <string>Reduce</string>
-           </property>
-           <property name="flat" >
-            <bool>false</bool>
-           </property>
-           <layout class="QGridLayout" name="gridLayout" >
-            <item row="0" column="0" >
-             <widget class="QPushButton" name="load_dataBtn" >
-              <property name="text" >
-               <string>Load Data</string>
-              </property>
-             </widget>
-            </item>
-            <item row="0" column="1" colspan="2" >
-             <widget class="QPushButton" name="oneDBtn" >
-              <property name="text" >
-               <string>1D Reduce</string>
-              </property>
-             </widget>
-            </item>
-            <item row="0" column="3" >
-             <widget class="QPushButton" name="twoDBtn" >
-              <property name="text" >
-               <string>2D Reduce</string>
-              </property>
-             </widget>
-            </item>
-            <item row="0" column="4" >
-             <widget class="QPushButton" name="saveBtn" >
-              <property name="text" >
-               <string>Save</string>
-              </property>
-             </widget>
-            </item>
-           </layout>
-          </widget>
-         </item>
-         <item>
-          <spacer name="horizontalSpacer_8" >
-           <property name="orientation" >
-            <enum>Qt::Horizontal</enum>
-           </property>
-           <property name="sizeHint" stdset="0" >
-            <size>
-             <width>40</width>
-             <height>20</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-         <item>
-          <widget class="QGroupBox" name="groupBox_12" >
-           <property name="title" >
-            <string/>
-           </property>
-           <property name="flat" >
-            <bool>false</bool>
-           </property>
-           <layout class="QHBoxLayout" name="horizontalLayout_16" >
-            <item>
-             <widget class="QLabel" name="label_34" >
-              <property name="text" >
-               <string>Instrument:</string>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QComboBox" name="inst_opt" >
-              <item>
-               <property name="text" >
-                <string>LOQ</string>
-               </property>
-              </item>
-              <item>
-               <property name="text" >
-                <string>SANS2D</string>
-               </property>
-              </item>
-             </widget>
-            </item>
-            <item>
-             <widget class="QLabel" name="label_26" >
-              <property name="text" >
-               <string>File type:</string>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QComboBox" name="file_opt" />
-            </item>
-           </layout>
-          </widget>
-         </item>
-        </layout>
+         <layout class="QGridLayout" name="gridLayout" >
+          <item rowspan="2" row="0" column="4" >
+           <widget class="QPushButton" name="saveFilename_btn" >
+            <property name="minimumSize" >
+             <size>
+              <width>0</width>
+              <height>0</height>
+             </size>
+            </property>
+            <property name="toolTip" >
+             <string>Save the output workspace to this file</string>
+            </property>
+            <property name="text" >
+             <string>Browse</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="4" >
+           <widget class="QCheckBox" name="saveCSV_check" >
+            <property name="toolTip" >
+             <string>Select one or more file formats</string>
+            </property>
+            <property name="text" >
+             <string>CSV</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="3" >
+           <widget class="QCheckBox" name="saveRKH_check" >
+            <property name="toolTip" >
+             <string>Select one or more file formats</string>
+            </property>
+            <property name="text" >
+             <string>RKH</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="2" >
+           <widget class="QCheckBox" name="saveCan_check" >
+            <property name="toolTip" >
+             <string>Select one or more file formats</string>
+            </property>
+            <property name="text" >
+             <string>CanSAS</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="1" >
+           <widget class="QCheckBox" name="saveNex_check" >
+            <property name="toolTip" >
+             <string>Select one or more file formats</string>
+            </property>
+            <property name="text" >
+             <string>Nexus</string>
+            </property>
+           </widget>
+          </item>
+          <item rowspan="2" row="0" column="0" >
+           <widget class="QLabel" name="label_58" >
+            <property name="toolTip" >
+             <string>Save the output workspace to this file</string>
+            </property>
+            <property name="text" >
+             <string>Filename:</string>
+            </property>
+           </widget>
+          </item>
+          <item rowspan="2" row="2" column="0" >
+           <widget class="QLabel" name="label_64" >
+            <property name="toolTip" >
+             <string>Select one or more file formats</string>
+            </property>
+            <property name="text" >
+             <string>Formats:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="4" column="1" colspan="2" >
+           <widget class="QPushButton" name="saveDefault_btn" >
+            <property name="toolTip" >
+             <string>Save the workspace from a single run file reduction</string>
+            </property>
+            <property name="text" >
+             <string>Save Result</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1" colspan="3" >
+           <widget class="QLineEdit" name="outfile_edit" >
+            <property name="toolTip" >
+             <string>Save the output workspace to this file</string>
+            </property>
+           </widget>
+          </item>
+          <item row="4" column="3" colspan="2" >
+           <widget class="QPushButton" name="saveSel_btn" >
+            <property name="toolTip" >
+             <string>Save multiple workspaces</string>
+            </property>
+            <property name="text" >
+             <string>Save Other</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
        </item>
       </layout>
-      <zorder>line_2</zorder>
-      <zorder>mode_stack</zorder>
-      <zorder></zorder>
      </widget>
      <widget class="QWidget" name="tab_2" >
       <attribute name="title" >
diff --git a/Code/qtiplot/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp b/Code/qtiplot/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp
index 2369407adfe..c6895f26d80 100644
--- a/Code/qtiplot/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp
+++ b/Code/qtiplot/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp
@@ -24,7 +24,6 @@
 #include <QHash>
 #include <QTextStream>
 #include <QTreeWidgetItem>
-#include <QSettings>
 #include <QMessageBox>
 #include <QInputDialog>
 #include <QSignalMapper>
@@ -45,6 +44,7 @@ namespace CustomInterfaces
 }
 }
 
+using namespace MantidQt::MantidWidgets;
 using namespace MantidQt::CustomInterfaces;
 using namespace Mantid::Kernel;
 using namespace Mantid::API;
@@ -57,8 +57,9 @@ Logger& SANSRunWindow::g_log = Logger::get("SANSRunWindow");
 //----------------------------------------------
 ///Constructor
 SANSRunWindow::SANSRunWindow(QWidget *parent) :
-  UserSubWindow(parent), m_addFilesTab(NULL), m_data_dir(""), m_ins_defdir(""),
-  m_last_dir(""), m_cfg_loaded(true), m_sample_no(), m_run_no_boxes(),
+  UserSubWindow(parent), m_addFilesTab(NULL), m_saveWorkspaces(NULL),
+  m_data_dir(""), m_ins_defdir(""), m_last_dir(""),
+  m_cfg_loaded(true), m_userFname(false), m_sample_no(), m_run_no_boxes(),
   m_period_lbls(), m_warnings_issued(false), m_force_reload(false),
   m_log_warnings(false),
   m_delete_observer(*this, &SANSRunWindow::handleMantidDeleteWorkspace),
@@ -93,27 +94,17 @@ void SANSRunWindow::initLayout()
   m_reducemapper = new QSignalMapper(this);
   m_mode_mapper = new QSignalMapper(this);
 
-  m_allowed_batchtags.insert("sample_sans",0);
-  m_allowed_batchtags.insert("sample_trans",1);
-  m_allowed_batchtags.insert("sample_direct_beam",2);
-  m_allowed_batchtags.insert("can_sans",3);
-  m_allowed_batchtags.insert("can_trans",4);
-  m_allowed_batchtags.insert("can_direct_beam",5);
-  m_allowed_batchtags.insert("background_sans",-1);
-  m_allowed_batchtags.insert("background_trans",-1);
-  m_allowed_batchtags.insert("background_direct_beam",-1);
-  m_allowed_batchtags.insert("output_as",6);
-
-
   //Set column stretch on the mask table
   m_uiForm.mask_table->horizontalHeader()->setStretchLastSection(true);
 
-  //Button connections
+  setupSaveBox();
+  
   connectButtonSignals();
 
   // Disable most things so that load is the only thing that can be done
   m_uiForm.oneDBtn->setEnabled(false);
   m_uiForm.twoDBtn->setEnabled(false);
+  m_uiForm.saveDefault_btn->setEnabled(false);
   for( int i = 1; i < 4; ++i)
   {
     m_uiForm.tabWidget->setTabEnabled(i, false);
@@ -147,27 +138,7 @@ void SANSRunWindow::initLayout()
   //Create the widget hash maps
   initWidgetMaps();
 
-  //Connect each box's edited signal to flag if the box's text has changed
-  for( int idx = 0; idx < 9; ++idx )
-  {
-    connect(m_run_no_boxes.value(idx), SIGNAL(textEdited(const QString&)), this, SLOT(runChanged()));
-  }
-
-  connect(m_uiForm.smpl_offset, SIGNAL(textEdited(const QString&)), this, SLOT(runChanged()));
-
-  // Combo boxes
-  connect(m_uiForm.wav_dw_opt, SIGNAL(currentIndexChanged(int)), this, 
-    SLOT(handleStepComboChange(int)));
-  connect(m_uiForm.q_dq_opt, SIGNAL(currentIndexChanged(int)), this, 
-    SLOT(handleStepComboChange(int)));
-  connect(m_uiForm.qy_dqy_opt, SIGNAL(currentIndexChanged(int)), this, 
-    SLOT(handleStepComboChange(int)));
-
-  connect(m_uiForm.inst_opt, SIGNAL(currentIndexChanged(int)), this, 
-    SLOT(handleInstrumentChange(int)));
-
-  // Default transmission switch
-  connect(m_uiForm.def_trans, SIGNAL(stateChanged(int)), this, SLOT(updateTransInfo(int)));
+  connectChangeSignals();
 
   // Add Python set functions as underlying data 
   m_uiForm.inst_opt->setItemData(0, "LOQ()");
@@ -211,6 +182,55 @@ void SANSRunWindow::initLocalPython()
     setProcessingState(true, -1);    
   }
 }
+/** Initialise some of the data and signal connections in the save box
+*/
+void SANSRunWindow::setupSaveBox()
+{
+  connect(m_uiForm.saveDefault_btn, SIGNAL(clicked()), this, SLOT(handleDefSaveClick()));
+  connect(m_uiForm.saveSel_btn, SIGNAL(clicked()),
+    this, SLOT(saveWorkspacesDialog()));
+  connect(m_uiForm.saveFilename_btn, SIGNAL(clicked()),
+    this, SLOT(saveFileBrowse()));
+  connect(m_uiForm.outfile_edit, SIGNAL(textEdited(const QString &)),
+    this, SLOT(setUserFname()));
+
+  //link the save option tick boxes to their save algorithm
+  m_savFormats.insert(m_uiForm.saveNex_check, "SaveNexus");
+  m_savFormats.insert(m_uiForm.saveCan_check, "SaveCanSAS1D");
+  m_savFormats.insert(m_uiForm.saveRKH_check, "SaveRKH");
+  m_savFormats.insert(m_uiForm.saveCSV_check, "SaveCSV");
+
+  for(SavFormatsConstIt i=m_savFormats.begin(); i != m_savFormats.end(); ++i)
+  {
+    connect(i.key(), SIGNAL(stateChanged(int)),
+      this, SLOT(enableOrDisableDefaultSave()));
+  }
+}
+/** Raises a saveWorkspaces dialog which allows people to save any workspace or
+*  workspaces the user chooses
+*/
+void SANSRunWindow::saveWorkspacesDialog()
+{
+  //this dialog must have delete on close selected to aviod a memory leak
+  m_saveWorkspaces =
+    new SaveWorkspaces(this, m_uiForm.outfile_edit->text(), m_savFormats);
+  //this dialog sometimes needs to run Python, pass this to Mantidplot via our runAsPythonScript() signal
+  connect(m_saveWorkspaces, SIGNAL(runAsPythonScript(const QString&)),
+    this, SIGNAL(runAsPythonScript(const QString&)));
+  //we need know if we have a pointer to a valid window or not
+  connect(m_saveWorkspaces, SIGNAL(closing()),
+    this, SLOT(saveWorkspacesClosed()));
+  m_uiForm.saveSel_btn->setEnabled(false);
+  m_saveWorkspaces->show();
+}
+/**When the save workspaces dialog box is closes its pointer, m_saveWorkspaces,
+* is set to NULL and the raise dialog button is re-enabled
+*/
+void SANSRunWindow::saveWorkspacesClosed()
+{
+  m_uiForm.saveSel_btn->setEnabled(true);
+  m_saveWorkspaces = NULL;
+}
 /** Connection the buttons to their signals
 */
 void SANSRunWindow::connectButtonSignals()
@@ -221,7 +241,6 @@ void SANSRunWindow::connectButtonSignals()
 
   connect(m_uiForm.load_dataBtn, SIGNAL(clicked()), this, SLOT(handleLoadButtonClick()));
   connect(m_uiForm.runcentreBtn, SIGNAL(clicked()), this, SLOT(handleRunFindCentre()));
-  connect(m_uiForm.saveBtn, SIGNAL(clicked()), this, SLOT(handleSaveButtonClick()));
 
   // Reduction buttons
   connect(m_uiForm.oneDBtn, SIGNAL(clicked()), m_reducemapper, SLOT(map()));
@@ -233,11 +252,41 @@ void SANSRunWindow::connectButtonSignals()
   connect(m_uiForm.showMaskBtn, SIGNAL(clicked()), this, SLOT(handleShowMaskButtonClick()));
   connect(m_uiForm.clear_log, SIGNAL(clicked()), m_uiForm.centre_logging, SLOT(clear()));
 }
+/** Connect signals from the textChanged() signal from text boxes, index changed
+*  on ComboBoxes etc.
+*/
+void SANSRunWindow::connectChangeSignals()
+{
+  //Connect each box's edited signal to flag if the box's text has changed
+  for( int idx = 0; idx < 9; ++idx )
+  {
+    connect(m_run_no_boxes.value(idx), SIGNAL(textEdited(const QString&)), this, SLOT(runChanged()));
+  }
+
+  connect(m_uiForm.smpl_offset, SIGNAL(textEdited(const QString&)), this, SLOT(runChanged()));
+  connect(m_uiForm.outfile_edit, SIGNAL(textEdited(const QString&)),
+    this, SLOT(enableOrDisableDefaultSave()));
+
+  // Combo boxes
+  connect(m_uiForm.wav_dw_opt, SIGNAL(currentIndexChanged(int)), this, 
+    SLOT(handleStepComboChange(int)));
+  connect(m_uiForm.q_dq_opt, SIGNAL(currentIndexChanged(int)), this, 
+    SLOT(handleStepComboChange(int)));
+  connect(m_uiForm.qy_dqy_opt, SIGNAL(currentIndexChanged(int)), this, 
+    SLOT(handleStepComboChange(int)));
+
+  connect(m_uiForm.inst_opt, SIGNAL(currentIndexChanged(int)), this, 
+    SLOT(handleInstrumentChange(int)));
+
+  // Default transmission switch
+  connect(m_uiForm.def_trans, SIGNAL(stateChanged(int)), this, SLOT(updateTransInfo(int)));
+}
 /**
  * Initialize the widget maps
  */
 void SANSRunWindow::initWidgetMaps()
 {
+  //          single run mode settings
     //Text edit map
     m_run_no_boxes.insert(0, m_uiForm.sct_sample_edit);
     m_run_no_boxes.insert(1, m_uiForm.sct_can_edit);
@@ -249,7 +298,7 @@ void SANSRunWindow::initWidgetMaps()
     m_run_no_boxes.insert(7, m_uiForm.direct_can_edit);
     m_run_no_boxes.insert(8, m_uiForm.direct_bkgd_edit);
 
-        //Period label hash. Each label has a buddy set to its corresponding text edit field
+    //Period label hash. Each label has a buddy set to its corresponding text edit field
     m_period_lbls.insert(0, m_uiForm.sct_prd_tot1);
     m_period_lbls.insert(1, m_uiForm.sct_prd_tot2);
     m_period_lbls.insert(2, m_uiForm.sct_prd_tot3);
@@ -260,7 +309,20 @@ void SANSRunWindow::initWidgetMaps()
     m_period_lbls.insert(7, m_uiForm.direct_prd_tot2);   
     m_period_lbls.insert(8, m_uiForm.direct_prd_tot3);
 
-    // SANS2D det names/label map
+  //       batch mode settings
+  m_allowed_batchtags.insert("sample_sans",0);
+  m_allowed_batchtags.insert("sample_trans",1);
+  m_allowed_batchtags.insert("sample_direct_beam",2);
+  m_allowed_batchtags.insert("can_sans",3);
+  m_allowed_batchtags.insert("can_trans",4);
+  m_allowed_batchtags.insert("can_direct_beam",5);
+  m_allowed_batchtags.insert("background_sans",-1);
+  m_allowed_batchtags.insert("background_trans",-1);
+  m_allowed_batchtags.insert("background_direct_beam",-1);
+  m_allowed_batchtags.insert("output_as",6);
+
+  //            detector info  
+  // SANS2D det names/label map
     QHash<QString, QLabel*> labelsmap;
     labelsmap.insert("Front_Det_Z", m_uiForm.dist_smp_frontZ);
     labelsmap.insert("Front_Det_X", m_uiForm.dist_smp_frontX);
@@ -342,10 +404,24 @@ void SANSRunWindow::readSettings()
   m_uiForm.file_opt->setCurrentIndex(value_store.value("fileextension", 0).toInt());
 
   value_store.endGroup();
+  readSaveSettings(value_store);
 
   g_log.debug() << "Found previous data directory " << m_uiForm.datadir_edit->text().toStdString()
     << "\nFound previous user mask file" << m_uiForm.userfile_edit->text().toStdString() 
     << "\nFound instrument definition directory " << m_ins_defdir.toStdString() << std::endl;
+
+}
+/** Sets the states of the checkboxes in the save box using those
+* in the passed QSettings object
+*  @param valueStore where the settings will be stored
+*/
+void SANSRunWindow::readSaveSettings(QSettings & valueStore)
+{
+  valueStore.beginGroup("CustomInterfaces/SANSRunWindow/SaveOutput");
+  m_uiForm.saveNex_check->setChecked(valueStore.value("nexus",false).toBool());
+  m_uiForm.saveCan_check->setChecked(valueStore.value("canSAS",false).toBool());
+  m_uiForm.saveRKH_check->setChecked(valueStore.value("RKH", false).toBool());
+  m_uiForm.saveCSV_check->setChecked(valueStore.value("CSV", false).toBool());
 }
 
 /**
@@ -379,8 +455,20 @@ void SANSRunWindow::saveSettings()
   }
   value_store.setValue("runmode",mode_id);
   value_store.endGroup();
+  saveSaveSettings(value_store);
+}
+/** Stores the state of the checkboxes in the save box with the
+* passed QSettings object
+*  @param valueStore where the settings will be stored
+*/
+void SANSRunWindow::saveSaveSettings(QSettings & valueStore)
+{
+  valueStore.beginGroup("CustomInterfaces/SANSRunWindow/SaveOutput");
+  valueStore.setValue("nexus", m_uiForm.saveNex_check->isChecked());
+  valueStore.setValue("canSAS", m_uiForm.saveCan_check->isChecked());
+  valueStore.setValue("RKH", m_uiForm.saveRKH_check->isChecked());
+  valueStore.setValue("CSV", m_uiForm.saveCSV_check->isChecked());
 }
-
 /**
  * Run a function from the SANS reduction script, ensuring that the first call imports the module
  * @param pycode The code to execute
@@ -766,16 +854,18 @@ void SANSRunWindow::setProcessingState(bool running, int type)
     m_uiForm.load_dataBtn->setEnabled(false);
   }
 
+  //buttons that are available as long as Python is available
   m_uiForm.oneDBtn->setEnabled(!running);
   m_uiForm.twoDBtn->setEnabled(!running);
-  //m_uiForm.plotBtn->setEnabled(!running);
-  m_uiForm.saveBtn->setEnabled(!running);
+  m_uiForm.saveSel_btn->setEnabled(!running);
   m_uiForm.runcentreBtn->setEnabled(!running);
   m_uiForm.userfileBtn->setEnabled(!running);
   m_uiForm.data_dirBtn->setEnabled(!running);
-
+  
   if( running )
   {
+    m_uiForm.saveDefault_btn->setEnabled(false);
+
     if( type == 0 )
     {   
       m_uiForm.oneDBtn->setText("Running ...");
@@ -788,6 +878,8 @@ void SANSRunWindow::setProcessingState(bool running, int type)
   }
   else
   {
+    enableOrDisableDefaultSave();
+
     m_uiForm.oneDBtn->setText("1D Reduce");
     m_uiForm.twoDBtn->setText("2D Reduce");
   }
@@ -1231,7 +1323,30 @@ void SANSRunWindow::selectCSVFile()
   m_last_dir = QFileInfo(m_uiForm.csv_filename->text()).path();
   if( m_cfg_loaded ) setProcessingState(false, -1);
 }
+/** Raises a browse dialog and inserts the selected file into the
+*  save text edit box, outfile_edit
+*/
+void SANSRunWindow::saveFileBrowse()
+{
+  QString title = "Save output workspace as";
+
+  QSettings prevValues;
+  prevValues.beginGroup("CustomInterfaces/SANSRunWindow/SaveOutput");
+  //use their previous directory first and go to their default if that fails
+  QString prevPath = prevValues.value("dir", QString::fromStdString(
+    ConfigService::Instance().getString("defaultsave.directory"))).toString();
 
+  QString filter = ";;AllFiles (*.*)";
+  QString oFile = QFileDialog::getSaveFileName(this, title, prevPath, filter);
+
+  if( ! oFile.isEmpty() )
+  {
+    m_uiForm.outfile_edit->setText(oFile);
+    
+    QString directory = QFileInfo(oFile).path();
+    prevValues.setValue("dir", directory);
+  }
+}
 /**
  * Mark that a run number has changed
 */
@@ -1240,7 +1355,6 @@ void SANSRunWindow::runChanged()
   m_warnings_issued = false;
   forceDataReload(true);
 }
-
 /**
  * Flip the flag to confirm whether data is reloaded
  * @param force If true, the data is reloaded when reduce is clicked
@@ -1299,6 +1413,7 @@ bool SANSRunWindow::handleLoadButtonClick()
   runReduceScriptFunction(pythonSetInst);
 
   setProcessingState(true, -1);
+  m_uiForm.load_dataBtn->setText("Loading ...");
 
   if( m_force_reload ) cleanup();
 
@@ -1307,6 +1422,7 @@ bool SANSRunWindow::handleLoadButtonClick()
   {
     showInformationBox("Error: No sample run given, cannot continue.");
     setProcessingState(false, -1);
+    m_uiForm.load_dataBtn->setText("Loading Data");
     return false;
   }
 
@@ -1314,6 +1430,7 @@ bool SANSRunWindow::handleLoadButtonClick()
   {
     showInformationBox("Error: Can run supplied without direct run, cannot continue.");
     setProcessingState(false, -1);
+      m_uiForm.load_dataBtn->setText("Load Data");
     return false;
   }
 
@@ -1382,6 +1499,7 @@ bool SANSRunWindow::handleLoadButtonClick()
   if (!is_loaded) 
   {
     setProcessingState(false, -1);
+    m_uiForm.load_dataBtn->setText("Load Data");
     return false;
   }
 
@@ -1403,6 +1521,7 @@ bool SANSRunWindow::handleLoadButtonClick()
     catch(std::exception &)
     {
       setProcessingState(false, -1);
+      m_uiForm.load_dataBtn->setText("Load Data");
       showInformationBox("Error: Could not retrieve sample workspace from Mantid");
       return false;
     }
@@ -1445,6 +1564,7 @@ bool SANSRunWindow::handleLoadButtonClick()
  
   m_sample_no = run_number;
   setProcessingState(false, -1);
+  m_uiForm.load_dataBtn->setText("Load Data");
   return true;
 }
 
@@ -1546,8 +1666,13 @@ QString SANSRunWindow::createAnalysisDetailsScript(const QString & type)
  */
 void SANSRunWindow::handleReduceButtonClick(const QString & type)
 {
-    //Need to check which mode we're in
-  if( m_uiForm.single_mode_btn->isChecked() )
+  //new reduction is going to take place, remove the results from the last reduction
+  resetDefaultOutput();
+
+  //The possiblities are batch mode or single run mode
+  const RunMode runMode =
+    m_uiForm.single_mode_btn->isChecked() ? SingleMode : BatchMode;
+  if ( runMode == SingleMode )
   {
     // Currently the components are moved with each reduce click. Check if a load is necessary
     // This must be done before the script is written as we need to get correct values from the
@@ -1571,13 +1696,16 @@ void SANSRunWindow::handleReduceButtonClick(const QString & type)
     trans_behav += "NewTrans";
   }
 
+  const static QString runPythonSep("C++ImplementationReservedC++");
   //Need to check which mode we're in
-  if( m_uiForm.single_mode_btn->isChecked() )
+  if ( runMode == SingleMode )
   {
     py_code += "\nreduced = WavRangeReduction(use_def_trans=" + trans_behav + ")\n";
+    //output the name of the output workspace, this is returned up by the runPythonCode() call below
+    py_code += "print '"+runPythonSep+"'+reduced";
     if( m_uiForm.plot_check->isChecked() )
     {
-      py_code += "PlotResult(reduced)\n";
+      py_code += "\nPlotResult(reduced)\n";
     }
   }
   else
@@ -1616,7 +1744,14 @@ void SANSRunWindow::handleReduceButtonClick(const QString & type)
   m_lastreducetype = idtype;
 
   //Execute the code
-  runPythonCode(py_code, false);
+  QString pythonStdOut = runPythonCode(py_code, false);
+  if ( runMode == SingleMode )
+  {
+    QString reducedWS = pythonStdOut.split(runPythonSep)[1];
+    reducedWS = reducedWS.split("\n")[0];
+    resetDefaultOutput(reducedWS);
+  }
+
   // Mark that a reload is necessary to rerun the same reduction
   forceDataReload();
   //Reenable stuff
@@ -1746,13 +1881,30 @@ void SANSRunWindow::handleRunFindCentre()
   //Reenable stuff
   setProcessingState(false, 0);
 }
-
-/**
- * Save a workspace
- */
-void SANSRunWindow::handleSaveButtonClick()
+/** Save the output workspace from a single run reduction (i.e. the
+*  workspace m_outputWS) in all the user selected formats
+*/
+void SANSRunWindow::handleDefSaveClick()
 {
-  runPythonCode("SaveRKHDialog()", false);
+  const QString fileBase = m_uiForm.outfile_edit->text();
+  if (fileBase.isEmpty())
+  {
+    QMessageBox::warning(this, "Filename required", "A filename must be entred into the text box above to save this file");
+  }
+
+  QString saveCommand;
+  for(SavFormatsConstIt i = m_savFormats.begin(); i != m_savFormats.end(); ++i)
+  {//the key is the check box
+    if (i.key()->isChecked())
+    {// and value() is the name of the algorithm associated with that chackbox
+      QString algName = i.value();
+      QString ext = SaveWorkspaces::getSaveAlgExt(algName);
+      QString fname = fileBase.endsWith(ext) ? fileBase : fileBase+ext;
+      saveCommand += algName+"('"+m_outputWS+"','"+fname+"')\n";
+    }
+  }
+
+  runPythonCode(saveCommand);
 }
 
 /**
@@ -1859,7 +2011,13 @@ void SANSRunWindow::handleInstrumentChange(int index)
 
   m_cfg_loaded = false;
 }
-
+/** Record if the user has changed the default filename, because then we don't
+*  change it
+*/
+void SANSRunWindow::setUserFname()
+{
+  m_userFname = true;
+}
 /**
  * Update the centre finding status label
  * @param msg The message string
@@ -1878,7 +2036,30 @@ void SANSRunWindow::updateCentreFindingStatus(const QString & msg)
     }
   }  
 }
-
+/**Enables  the default save button, saveDefault_Btn, if there is an output workspace
+* stored in m_outputWS and text in outfile_edit
+*/
+void SANSRunWindow::enableOrDisableDefaultSave()
+{
+  if ( m_outputWS.isEmpty() )
+  {//setEnabled(false) gets run below 
+  }
+  else if ( m_uiForm.outfile_edit->text().isEmpty() )
+  {//setEnabled(false) gets run below 
+  }
+  else
+  {//ensure that one format box is checked
+    for(SavFormatsConstIt i=m_savFormats.begin(); i != m_savFormats.end(); ++i)
+    {
+      if (i.key()->isChecked())
+      {
+        m_uiForm.saveDefault_btn->setEnabled(true);
+        return;
+      }
+    }
+  }
+  m_uiForm.saveDefault_btn->setEnabled(false);
+}
 /**
  * Update the logging window with status messages
  * @param msg The message received
@@ -1984,7 +2165,6 @@ void SANSRunWindow::clearLogger()
   m_uiForm.logging_field->clear();
   m_uiForm.tabWidget->setTabText(4, "Logging");
 }
-
 /**
  * Handle a verbose mode check box state change
  * state The new state
@@ -2013,6 +2193,20 @@ void SANSRunWindow::updateTransInfo(int state)
     m_uiForm.trans_max->setText(runReduceScriptFunction("printParameter('TRANS_WAV2_FULL'),"));
   }
 }
+/** Record the output workspace name, if there is no output
+*  workspace pass an empty string or an empty argument list
+*  @param wsName the name of the output workspace or empty for no output
+*/
+void SANSRunWindow::resetDefaultOutput(const QString & wsName)
+{
+  m_outputWS = wsName;
+  enableOrDisableDefaultSave();
+
+  if ( ! m_userFname )
+  {
+    m_uiForm.outfile_edit->setText(wsName);
+  }
+}
 /** 
  * Run a SANS assign command
  * @param key The key of the edit box to assign from
@@ -2265,7 +2459,6 @@ QString SANSRunWindow::getWorkspaceName(int key)
 void SANSRunWindow::handleMantidDeleteWorkspace(Mantid::API::WorkspaceDeleteNotification_ptr p_dnf)
 {
   QString wksp_name = QString::fromStdString(p_dnf->object_name());
-  QHashIterator<int, QString> itr(m_workspace_names);
   int names_count = m_workspace_names.count();
   for( int key = 0; key < names_count; ++key )
   {
diff --git a/Code/qtiplot/MantidQt/MantidWidgets/MantidWidgets.pro b/Code/qtiplot/MantidQt/MantidWidgets/MantidWidgets.pro
index 89daeb54e9e..8c81861b06a 100644
--- a/Code/qtiplot/MantidQt/MantidWidgets/MantidWidgets.pro
+++ b/Code/qtiplot/MantidQt/MantidWidgets/MantidWidgets.pro
@@ -29,7 +29,8 @@ SOURCES = \
   $$SRCDIR/pythonCalc.cpp \
   $$SRCDIR/DiagResults.cpp \
   $$SRCDIR/MWDiag.cpp \
-  $$SRCDIR/MWDiagCalcs.cpp
+  $$SRCDIR/MWDiagCalcs.cpp \
+  $$SRCDIR/SaveWorkspaces.cpp
 
 HEADERS = \
   $$HEADERDIR/MantidWidget.h \
@@ -38,7 +39,8 @@ HEADERS = \
   $$HEADERDIR/WidgetDllOption.h \
   $$HEADERDIR/DiagResults.h \
   $$HEADERDIR/MWDiag.h \
-  $$HEADERDIR/MWDiagCalcs.h
+  $$HEADERDIR/MWDiagCalcs.h \
+  $$HEADERDIR/SaveWorkspaces.h
   
 UI_DIR = $$HEADERDIR
 
diff --git a/Code/qtiplot/MantidQt/MantidWidgets/inc/SaveWorkspaces.h b/Code/qtiplot/MantidQt/MantidWidgets/inc/SaveWorkspaces.h
new file mode 100644
index 00000000000..6a4710e8138
--- /dev/null
+++ b/Code/qtiplot/MantidQt/MantidWidgets/inc/SaveWorkspaces.h
@@ -0,0 +1,87 @@
+#ifndef MANTIDQTMANTIDWIDGETS_SAVEWORKSPACES_H_
+#define MANTIDQTMANTIDWIDGETS_SAVEWORKSPACES_H_
+
+#include "MantidQtAPI/MantidQtDialog.h"
+#include "WidgetDllOption.h"
+#include <QHash>
+#include <QSignalMapper>
+#include <QGridLayout>
+#include <QListWidget>
+#include <QLineEdit>
+#include <QCheckBox>
+#include <QString>
+
+#include "MantidAPI/FrameworkManager.h"
+
+namespace MantidQt
+{
+  namespace MantidWidgets
+  {
+	/** 
+    Implements a dialog box that allows users to save multiple Mantid workspaces
+
+    @author Steve Williams
+
+    Copyright &copy; 2010 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
+
+    This file is part of Mantid.
+
+    Mantid is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    Mantid is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+    File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
+    Code Documentation is available at: <http://doxygen.mantidproject.org>    
+    */
+    class EXPORT_OPT_MANTIDQT_MANTIDWIDGETS SaveWorkspaces : public API::MantidQtDialog
+    {
+      Q_OBJECT
+
+    public: 
+      SaveWorkspaces(QWidget *parent, const QString & suggFname = "",
+        const QHash<const QCheckBox * const, QString> & defSavs =
+        QHash<const QCheckBox * const, QString>());
+      void initLayout();
+      ///Returns the save extension expected the name algorithm
+      static QString getSaveAlgExt(const QString & algName);
+
+    signals:
+      void closing();
+      void runAsPythonScript(const QString&);
+
+    private:
+      QLineEdit *m_fNameEdit;
+      QListWidget *m_workspaces;
+      QCheckBox *m_append;
+
+      QHash<QCheckBox * const, QString> m_savFormats;
+      typedef QHash<QCheckBox * const, QString>::const_iterator SavFormatsConstIt;
+      
+      void setupLine1(QHBoxLayout * const lineOne, const QString defName);
+      void setupLine2(QHBoxLayout * const lineTwo, const QHash<const QCheckBox * const, QString> & defSavs);
+      void setupFormatTicks(const QHash<const QCheckBox * const, QString> & defSavs);
+
+      void addButtonsDisab(int row);
+      void setupButtons(int row, QString test);
+      void updateRow(int row, QString firstColumn, int secondColumn);
+      void closeEvent(QCloseEvent *event);
+      QString saveList(const QList<QListWidgetItem*> & list, const QString & algorithm, QString fileBase, bool toAppend);
+
+    private slots:
+      void saveSel();
+      void saveFileBrowse();
+
+    };
+  }
+}
+
+#endif //MANTIDQTMANTIDWIDGETS_SAVEWORKSPACES_H_
diff --git a/Code/qtiplot/MantidQt/MantidWidgets/src/SaveWorkspaces.cpp b/Code/qtiplot/MantidQt/MantidWidgets/src/SaveWorkspaces.cpp
new file mode 100644
index 00000000000..1bce54643cc
--- /dev/null
+++ b/Code/qtiplot/MantidQt/MantidWidgets/src/SaveWorkspaces.cpp
@@ -0,0 +1,299 @@
+//----------------------
+// Includes
+//----------------------
+#include "MantidQtMantidWidgets/SaveWorkspaces.h"
+#include "MantidAPI/AnalysisDataService.h"
+#include "MantidAPI/FrameworkManager.h"
+#include "MantidAPI/AlgorithmManager.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidKernel/ConfigService.h"
+#include "MantidKernel/FileProperty.h"
+#include <QLabel>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QPushButton>
+#include <QDoubleValidator>
+#include <QCloseEvent>
+#include <QShowEvent>
+#include <QGroupBox>
+#include <QSettings>
+#include <QFileDialog>
+#include <QMessageBox>
+
+using namespace MantidQt::MantidWidgets;
+using namespace Mantid::Kernel;
+using namespace Mantid::API;
+
+//----------------------
+// Public member functions
+//----------------------
+/** 
+ *  @param parent used by QT
+ *  @param suggFname sets the initial entry in the filename box
+ *  @param defSavs sets which boxes are ticked
+ */
+SaveWorkspaces::SaveWorkspaces(QWidget *parent, const QString & suggFname, const QHash<const QCheckBox * const, QString> & defSavs) :
+  API::MantidQtDialog(parent)
+{
+  setWindowTitle("Save Workspaces");
+
+  //the form is split into 3 lines of controls in horizontal layouts
+  QHBoxLayout *lineOne = new QHBoxLayout;
+  setupLine1(lineOne, suggFname);
+  QHBoxLayout *lineTwo = new QHBoxLayout;
+  setupLine2(lineTwo, defSavs);
+
+  QVBoxLayout *dialogLayout = new QVBoxLayout;
+  dialogLayout->addLayout(lineOne);
+  dialogLayout->addLayout(lineTwo);
+   
+  setLayout(dialogLayout);
+  setAttribute(Qt::WA_DeleteOnClose);
+}
+/// Set up the dialog layout
+void SaveWorkspaces::initLayout()
+{
+}
+/** Puts the controls that go on the first line, the output
+*  filename commands, on to the layout that's passed to it
+*  @param lineOne the layout on to which the controls will be placed
+*/
+void SaveWorkspaces::setupLine1(QHBoxLayout * const lineOne, const QString defName)
+{
+  QLabel* fNameLabel = new QLabel("Filename:");
+  m_fNameEdit = new QLineEdit();
+  m_fNameEdit->setText(defName);
+  QPushButton *fNameButton = new QPushButton("Browse");
+  connect(fNameButton, SIGNAL(clicked()), this, SLOT(saveFileBrowse()));
+
+  lineOne->addWidget(fNameLabel);
+  lineOne->addWidget(m_fNameEdit);
+  lineOne->addWidget(fNameButton);
+
+  fNameLabel->setToolTip("Filename to save under");
+  m_fNameEdit->setToolTip("Filename to save under");
+  fNameButton->setToolTip("Filename to save under");
+}
+/** Puts the controls that go on the second line, the workspace
+*  list and save commands, on to the layout that's passed to it
+*  @param lineTwo the layout on to which the controls will be placed
+*/
+void SaveWorkspaces::setupLine2(QHBoxLayout * const lineTwo, const QHash<const QCheckBox * const, QString> & defSavs)
+{
+  m_workspaces = new QListWidget();
+  std::set<std::string> ws = AnalysisDataService::Instance().getObjectNames();
+  std::set<std::string>::const_iterator it = ws.begin(), wsEnd = ws.end();
+  for( ; it != wsEnd; ++it)
+  {
+    Workspace *wksp =  FrameworkManager::Instance().getWorkspace(*it);
+    if( dynamic_cast<MatrixWorkspace*>(wksp) )
+    {//only include matrix workspaces, not groups or tables
+      m_workspaces->addItem(QString::fromStdString(*it));
+    }
+  }
+  //allow users to select more than one workspace in the list
+  m_workspaces->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+  QPushButton *save = new QPushButton("Save");
+  connect(save, SIGNAL(clicked()), this, SLOT(saveSel()));
+  QPushButton *cancel = new QPushButton("Cancel");
+  connect(cancel, SIGNAL(clicked()), this, SLOT(close()));
+  
+  QCheckBox *saveNex = new QCheckBox("Nexus");
+  QCheckBox *saveCan = new QCheckBox("CanSAS");
+  QCheckBox *saveRKH = new QCheckBox("RKH");
+  QCheckBox *saveCSV = new QCheckBox("CSV");
+  //link the save option tick boxes to their save algorithm
+  m_savFormats.insert(saveNex, "SaveNexus");
+  m_savFormats.insert(saveCan, "SaveCanSAS1D");
+  m_savFormats.insert(saveRKH, "SaveRKH");
+  m_savFormats.insert(saveCSV, "SaveCSV");
+  setupFormatTicks(defSavs);
+
+  m_append = new QCheckBox("Append");
+  
+  // place controls into the layout, which places them on the form and takes care of deleting them
+  QVBoxLayout *ly_saveConts = new QVBoxLayout;
+  ly_saveConts->addWidget(save);
+  ly_saveConts->addWidget(cancel);
+  ly_saveConts->addWidget(m_append);
+  ly_saveConts->addStretch();
+
+  QVBoxLayout *ly_saveFormats = new QVBoxLayout;
+  ly_saveFormats->addWidget(saveNex);
+  ly_saveFormats->addWidget(saveCan);
+  ly_saveFormats->addWidget(saveRKH);
+  ly_saveFormats->addWidget(saveCSV);
+  QGroupBox *gb_saveForms = new QGroupBox(tr("Save Formats"));
+  gb_saveForms->setLayout(ly_saveFormats);
+  ly_saveConts->addWidget(gb_saveForms);
+
+  lineTwo->addWidget(m_workspaces);
+  lineTwo->addLayout(ly_saveConts);
+
+  m_workspaces->setToolTip("Select one or more workspaces");
+  const QString formatsTip = "Some formats support appending multiple workspaces in one file";
+  gb_saveForms->setToolTip(formatsTip);
+  save->setToolTip(formatsTip);
+  cancel->setToolTip(formatsTip);
+  saveNex->setToolTip(formatsTip);
+  saveCan->setToolTip(formatsTip);
+  saveRKH->setToolTip(formatsTip);
+  saveCSV->setToolTip(formatsTip);
+  m_append->setToolTip(formatsTip);
+}
+/** For each save format tick box take the user setting from the
+* main form
+*/
+void SaveWorkspaces::setupFormatTicks(const QHash<const QCheckBox * const, QString> & defSavs)
+{
+  for(SavFormatsConstIt i=m_savFormats.begin(); i != m_savFormats.end(); ++i)
+  {
+    //find the setting that has been passed for this save format
+    QHash<const QCheckBox * const, QString>::const_iterator j = defSavs.begin();
+    for ( ; j != defSavs.end(); ++j )
+    {
+      //the values are the algorithm names
+      if ( j.value() == i.value() )
+      {//copy over the checked status of the check box
+        i.key()->setChecked(j.key()->isChecked());
+      }
+    }
+  }
+}
+/**
+ * Called in response to a close event
+ * @parma event The event object
+ */
+void SaveWorkspaces::closeEvent(QCloseEvent* event)
+{
+  emit closing();
+  event->accept();
+}
+QString SaveWorkspaces::saveList(const QList<QListWidgetItem*> & wspaces, const QString & algorithm, QString fileBase, bool toAppend)
+{
+  if ( wspaces.count() < 1 )
+  {
+    throw std::logic_error("");
+  }
+
+  if (toAppend && fileBase.isEmpty())
+  {//no file name was given, use the name of the first workspace
+    fileBase = wspaces[0]->text();
+  }
+  QString exten = getSaveAlgExt(algorithm);
+  
+  QString saveCommands;
+  for (int j =0; j < wspaces.count(); ++j)
+  {
+    saveCommands += algorithm + "('"+wspaces[j]->text()+"','";
+
+    QString outFile = fileBase;
+    if (outFile.isEmpty())
+    {//if no filename was given use the workspace names
+      outFile = wspaces[j]->text();
+    }
+    else
+    {//we have a file name
+      if( ( wspaces.count() > 1 ) && ( ! toAppend ) )
+      {//but multiple output files, number the files
+        if (outFile.endsWith(exten))
+        {//put the number before the extension
+          outFile = outFile.split(exten)[0];
+        }
+        outFile += "-"+QString::number(j+1);
+      }
+    }
+    
+    if ( ! outFile.endsWith(exten) )
+    {//code above sometimes removes the extension and the possiblity that one just wasn't added needs dealing with too
+      outFile += exten;
+    }
+    saveCommands += outFile + "'";
+    if (toAppend)
+    {
+      saveCommands += ", Append=True";
+    }
+    //finally finish the algorithm call
+    saveCommands += ")\n";
+  }
+  return saveCommands;
+}
+/** Gets the first extension that the algorithm passed algorithm has in it's
+*  FileProperty (the FileProperty must have the name "Filename"
+*  @param algName name of the Mantid save algorithm
+*  @return the first extension, if the algorithm's Filename property has an extension list or ""
+*/
+QString SaveWorkspaces::getSaveAlgExt(const QString & algName)
+{
+  IAlgorithm_sptr alg = AlgorithmManager::Instance().create(
+    algName.toStdString());
+  Property *prop = alg->getProperty("Filename");
+  FileProperty *fProp = dynamic_cast<FileProperty*>(prop);
+  if (fProp)
+  {
+    return QString::fromStdString(fProp->getDefaultExt());
+  }
+  else
+  {// the algorithm doesn't have a "Filename" file property which may indicate an error later on or maybe OK
+    return "";
+  }
+}
+/** Excutes the selected save algorithms on the workspaces that
+*  have been selected to be saved
+*/
+void SaveWorkspaces::saveSel()
+{
+  QString saveCommands;
+  for(SavFormatsConstIt i = m_savFormats.begin(); i != m_savFormats.end(); ++i)
+  {//the key a pointer to the check box that the user may have clicked
+    if (i.key()->isChecked())
+    {//we need to save in this format
+      
+      bool toAppend = m_append->isChecked();
+      if (toAppend)
+      {//there are two algorithms that don't support appending, check for those
+        if ( i.value() == "SaveCanSAS1D" ||  i.value() == "SaveCSV" )
+        {
+          toAppend = false;
+        }
+      }
+
+      try
+      {
+        saveCommands += saveList(m_workspaces->selectedItems(), i.value(),
+          m_fNameEdit->text(), toAppend);
+      }
+      catch(std::logic_error)
+      {
+        QMessageBox::information(this, "No workspace to save", "You must select at least one workspace to save");
+        return;
+      }
+    }//end if save in this format
+  }//end loop over formats
+  emit runAsPythonScript(saveCommands);
+}
+/** Raises a browse dialog and inserts the selected file into the
+*  save text edit box, outfile_edit
+*/
+void SaveWorkspaces::saveFileBrowse()
+{
+  QString title = "Save output workspace as";
+
+  QSettings prevValues;
+  prevValues.beginGroup("CustomInterfaces/SANSRunWindow/SaveWorkspaces");
+  //use their previous directory first and go to their default if that fails
+  QString prevPath = prevValues.value("dir", QString::fromStdString(
+    ConfigService::Instance().getString("defaultsave.directory"))).toString();
+
+  QString filter = ";;AllFiles (*.*)";
+  QString oFile = QFileDialog::getSaveFileName(this, title, prevPath, filter);
+
+  if( ! oFile.isEmpty() )
+  {
+    m_fNameEdit->setText(oFile);
+    
+    QString directory = QFileInfo(oFile).path();
+    prevValues.setValue("dir", directory);
+  }
+}
\ No newline at end of file
-- 
GitLab