Index: guiframe/data_loader.py
===================================================================
--- guiframe/data_loader.py	(revision af47c8dae448fee26523f89845052b9a9b97987f)
+++ guiframe/data_loader.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -150,10 +150,10 @@
                                                 type="stop"))
     
-def plot_data(parent, path):
+def plot_data(parent, path, format=None):
     """
     Use the DataLoader loader to created data to plot.
     
     :param path: the path of the data to load
-    
+    : param format: file format (as file extension)
     """
     from sans.guicomm.events import NewPlotEvent, StatusEvent
@@ -165,5 +165,5 @@
     # Load data 
     try:
-        output = L.load(path)
+        output = L.load(path, format)
     except:
         load_error(sys.exc_value)
@@ -235,56 +235,58 @@
         i=1
         for item in output:
-            msg = "Loading 1D data: %s"%str(item.run[0])
-            wx.PostEvent(parent, StatusEvent(status=msg, info="info", type="stop"))
             try:
-                dx = item.dx
-                dxl = item.dxl
-                dxw = item.dxw
+                msg = "Loading 1D data: %s"%str(item.run[0])
+                wx.PostEvent(parent, StatusEvent(status=msg, info="info", type="stop"))
+                try:
+                    dx = item.dx
+                    dxl = item.dxl
+                    dxw = item.dxw
+                except:
+                    dx = None
+                    dxl = None
+                    dxw = None
+    
+                new_plot = Data1D(x=item.x,y=item.y,dx=dx,dy=item.dy)
+                new_plot.copy_from_datainfo(item)
+                item.clone_without_data(clone=new_plot)
+                new_plot.dxl = dxl
+                new_plot.dxw = dxw
+                
+                name = parse_name(name=str(item.run[0]), expression="_")
+                #if not name in parent.indice_load_data.keys():
+                #    parent.indice_load_data[name] = 0
+                #else:
+                    ## create a copy of the loaded data
+                    
+                    #TODO: this is a very annoying feature. We should make this
+                    # an option. Excel doesn't do this. Why should we?
+                    # What is the requirement for this feature, and are the
+                    # counter arguments stronger? Is this feature developed
+                    # to please at least 80% of the users or a special few?
+                    #parent.indice_load_data[name] += 1
+                    #name = name + "(copy %i)"%parent.indice_load_data[name]
+                    
+                new_plot.name = name
+                new_plot.interactive = True
+                new_plot.group_id = name
+                new_plot.id = name
+                new_plot.is_data = True
+            
+                if hasattr(item,"title"):
+                    title = item.title.lstrip().rstrip()
+                    if title == "":
+                        title = str(name)
+                else:
+                    title = name
+                if hasattr(parent, "panel_on_focus") and not(parent.panel_on_focus is None):
+                    existing_panel  = parent.panel_on_focus
+                    panel_name = existing_panel.window_caption
+                    data_name = new_plot.name
+                    if enable_add_data(existing_panel, new_plot):
+                        if open_dialog_append_data(panel_name, data_name):
+                            #add this plot the an existing panel
+                            new_plot.group_id = existing_panel.group_id
+                wx.PostEvent(parent, NewPlotEvent(plot=new_plot, title=str(title)))
+                i+=1
             except:
-                dx = None
-                dxl = None
-                dxw = None
-
-            new_plot = Data1D(x=item.x,y=item.y,dx=dx,dy=item.dy)
-            new_plot.copy_from_datainfo(item)
-            item.clone_without_data(clone=new_plot)
-            new_plot.dxl = dxl
-            new_plot.dxw = dxw
-           
-            name = parse_name(name=str(item.run[0]), expression="_")
-            #if not name in parent.indice_load_data.keys():
-            #    parent.indice_load_data[name] = 0
-            #else:
-                ## create a copy of the loaded data
-                
-                #TODO: this is a very annoying feature. We should make this
-                # an option. Excel doesn't do this. Why should we?
-                # What is the requirement for this feature, and are the
-                # counter arguments stronger? Is this feature developed
-                # to please at least 80% of the users or a special few?
-                #parent.indice_load_data[name] += 1
-                #name = name + "(copy %i)"%parent.indice_load_data[name]
-                
-            new_plot.name = name
-            new_plot.interactive = True
-            new_plot.group_id = name
-            new_plot.id = name
-            new_plot.is_data = True
-        
-            if hasattr(item,"title"):
-                title = item.title.lstrip().rstrip()
-                if title == "":
-                    title = str(name)
-            else:
-                title = name
-            if hasattr(parent, "panel_on_focus") and not(parent.panel_on_focus is None):
-                existing_panel  = parent.panel_on_focus
-                panel_name = existing_panel.window_caption
-                data_name = new_plot.name
-                if enable_add_data(existing_panel, new_plot):
-                    if open_dialog_append_data(panel_name, data_name):
-                        #add this plot the an existing panel
-                        new_plot.group_id = existing_panel.group_id
-            wx.PostEvent(parent, NewPlotEvent(plot=new_plot, title=str(title)))
-            i+=1
-         
+                raise
Index: guiframe/gui_manager.py
===================================================================
--- guiframe/gui_manager.py	(revision af47c8dae448fee26523f89845052b9a9b97987f)
+++ guiframe/gui_manager.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -537,6 +537,11 @@
         
         id = wx.NewId()
-        self.filemenu.Append(id, '&Load Data', 'Load data file into the application')
+        self.filemenu.Append(id, '&Open', 'Load data file into the application')
         wx.EVT_MENU(self, id, self._on_open)
+        #self.filemenu.AppendSeparator()
+        
+        id = wx.NewId()
+        self.filemenu.Append(id, '&Save', 'Save project as a SanaView (svs) file')
+        wx.EVT_MENU(self, id, self._on_save)
         #self.filemenu.AppendSeparator()
         
@@ -711,8 +716,77 @@
         from data_loader import plot_data
         if path and os.path.isfile(path):
-            plot_data(self, path)
+             basename  = os.path.basename(path)
+             if  basename.endswith('.svs'):
+                 plot_data(self, path,'.inv')
+                 plot_data(self, path,'.prv')
+                 plot_data(self, path,'.fitv')
+             else: plot_data(self,path)
         if self.defaultPanel is not None and \
             self._mgr.GetPane(self.panels["default"].window_name).IsShown():
             self.on_close_welcome_panel()
+            
+    def _on_save(self, event):
+        """
+        Save state into a file
+        """
+        # Ask the user the location of the file to write to.
+        
+        ## Default file location for save
+        self._default_save_location = os.getcwd()
+        path = None
+        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location, "", "*.svs", wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self._default_save_location = os.path.dirname(path)
+        else:
+            return None
+        
+        dlg.Destroy()
+        if path is None:
+            return
+        # default cansas xml doc
+        doc = None
+        for item in self.panels:
+            try:
+                if self.panels[item].window_name == 'Invariant' or self.panels[item].window_name == 'pr_control' or self.panels[item].window_name == 'Fit panel':
+                    if self.panels[item].window_name == 'Invariant':
+                        data = self.panels[item]._data
+                        state = self.panels[item].state
+                        new_doc =self.panels[item]._manager.state_reader.write_toXML(data,state)
+                        if hasattr(doc, "firstChild"):
+                            doc.firstChild.appendChild (new_doc.firstChild.firstChild)  
+                        else:
+                            doc = new_doc 
+                    elif self.panels[item].window_name == 'pr_control':
+                        data = self.panels[item].manager.current_plottable
+                        state = self.panels[item].get_state()
+                        new_doc =self.panels[item].manager.state_reader.write_toXML(data,state)
+                        if hasattr(doc, "firstChild"):
+                            doc.firstChild.appendChild (new_doc.firstChild.firstChild)  
+                        else:
+                            doc = new_doc 
+                    elif self.panels[item].window_name == 'Fit panel':
+                        for index in range(self.panels[item].GetPageCount()):
+                            selected_page = self.panels[item].GetPage(index) 
+                            if hasattr(selected_page,"get_data"):
+                                data = selected_page.get_data()
+                                state = selected_page.state
+                                new_doc =selected_page.manager.state_reader.write_toXML(data,state)
+                                if doc != None and hasattr(doc, "firstChild"):
+                                    doc.firstChild.appendChild (new_doc.firstChild.firstChild)
+                                else:
+                                    doc = new_doc
+
+            except: 
+                pass
+
+        # Write the XML document
+        if doc != None:
+            fd = open(path, 'w')
+            fd.write(doc.toprettyxml())
+            fd.close()
+        else:
+            raise RuntimeError, "%s is not a SansView (.svs) file..." % path
+
        
     def _onClose(self, event):
Index: invariantview/perspectives/invariant/invariant.py
===================================================================
--- invariantview/perspectives/invariant/invariant.py	(revision 210ff4f98308a7b7336ce09f8a796d760a751e84)
+++ invariantview/perspectives/invariant/invariant.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -105,4 +105,5 @@
         loader = Loader()
         loader.associate_file_reader(".inv", self.state_reader)
+        loader.associate_file_reader(".svs", self.state_reader)
         # Return the list of panels
         return [self.invariant_panel]
Index: invariantview/perspectives/invariant/invariant_panel.py
===================================================================
--- invariantview/perspectives/invariant/invariant_panel.py	(revision 210ff4f98308a7b7336ce09f8a796d760a751e84)
+++ invariantview/perspectives/invariant/invariant_panel.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -131,4 +131,5 @@
             self.reset_panel()
             self.compute_invariant(event=None)
+            self.state.file = self._data.name
         return True  
     def set_message(self):
@@ -166,6 +167,6 @@
             if not self.set_data(data):
                 return
-            self.state = state   
-            
+            self.state = state 
+            self.state.file = data.name   
             num = self.state.saved_state['state_num']
             
Index: invariantview/perspectives/invariant/invariant_state.py
===================================================================
--- invariantview/perspectives/invariant/invariant_state.py	(revision 5b031223a262b0709ad65c0585c105472958a8de)
+++ invariantview/perspectives/invariant/invariant_state.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -266,5 +266,5 @@
         # File name
         element = newdoc.createElement("filename")
-        if self.file is not None:
+        if self.file != None and self.file !='':
             element.appendChild(newdoc.createTextNode(str(self.file)))
         else:
@@ -353,6 +353,6 @@
             entry = get_content('ns:filename', node)
             if entry is not None:
-                self.file = entry.text.strip()
-            
+                file_name = entry.text.strip()
+
             # Get time stamp
             entry = get_content('ns:timestamp', node)
@@ -432,5 +432,7 @@
                     if input_field is not None:
                         self.set_saved_state(name=item, value=val)
-
+                        
+            self.file = file_name
+            
     def set_report_string(self):
         """
@@ -606,7 +608,8 @@
     
     ## Wildcards
-    type = ["Invariant file (*.inv)|*.inv"]
+    type = ["Invariant file (*.inv)|*.inv",
+            "SANSView file (*.svs)|*.svs"]
     ## List of allowed extensions
-    ext=['.inv', '.INV']  
+    ext=['.inv', '.INV', '.svs', 'SVS']  
     
     def __init__(self, call_back, cansas=True):
@@ -660,11 +663,12 @@
         : return: InvariantState object
         """
-        # Create an empty state
-        state = InvariantState()
-
+        state = None
         # Locate the invariant node
         try:
             nodes = entry.xpath('ns:%s' % INVNODE_NAME, namespaces={'ns': CANSAS_NS})
-            state.fromXML(node=nodes[0])
+            # Create an empty state
+            if nodes !=[]:
+                state = InvariantState()
+                state.fromXML(node=nodes[0])
         except:
             logging.info("XML document does not contain invariant information.\n %s" % sys.exc_value)  
@@ -683,5 +687,4 @@
         """
         output = []
-        
         if os.path.isfile(path):
             basename  = os.path.basename(path)
@@ -702,9 +705,11 @@
                     sas_entry = self._parse_entry(entry)
                     invstate = self._parse_state(entry)
-                    sas_entry.meta_data['invstate'] = invstate
-
-                    sas_entry.filename = invstate.file
-                    output.append(sas_entry)
-                
+                    
+                    #invstate could be None when .svs file is loaded
+                    #in this case, skip appending to output
+                    if invstate != None:
+                        sas_entry.meta_data['invstate'] = invstate
+                        sas_entry.filename = invstate.file
+                        output.append(sas_entry)
         else:
             raise RuntimeError, "%s is not a file" % path
@@ -730,19 +735,7 @@
         : param invstate: InvariantState object
         """
-
         # Sanity check
         if self.cansas == True:
-            if datainfo is None:
-                datainfo = DataLoader.data_info.Data1D(x=[], y=[])    
-            elif not issubclass(datainfo.__class__, DataLoader.data_info.Data1D):
-                raise RuntimeError, "The cansas writer expects a Data1D instance: %s" % str(datainfo.__class__.__name__)
-        
-            # Create basic XML document
-            doc, sasentry = self._to_xml_doc(datainfo)
-        
-            # Add the invariant information to the XML document
-            if invstate is not None:
-                invstate.toXML(doc=doc, entry_node=sasentry)
-        
+            doc = self.write_toXML(datainfo,invstate)
             # Write the XML document
             fd = open(filename, 'w')
@@ -752,4 +745,26 @@
             invstate.toXML(file=filename)
         
-    
-    
+    def write_toXML(self, datainfo=None, state=None):
+        """
+        Write toXML, a helper for write()
+        
+        : return: xml doc
+        """
+        if datainfo is None:
+            datainfo = DataLoader.data_info.Data1D(x=[], y=[])    
+        elif not issubclass(datainfo.__class__, DataLoader.data_info.Data1D):
+            raise RuntimeError, "The cansas writer expects a Data1D instance: %s" % str(datainfo.__class__.__name__)
+        #make sure title and data run is filled up.
+        if datainfo.title == None or datainfo.title=='': datainfo.title = datainfo.name
+        if datainfo.run_name == None or datainfo.run_name=={}: 
+            datainfo.run = [str(datainfo.name)]
+            datainfo.run_name[0] = datainfo.name
+
+        # Create basic XML document
+        doc, sasentry = self._to_xml_doc(datainfo)
+    
+        # Add the invariant information to the XML document
+        if state is not None:
+            state.toXML(doc=doc, entry_node=sasentry)
+            
+        return doc
Index: prview/perspectives/pr/inversion_panel.py
===================================================================
--- prview/perspectives/pr/inversion_panel.py	(revision 0d21ac1cb375f21c85e0a82e2f136c2dafeac8ba)
+++ prview/perspectives/pr/inversion_panel.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -252,5 +252,17 @@
         
         dlg.Destroy()
-                
+        
+        state = self.get_state()
+            
+        self.manager.save_data(filepath=path, prstate=state)
+        
+        return state
+    
+    def get_state(self):
+        """
+        Get the current state
+        
+        : return: state object
+        """
         # Construct the state object    
         state = InversionState()
@@ -287,6 +299,4 @@
         state.iq0     = self.iq0
         state.bck     = self.bck
-            
-        self.manager.save_data(filepath=path, prstate=state)
         
         return state
Index: prview/perspectives/pr/inversion_state.py
===================================================================
--- prview/perspectives/pr/inversion_state.py	(revision 7116b6e08b4906240b8a779bf2f7c0f6e2623f6f)
+++ prview/perspectives/pr/inversion_state.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -341,7 +341,8 @@
     
     ## Wildcards
-    type = ["P(r) files (*.prv)|*.prv"]
+    type = ["P(r) files (*.prv)|*.prv",
+            "SANSView files (*.svs)|*.svs"]
     ## List of allowed extensions
-    ext=['.prv', '.PRV']  
+    ext=['.prv', '.PRV', '.svs', '.SVS']  
     
     def __init__(self, call_back, cansas=True):
@@ -401,11 +402,13 @@
         
         """
-        # Create an empty state
-        state = InversionState()
+        state = None
         
         # Locate the P(r) node
         try:
             nodes = entry.xpath('ns:%s' % PRNODE_NAME, namespaces={'ns': CANSAS_NS})
-            state.fromXML(node=nodes[0])
+            if nodes !=[]:
+                # Create an empty state
+                state =  InversionState()
+                state.fromXML(node=nodes[0])
         except:
             logging.info("XML document does not contain P(r) information.\n %s" % sys.exc_value)
@@ -447,7 +450,10 @@
                     sas_entry = self._parse_entry(entry)
                     prstate = self._parse_prstate(entry)
-                    sas_entry.meta_data['prstate'] = prstate
-                    sas_entry.filename = prstate.file
-                    output.append(sas_entry)
+                    #prstate could be None when .svs file is loaded
+                    #in this case, skip appending to output
+                    if prstate != None:
+                        sas_entry.meta_data['prstate'] = prstate
+                        sas_entry.filename = prstate.file
+                        output.append(sas_entry)
         else:
             raise RuntimeError, "%s is not a file" % path
@@ -475,16 +481,5 @@
         # Sanity check
         if self.cansas == True:
-            if datainfo is None:
-                datainfo = DataLoader.data_info.Data1D(x=[], y=[])    
-            elif not issubclass(datainfo.__class__, DataLoader.data_info.Data1D):
-                raise RuntimeError, "The cansas writer expects a Data1D instance: %s" % str(datainfo.__class__.__name__)
-        
-            # Create basic XML document
-            doc, sasentry = self._to_xml_doc(datainfo)
-        
-            # Add the P(r) information to the XML document
-            if prstate is not None:
-                prstate.toXML(doc=doc, entry_node=sasentry)
-        
+            doc =self.write_toXML(datainfo, prstate)        
             # Write the XML document
             fd = open(filename, 'w')
@@ -494,4 +489,22 @@
             prstate.toXML(file=filename)
         
-    
-    
+    def write_toXML(self, datainfo=None, state=None):
+        """
+        Write toXML, a helper for write()
+        
+        : return: xml doc
+        """
+        if datainfo is None:
+            datainfo = DataLoader.data_info.Data1D(x=[], y=[])    
+        elif not issubclass(datainfo.__class__, DataLoader.data_info.Data1D):
+            raise RuntimeError, "The cansas writer expects a Data1D instance: %s" % str(datainfo.__class__.__name__)
+    
+        # Create basic XML document
+        doc, sasentry = self._to_xml_doc(datainfo)
+    
+        # Add the invariant information to the XML document
+        if state is not None:
+            state.toXML(doc=doc, entry_node=sasentry)
+            
+        return doc 
+    
Index: prview/perspectives/pr/pr.py
===================================================================
--- prview/perspectives/pr/pr.py	(revision ed90edb7b130162c4a7dbdfc0a94c0df30d346ad)
+++ prview/perspectives/pr/pr.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -112,4 +112,5 @@
         l = Loader()
         l.associate_file_reader('.prv', self.state_reader)
+        l.associate_file_reader(".sav", self.state_reader)
                 
         # Log startup
Index: sansview/perspectives/fitting/fitting.py
===================================================================
--- sansview/perspectives/fitting/fitting.py	(revision 4f813427ffc479ac0a4cba49e2aa7b75fd410820)
+++ sansview/perspectives/fitting/fitting.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -257,4 +257,5 @@
         loader = Loader()
         loader.associate_file_reader(".fitv", self.state_reader)
+        loader.associate_file_reader(".svs", self.state_reader)
         #Send the fitting panel to guiframe
         self.mypanels.append(self.fit_panel)
Index: sansview/perspectives/fitting/pagestate.py
===================================================================
--- sansview/perspectives/fitting/pagestate.py	(revision 3c44c66f5a43cf590e90b9a7ba318e6e6f869f58)
+++ sansview/perspectives/fitting/pagestate.py	(revision b35d3d1fd21996b7e3eed67f36661a33aeccb11d)
@@ -603,7 +603,8 @@
     
     ## Wildcards
-    type = ["Fitting files (*.fitv)|*.fitv"]
+    type = ["Fitting files (*.fitv)|*.fitv"
+            "SANSView file (*.svs)|*.svs"]
     ## List of allowed extensions
-    ext=['.fitv', '.FITV']  
+    ext=['.fitv', '.FITV', '.svs', 'SVS']   
     
     def __init__(self, call_back=None, cansas=True):
@@ -834,9 +835,12 @@
         """
         # Create an empty state
-        state = PageState()
+        state = None   
         # Locate the P(r) node
         try:
             nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME, namespaces={'ns': CANSAS_NS})
-            state.fromXML(node=nodes[0])
+            if nodes !=[]:
+                # Create an empty state
+                state =  PageState()
+                state.fromXML(node=nodes[0])
         except:
             logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
@@ -1106,5 +1110,5 @@
                     root = tree.getroot()
                     entry_list = root.xpath('ns:SASentry', namespaces={'ns': CANSAS_NS})
-                    for entry in entry_list:
+                    for entry in entry_list:   
                         try:
                             sas_entry = self._parse_entry(entry)
@@ -1112,41 +1116,48 @@
                             raise
                         fitstate = self._parse_state(entry)
-                        sas_entry.meta_data['fitstate'] = fitstate
-                        sas_entry.filename = fitstate.file
-                        output.append(sas_entry)
+                        
+                        #state could be None when .svs file is loaded
+                        #in this case, skip appending to output
+                        if fitstate != None:
+                            sas_entry.meta_data['fitstate'] = fitstate
+                            sas_entry.filename = fitstate.file
+                            output.append(sas_entry)
             else:
                 raise RuntimeError, "%s is not a file" % path
-            
+
             # Return output consistent with the loader's api
             if len(output)==0:
                 return None
-            elif len(output)==1:
-                # Call back to post the new state
-                state = output[0].meta_data['fitstate']
-                t = time.localtime(state.timestamp)
-                time_str = time.strftime("%b %d %H:%M", t)
-                # Check that no time stamp is already appended
-                max_char = state.file.find("[")
-                if max_char < 0:
-                    max_char = len(state.file)
-                state.file = state.file[0:max_char] +' [' + time_str + ']'
-               
-                    
-                if state is not None and state.is_data is not None:
-                    exec 'output[0].is_data = state.is_data' 
-                 
-                output[0].filename = state.file
-                state.data = output[0]
-                state.data.name = output[0].filename #state.data_name
-                state.data.id = state.data_id
-                state.data.id = state.data_id
-                if state.is_data is not None:
-                    state.data.is_data = state.is_data
-                state.data.group_id = output[0].filename
+            else:
+                for ind in range(len(output)):
+                    # Call back to post the new state
+                    state = output[ind].meta_data['fitstate']
+                    t = time.localtime(state.timestamp)
+                    time_str = time.strftime("%b %d %H:%M", t)
+                    # Check that no time stamp is already appended
+                    max_char = state.file.find("[")
+                    if max_char < 0:
+                        max_char = len(state.file)
+                    state.file = state.file[0:max_char] +' [' + time_str + ']'
+                   
+                        
+                    if state is not None and state.is_data is not None:
+                        exec 'output[%d].is_data = state.is_data'% ind 
+                     
+                    output[ind].filename = state.file
+                    state.data = output[ind]
+                    state.data.name = output[ind].filename #state.data_name
+                    state.data.id = state.data_id
+                    state.data.id = state.data_id
+                    if state.is_data is not None:
+                        state.data.is_data = state.is_data
+                    state.data.group_id = output[ind].filename
+                  
+                    # make sure to put run name if none
+                    #if output[ind].run == None or output[ind].run ==[]:
+                    #    exec 'output[%d].run = [output[%d].filename]'% (ind,ind)
+                    self.call_back(state=state, datainfo=output[ind])
+                    return output[ind]
               
-                self.call_back(state=state, datainfo=output[0])
-                return output[0]
-            else:
-                return output                
         except:
             raise
@@ -1154,5 +1165,5 @@
     def write(self, filename, datainfo=None, fitstate=None):
         """
-        Write the content of a Data1D as a CanSAS XML file
+        Write the content of a Data1D as a CanSAS XML file only for standalone
         
         :param filename: name of the file to write
@@ -1165,14 +1176,5 @@
             
             # Add fitting information to the XML document
-            if fitstate is not None:
-                if fitstate.data is None:
-                    data = DataLoader.data_info.Data1D(x=[], y=[])   
-                elif issubclass(fitstate.data.__class__, DataLoader.data_info.Data1D):
-                    data = fitstate.data
-                    doc, sasentry = self._to_xml_doc(data)
-                else:
-                    data = fitstate.data
-                    doc, sasentry = self._data2d_to_xml_doc(data)
-                fitstate.toXML(doc=doc, file=data.name, entry_node=sasentry)
+            self.write_toXML(datainfo, fitstate)
             # Write the XML document
             fd = open(filename, 'w')
@@ -1182,5 +1184,32 @@
             fitstate.toXML(file=filename)
         
-
+    def write_toXML(self, datainfo=None, state=None):
+        """
+        Write toXML, a helper for write() , could be used by guimanager._on_save()
+        
+        : return: xml doc
+        """
+
+        if state.data is None:
+            data = DataLoader.data_info.Data1D(x=[], y=[])  
+        else:  
+            #make sure title and data run is filled up.
+            #if state.data.title == None or state.data.title=='': state.data.title = state.data.name
+            #if state.data.run_name == None or state.data.run_name=={}: 
+            #    state.data.run = [str(state.data.name)]
+            #    state.data.run_name[0] = state.data.name
+   
+            if issubclass(state.data.__class__, DataLoader.data_info.Data1D):
+                data = state.data
+                doc, sasentry = self._to_xml_doc(data)
+            else:
+                data = state.data
+                doc, sasentry = self._data2d_to_xml_doc(data)
+            
+
+        if state is not None:
+            state.toXML(doc=doc, file=data.name, entry_node=sasentry)
+            
+        return doc 
   
 if __name__ == "__main__":
