Index: sansmodels/src/sans/models/CoreMultiShellModel.py
===================================================================
--- sansmodels/src/sans/models/CoreMultiShellModel.py	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
+++ sansmodels/src/sans/models/CoreMultiShellModel.py	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
@@ -0,0 +1,230 @@
+   
+from sans.models.BaseComponent import BaseComponent
+from sans.models.CoreFourShellModel import CoreFourShellModel
+import copy
+max_nshells = 4
+class CoreMultiShellModel(BaseComponent):
+    """
+    This multi-model is based on CoreFourShellModel and provides the capability
+    of changing the number of shells between 1 and 4.
+    """
+    def __init__(self, n_shells=4):
+        BaseComponent.__init__(self)
+        """
+        :param n_shells: number of shells in the model, assumes 1<= n_shells <=4.
+        """
+
+        ## Setting  model name model description
+        self.description=""
+        model = CoreFourShellModel()
+        self.model = model
+        self.name = "CoreMultiShellModel"
+        self.description=""
+        self.n_shells = n_shells
+        ## Define parameters
+        self.params = {}
+
+        ## Parameter details [units, min, max]
+        self.details = {}
+        
+        ## dispersion
+        self._set_dispersion()
+        ## Define parameters
+        self._set_params()
+        
+        ## Parameter details [units, min, max]
+        self._set_details()
+        
+        #list of parameter that can be fitted
+        self._set_fixed_params()  
+        
+        ## parameters with orientation: can be removed since there is no orientational params
+        for item in self.model.orientation_params:
+            self.orientation_params.append(item)
+                
+        
+    def _clone(self, obj):
+        """
+        Internal utility function to copy the internal
+        data members to a fresh copy.
+        """
+        obj.params     = copy.deepcopy(self.params)
+        obj.description     = copy.deepcopy(self.description)
+        obj.details    = copy.deepcopy(self.details)
+        obj.dispersion = copy.deepcopy(self.dispersion)
+        obj.model  = self.model.clone()
+
+        return obj
+    
+    
+    def _set_dispersion(self):
+        """
+        model dispersions
+        Polydispersion should not be applied to s_model
+        """ 
+        ##set dispersion from model 
+        for name , value in self.model.dispersion.iteritems():      
+            nshell = 0
+            if name.split('_')[0] == 'thick':
+                while nshell<self.n_shells:
+                    nshell += 1
+                    if name.split('_')[1] == 'shell%s' % str(nshell):
+                        self.dispersion[name]= value
+                    else: 
+                        continue
+            else:
+                self.dispersion[name]= value
+                              
+
+    def _set_params(self):
+        """
+        Concatenate the parameters of the model to create
+        this model parameters 
+        """
+        # rearrange the parameters for the given # of shells
+        for name , value in self.model.params.iteritems():
+            nshell = 0
+            if name.split('_')[0] == 'thick' or name.split('_')[0] == 'sld':
+                if name.split('_')[1] == 'solv' or name.split('_')[1] == 'core':
+                    self.params[name]= value
+                    continue
+                while nshell<self.n_shells:
+                    nshell += 1
+                    if name.split('_')[1] == 'shell%s' % str(nshell):
+                        self.params[name]= value
+                        continue
+            else:
+                self.params[name]= value
+            
+            
+        # set constrained values for the original model params
+        self._set_xtra_model_param()       
+  
+    def _set_details(self):
+        """
+        Concatenate details of the original model to create
+        this model details 
+        """
+        for name ,detail in self.model.details.iteritems():
+            if name in self.params.iterkeys():
+                self.details[name]= detail
+            
+    
+    def _set_xtra_model_param(self):
+        """
+        Set params of original model that are hidden from this model
+        """
+        # look for the model parameters that are not in param list
+        for key in self.model.params.iterkeys():
+            if key not in self.params.keys():
+                if  key.split('_')[0] == 'thick':
+                    self.model.setParam(key, 0)
+                    continue
+
+                for nshell in range(self.n_shells,max_nshells):
+                    if key.split('_')[0] == 'sld' and key.split('_')[1] == 'shell%s' % str(nshell+1):
+                        try:
+                            value = self.model.params['sld_solv']
+                            self.model.setParam(key, value)
+                        except: pass
+
+   
+    def setParam(self, name, value):
+        """ 
+        Set the value of a model parameter
+    
+        : param name: name of the parameter
+        : param value: value of the parameter
+        """
+        # set param to new model
+        self._setParamHelper( name, value)
+        ## setParam to model 
+        if name=='sld_solv':
+            # the sld_*** model.params not in params must set to value of sld_solv
+            for key in self.model.params.iterkeys():
+                if key not in self.params.keys()and key.split('_')[0] == 'sld':
+                        self.model.setParam(key, value)
+        self.model.setParam( name, value)
+
+    def _setParamHelper(self, name, value):
+        """
+        Helper function to setParam
+        """
+        #look for dispersion parameters
+        toks = name.split('.')
+        if len(toks)==2:
+            for item in self.dispersion.keys():
+                if item.lower()==toks[0].lower():
+                    for par in self.dispersion[item]:
+                        if par.lower() == toks[1].lower():
+                            self.dispersion[item][par] = value
+                            return
+        # Look for standard parameter
+        for item in self.params.keys():
+            if item.lower()==name.lower():
+                self.params[item] = value
+                return
+        
+        raise ValueError, "Model does not contain parameter %s" % name
+             
+   
+    def _set_fixed_params(self):
+        """
+        Fill the self.fixed list with the model fixed list
+        """
+        for item in self.model.fixed:
+            if item.split('.')[0] in self.params.keys(): 
+                self.fixed.append(item)
+
+        self.fixed.sort()
+        pass         
+                
+    def run(self, x = 0.0):
+        """ 
+        Evaluate the model
+        
+        : param x: input q-value (float or [float, float] as [r, theta])
+        : return: (DAB value)
+        """
+        # set effective radius and scaling factor before run
+
+        return self.model.run(x)
+
+    def runXY(self, x = 0.0):
+        """ 
+        Evaluate the model
+        
+        : param x: input q-value (float or [float, float] as [qx, qy])
+        : return: DAB value
+        """  
+        # set effective radius and scaling factor before run
+
+        return self.model.runXY(x)
+    
+    ## Now (May27,10) directly uses the model eval function 
+    ## instead of the for-loop in Base Component.
+    def evalDistribution(self, x = []):
+        """ 
+        Evaluate the model in cartesian coordinates
+        
+        : param x: input q[], or [qx[], qy[]]
+        : return: scattering function P(q[])
+        """
+        # set effective radius and scaling factor before run
+        return self.model.evalDistribution(x)
+
+    def set_dispersion(self, parameter, dispersion):
+        """
+        Set the dispersion object for a model parameter
+        
+        : param parameter: name of the parameter [string]
+        :dispersion: dispersion object of type DispersionModel
+        """
+        value= None
+        try:
+            if parameter in self.model.dispersion.keys():
+                value= self.model.set_dispersion(parameter, dispersion)
+            self._set_dispersion()
+            return value
+        except:
+            raise 
Index: sansview/perspectives/fitting/basepage.py
===================================================================
--- sansview/perspectives/fitting/basepage.py	(revision 3c44c66f5a43cf590e90b9a7ba318e6e6f869f58)
+++ sansview/perspectives/fitting/basepage.py	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
@@ -929,5 +929,7 @@
         self.formfactorbox.Select(int(state.formfactorcombobox))
         self.structurebox.SetSelection(state.structurecombobox )
-       
+        if state.multi_factor != None:
+            self.multifactorbox.SetSelection(state.multi_factor-1)
+
         #reset the fitting engine type
         self.engine_type = state.engine_type
@@ -1584,5 +1586,5 @@
         else:
             boxsizer1 = box_sizer
-            
+        sizer_buttons = wx.BoxSizer(wx.HORIZONTAL)    
         #--------------------------------------------------------
         self.shape_rbutton = wx.RadioButton(self, -1, 'Shapes', style=wx.RB_GROUP)
@@ -1605,12 +1607,19 @@
         sizer_radiobutton.Add(self.shape_rbutton)
         sizer_radiobutton.Add(self.shape_indep_rbutton)
+
         sizer_radiobutton.Add(self.plugin_rbutton)
         sizer_radiobutton.Add(self.struct_rbutton)
+        sizer_buttons.Add(sizer_radiobutton)
+        # detail button
+        if object !=None:
+            sizer_buttons.Add((50,0))
+            sizer_buttons.Add(object)
         
         sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
+        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
         
         self.text1 = wx.StaticText( self,-1,"" )
         self.text2 = wx.StaticText( self,-1,"P(Q)*S(Q)" )
-        
+        self.mutifactor_text = wx.StaticText( self,-1,"No. of Shells: " )
         
         self.formfactorbox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
@@ -1619,11 +1628,11 @@
            
         self.structurebox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
-        
+        self.multifactorbox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
         self.initialize_combox()
              
         wx.EVT_COMBOBOX(self.formfactorbox,-1, self._on_select_model)
         wx.EVT_COMBOBOX(self.structurebox,-1, self._on_select_model)
-        
-       
+        wx.EVT_COMBOBOX(self.multifactorbox,-1, self._on_select_model)
+        
         ## check model type to show sizer
         if self.model !=None:
@@ -1637,17 +1646,49 @@
         sizer_selection.Add((5,5))
         sizer_selection.Add(self.structurebox)
-        sizer_selection.Add((5,5))
-        
-        boxsizer1.Add( sizer_radiobutton )
-        boxsizer1.Add( (20,20))
+        #sizer_selection.Add((5,5))
+        mutifactor_selection.Add((10,5))
+        mutifactor_selection.Add(self.mutifactor_text)
+        mutifactor_selection.Add(self.multifactorbox)
+        
+        boxsizer1.Add( sizer_buttons )
+        boxsizer1.Add( (15,15))
         boxsizer1.Add( sizer_selection )
-        if object !=None:
-            boxsizer1.Add( (-72,-72))
-            boxsizer1.Add( object,  0, wx.ALIGN_RIGHT| wx.RIGHT, 35)
-            boxsizer1.Add( (60,60))
+        boxsizer1.Add( (10,10))
+        boxsizer1.Add(mutifactor_selection)
+        
+        self._set_multfactor_combobox()
+
         #--------------------------------------------------------
         sizer.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
         sizer.Layout()
         self.SetScrollbars(20,20,25,65)
+        
+    def _set_multfactor_combobox(self):   
+        """
+        Set comboBox for muitfactor of CoreMultiShellModel
+        """
+        for idx in range(1,5):
+            self.multifactorbox.Append(str(idx),int(idx))
+            self.multifactorbox.SetSelection(0) 
+        self._hide_multfactor_combobox()
+        
+    def _show_multfactor_combobox(self):   
+        """
+        Show the comboBox of muitfactor of CoreMultiShellModel
+        """ 
+        if not self.mutifactor_text.IsShown():
+            self.mutifactor_text.Show(True)
+        if not self.multifactorbox.IsShown():
+            self.multifactorbox.Show(True)  
+             
+    def _hide_multfactor_combobox(self):   
+        """
+        Hide the comboBox of muitfactor of CoreMultiShellModel
+        """ 
+        if self.mutifactor_text.IsShown():
+            self.mutifactor_text.Hide()
+        if self.multifactorbox.IsShown():
+            self.multifactorbox.Hide()   
+
         
     def _show_combox_helper(self):
@@ -1797,20 +1838,41 @@
             self.structurebox.Enable()
             self.text2.Enable()
+            
+        if form_factor != None:    
+            # set multifactor for Mutifunctional models    
+            if form_factor().name == 'CoreMultiShellModel':
+                self._show_multfactor_combobox()
+                m_id = self.multifactorbox.GetCurrentSelection()
+                self.multi_factor = self.multifactorbox.GetClientData(m_id)
+                if self.multi_factor == None: self.multi_factor =0
+                form_factor = form_factor(int(self.multi_factor))
+            else:
+                self._hide_multfactor_combobox()
+                form_factor = form_factor()
+                self.multi_factor = None
+        else:
+            self._hide_multfactor_combobox()
+            self.multi_factor = None  
+              
         s_id = self.structurebox.GetCurrentSelection()
         struct_factor = self.structurebox.GetClientData( s_id )
-       
+        
         if  struct_factor !=None:
             from sans.models.MultiplicationModel import MultiplicationModel
-            self.model= MultiplicationModel(form_factor(),struct_factor())
+            self.model= MultiplicationModel(form_factor,struct_factor())
             
         else:
             if form_factor != None:
-                self.model= form_factor()
+                self.model= form_factor
             else:
                 self.model = None
                 return self.model
+            
+
         ## post state to fit panel
         self.state.parameters =[]
         self.state.model =self.model
+        self.state.qmin = self.qmin_x
+        self.state.multi_factor = self.multi_factor
         self.disp_list =self.model.getDispParamList()
         self.state.disp_list = self.disp_list
Index: sansview/perspectives/fitting/models.py
===================================================================
--- sansview/perspectives/fitting/models.py	(revision 5062bbf9abf800e4b5ffab0375da1c4a03967672)
+++ sansview/perspectives/fitting/models.py	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
@@ -182,8 +182,8 @@
         self.shape_list.append(CoreShellModel)
         self.multiplication_factor.append(CoreShellModel)
-
-        from sans.models.CoreFourShellModel import CoreFourShellModel
-        self.shape_list.append(CoreFourShellModel)
-        self.multiplication_factor.append(CoreFourShellModel)
+        
+        from sans.models.CoreMultiShellModel import CoreMultiShellModel
+        self.shape_list.append(CoreMultiShellModel)
+        self.multiplication_factor.append(CoreMultiShellModel)
         
         from sans.models.VesicleModel import VesicleModel
Index: sansview/perspectives/fitting/pagestate.py
===================================================================
--- sansview/perspectives/fitting/pagestate.py	(revision b63dc6e69334f4bebbc94688bce123aa816c1c32)
+++ sansview/perspectives/fitting/pagestate.py	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
@@ -223,4 +223,5 @@
         self.npts = None
         self.name = ""
+        self.multi_factor = None
         ## enable smearering state
         self.enable_smearer = False
@@ -297,4 +298,5 @@
         obj.qmin = copy.deepcopy(self.qmin)
         obj.qmax = copy.deepcopy(self.qmax)
+        obj.multi_factor = copy.deepcopy(self.multi_factor)
         obj.npts = copy.deepcopy(self.npts )
         obj.cb1 = copy.deepcopy(self.cb1)
Index: setup_install/sansmodels_install.bat
===================================================================
--- setup_install/sansmodels_install.bat	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
+++ setup_install/sansmodels_install.bat	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
@@ -0,0 +1,5 @@
+@ECHO OFF
+CLS
+CD C:\ECLPS\workspace\trunk\sansmodels\src\ 
+COMMAND /C python setup.py install
+pause
Index: theoryview/perspectives/theory/basepage.py
===================================================================
--- theoryview/perspectives/theory/basepage.py	(revision 74755ffa1ce766f8db2e7443abc981f716782e5c)
+++ theoryview/perspectives/theory/basepage.py	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
@@ -94,5 +94,5 @@
         if self.model != None:
             self.disp_list= self.model.getDispParamList()
-        
+        self.multi_factor = None
         ##enable model 2D draw
         self.enable2D= False
@@ -497,4 +497,5 @@
             ## reset state of checkbox,textcrtl  and  regular parameters value
             self.reset_page(previous_state)      
+  
                   
     def onSave(self, event):
@@ -505,4 +506,6 @@
         if self.model==None:
             return 
+        self.save_current_state()
+        """
         if hasattr(self,"enable_disp"):
             self.state.enable_disp = copy.deepcopy(self.enable_disp.GetValue())
@@ -511,5 +514,5 @@
 
         self.state.model.name= self.model.name
-
+        """
         new_state = self.state.clone()
         new_state.model.name = self.state.model.name
@@ -697,5 +700,9 @@
         self.structurebox.SetSelection(state.structurecombobox )
         self.formfactorbox.SetSelection(state.formfactorcombobox)
-        
+        if state.multi_factor != None:
+            self.multifactorbox.SetSelection(state.multi_factor-1)
+        #draw the pnael according to the new model parameter 
+        self._on_select_model(event=None)
+       
         ## enable the view 2d button if this is a modelpage type
         if hasattr(self,"model_view"):
@@ -1153,5 +1160,5 @@
         else:
             boxsizer1 = box_sizer
-            
+        sizer_buttons = wx.BoxSizer(wx.HORIZONTAL)        
         #--------------------------------------------------------
         self.shape_rbutton = wx.RadioButton(self, -1, 'Shapes', style=wx.RB_GROUP)
@@ -1177,9 +1184,15 @@
         sizer_radiobutton.Add(self.struct_rbutton)
         
+        sizer_buttons.Add(sizer_radiobutton)
+        # detail button
+        if object !=None:
+            sizer_buttons.Add((50,0))
+            sizer_buttons.Add(object)
+            
         sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
-        
+        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
         self.text1 = wx.StaticText( self,-1,"" )
         self.text2 = wx.StaticText( self,-1,"P(Q)*S(Q)" )
-        
+        self.mutifactor_text = wx.StaticText( self,-1,"No. of Shells: " )
         
         self.formfactorbox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
@@ -1189,7 +1202,8 @@
             
         self.structurebox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
+        self.multifactorbox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
         wx.EVT_COMBOBOX(self.formfactorbox,-1, self._on_select_model)
         wx.EVT_COMBOBOX(self.structurebox,-1, self._on_select_model)
-        
+        wx.EVT_COMBOBOX(self.multifactorbox,-1, self._on_select_model)
     
         ## fill combox box
@@ -1224,13 +1238,17 @@
         sizer_selection.Add((5,5))
         sizer_selection.Add(self.structurebox)
-        sizer_selection.Add((5,5))
-        
-        boxsizer1.Add( sizer_radiobutton )
-        boxsizer1.Add( (20,20))
+        #sizer_selection.Add((5,5))
+        mutifactor_selection.Add((10,5))
+        mutifactor_selection.Add(self.mutifactor_text)
+        mutifactor_selection.Add(self.multifactorbox)
+        
+        boxsizer1.Add( sizer_buttons )
+        boxsizer1.Add( (15,15))
         boxsizer1.Add( sizer_selection )
-        if object !=None:
-            boxsizer1.Add( (-72,-72))
-            boxsizer1.Add( object,  0, wx.ALIGN_RIGHT| wx.RIGHT, 35)
-            boxsizer1.Add( (60,60))
+        boxsizer1.Add( (10,10))
+        boxsizer1.Add(mutifactor_selection)
+        
+        self._set_multfactor_combobox()
+
         #--------------------------------------------------------
         sizer.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
@@ -1238,4 +1256,31 @@
         self.SetScrollbars(20,20,25,65)
         
+    def _set_multfactor_combobox(self):   
+        """
+        Set comboBox for muitfactor of CoreMultiShellModel
+        """
+        for idx in range(1,5):
+            self.multifactorbox.Append(str(idx),int(idx))
+            self.multifactorbox.SetSelection(0) 
+        self._hide_multfactor_combobox()
+        
+    def _show_multfactor_combobox(self):   
+        """
+        Show the comboBox of muitfactor of CoreMultiShellModel
+        """ 
+        if not self.mutifactor_text.IsShown():
+            self.mutifactor_text.Show(True)
+        if not self.multifactorbox.IsShown():
+            self.multifactorbox.Show(True)  
+             
+    def _hide_multfactor_combobox(self):   
+        """
+        Hide the comboBox of muitfactor of CoreMultiShellModel
+        """ 
+        if self.mutifactor_text.IsShown():
+            self.mutifactor_text.Hide()
+        if self.multifactorbox.IsShown():
+            self.multifactorbox.Hide()   
+     
         
     def _show_combox(self, event):
@@ -1419,5 +1464,21 @@
             self.structurebox.Enable()
             self.text2.Enable()
-           
+
+        if form_factor != None:    
+            # set multifactor for Mutifunctional models    
+            if form_factor().name == 'CoreMultiShellModel':
+                self._show_multfactor_combobox()
+                m_id = self.multifactorbox.GetCurrentSelection()
+                self.multi_factor = self.multifactorbox.GetClientData(m_id)
+                if self.multi_factor == None: self.multi_factor =0
+                form_factor = form_factor(int(self.multi_factor))
+            else:
+                self._hide_multfactor_combobox()
+                form_factor = form_factor()
+                self.multi_factor = None
+        else:
+            self._hide_multfactor_combobox()
+            self.multi_factor = None  
+              
         s_id = self.structurebox.GetCurrentSelection()
         struct_factor = self.structurebox.GetClientData( s_id )
@@ -1425,9 +1486,9 @@
         if  struct_factor !=None:
             from sans.models.MultiplicationModel import MultiplicationModel
-            self.model= MultiplicationModel(form_factor(),struct_factor())
+            self.model= MultiplicationModel(form_factor,struct_factor())
             
         else:
             if form_factor != None:
-                self.model= form_factor()
+                self.model= form_factor
             else:
                 self.model = None
@@ -1437,4 +1498,6 @@
         self.state.parameters =[]
         self.state.model =self.model
+        self.state.qmin = self.qmin_x
+        self.state.multi_factor = self.multi_factor
         self.disp_list =self.model.getDispParamList()
         self.state.disp_list = self.disp_list
Index: theoryview/perspectives/theory/models.py
===================================================================
--- theoryview/perspectives/theory/models.py	(revision 74755ffa1ce766f8db2e7443abc981f716782e5c)
+++ theoryview/perspectives/theory/models.py	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
@@ -190,9 +190,9 @@
         self.shape_list.append(CoreShellModel)
         self.multiplication_factor.append(CoreShellModel)
-
-        from sans.models.CoreFourShellModel import CoreFourShellModel
-        self.shape_list.append(CoreFourShellModel)
-        self.multiplication_factor.append(CoreFourShellModel)
-        
+        
+        from sans.models.CoreMultiShellModel import CoreMultiShellModel
+        self.shape_list.append(CoreMultiShellModel)
+        self.multiplication_factor.append(CoreMultiShellModel)
+
         from sans.models.VesicleModel import VesicleModel
         self.shape_list.append(VesicleModel)
Index: theoryview/perspectives/theory/pagestate.py
===================================================================
--- theoryview/perspectives/theory/pagestate.py	(revision 74755ffa1ce766f8db2e7443abc981f716782e5c)
+++ theoryview/perspectives/theory/pagestate.py	(revision 4523b6854d1499ab35e8e024eeddfffd99dfe319)
@@ -84,4 +84,5 @@
         self.npts = None
         self.name=""
+        self.multi_factor = None
         ## enable smearering state
         self.enable_smearer = False
@@ -146,4 +147,5 @@
         obj.qmin = copy.deepcopy(self.qmin)
         obj.qmax = copy.deepcopy(self.qmax)
+        obj.multi_factor = copy.deepcopy(self.multi_factor)
         obj.npts = copy.deepcopy(self.npts )
         obj.cb1 = copy.deepcopy(self.cb1)
