Index: calculatorview/src/sans/perspectives/calculator/calculator.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/calculator.py	(revision fb582341ff7dff654d86cb90d74f4d5842bb9a71)
+++ calculatorview/src/sans/perspectives/calculator/calculator.py	(revision 9520702b5a0445c549187ce2cd62de05686a28d4)
@@ -57,7 +57,11 @@
         resolution_help += "resolution of Q in 2D based on the SANS "
         resolution_help += "instrumental parameter values."
+        mass_volume_help = "Based on the chemical formular, "
+        mass_volume_help += "compute the mass density or the molar volume."
         pyconsole_help = "Python Console."
         #data_editor_help = "Meta Data Editor"
         return [("SLD Calculator", sld_help, self.on_calculate_sld),
+                ("Density/Volume Calculator", mass_volume_help, 
+                                            self.on_calculate_dv),
                 ("Slit Size Calculator", slit_length_help,
                         self.on_calculate_slit_size),
@@ -77,5 +81,4 @@
         self.put_icon(frame)
         frame.Show(True)
-        event.Skip()
 
     def on_calculate_kiessig(self, event):
@@ -96,5 +99,14 @@
         self.put_icon(frame)
         frame.Show(True) 
-       
+    
+    def on_calculate_dv(self, event):
+        """
+        Compute the mass density or molar voulme
+        """
+        from density_panel import DensityWindow
+        frame = DensityWindow(base=self.parent)
+        self.put_icon(frame)
+        frame.Show(True) 
+              
     def on_calculate_slit_size(self, event):
         """
Index: calculatorview/src/sans/perspectives/calculator/density_panel.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/density_panel.py	(revision 9520702b5a0445c549187ce2cd62de05686a28d4)
+++ calculatorview/src/sans/perspectives/calculator/density_panel.py	(revision 9520702b5a0445c549187ce2cd62de05686a28d4)
@@ -0,0 +1,415 @@
+"""
+This module provide GUI for the neutron scattering length density calculator
+
+"""
+
+import wx
+import math
+import sys
+
+from sans.guiframe.panel_base import PanelBase
+from wx.lib.scrolledpanel import ScrolledPanel
+from sans.guiframe.utils import format_number
+from sans.guiframe.utils import check_float
+from sans.guiframe.events import StatusEvent  
+
+# the calculator default value for wavelength is 6
+#import periodictable
+from periodictable import formula as Formula
+from periodictable.xsf import xray_energy
+from periodictable.xsf import xray_sld_from_atoms
+from periodictable.nsf import neutron_scattering
+       
+AVOGADRO =  6.02214129e23
+
+_SCALE = 1e-6
+_INPUTS = ['Mass Density', 'Molar Volume']
+_UNITS = ['g/cm^(3)     ', 'cm^(3)/mol ']
+#SLD panel size 
+if sys.platform.count("win32") > 0:
+    _STATICBOX_WIDTH = 410
+    _BOX_WIDTH = 200
+    PANEL_SIZE = 440
+    FONT_VARIANT = 0
+else:
+    _STATICBOX_WIDTH = 430
+    _BOX_WIDTH = 200
+    PANEL_SIZE = 460
+    FONT_VARIANT = 1
+    
+class DensityPanel(ScrolledPanel, PanelBase):
+    """
+    Provides the mass density calculator GUI.
+    """
+    ## Internal nickname for the window, used by the AUI manager
+    window_name = "Mass Density Calculator"
+    ## Name to appear on the window title bar
+    window_caption = "Mass Density Calculator"
+    ## Flag to tell the AUI manager to put this panel in the center pane
+    CENTER_PANE = True
+    
+    def __init__(self, parent, base=None, *args, **kwds):
+        """
+        """
+        ScrolledPanel.__init__(self, parent, *args, **kwds)
+        PanelBase.__init__(self)
+        self.SetupScrolling()
+        #Font size 
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        # Object that receive status event
+        self.base = base
+        # chemeical formula, string
+        self.compound = ''
+        # value of the density/volume, float
+        self.input = None
+        # text controls
+        self.compound_ctl = None
+        self.input_ctl = None
+        self.molar_mass_ctl = None
+        self.output_ctl = None
+        self.ctr_color = self.GetBackgroundColour()
+        # button
+        self.button_calculate = None
+        # list
+        self._input_list = _INPUTS
+        self._input = self._input_list[1]
+        self._output = self._input_list[0]
+        self._unit_list = _UNITS
+        #Draw the panel
+        self._do_layout()
+        self.SetAutoLayout(True)
+        self.Layout()
+       
+    def _do_layout(self):
+        """
+        Draw window content
+        """
+        # units
+        unit_density = self._unit_list[0]
+        unit_volume = self._unit_list[1]
+        
+        # sizers
+        sizer_input = wx.GridBagSizer(5, 5)
+        sizer_output = wx.GridBagSizer(5, 5)
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer1 = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
+        sizer3 = wx.BoxSizer(wx.HORIZONTAL)
+        vbox  = wx.BoxSizer(wx.VERTICAL)
+        
+        # inputs
+        inputbox = wx.StaticBox(self, -1, "Input")
+        boxsizer1 = wx.StaticBoxSizer(inputbox, wx.VERTICAL)
+        boxsizer1.SetMinSize((_STATICBOX_WIDTH, -1))
+        compound_txt = wx.StaticText(self, -1, 'Molecular Formula ')
+        self.compound_ctl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
+        self.compound_eg1 = wx.StaticText(self, -1, '     e.g., H2O')
+        self.compound_eg2 = wx.StaticText(self, -1, 'e.g., D2O')
+        self.input_cb = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        wx.EVT_COMBOBOX(self.input_cb, -1, self.on_select_input) 
+        hint_input_name_txt = 'Mass or volume.'
+        self.input_cb.SetToolTipString(hint_input_name_txt) 
+        unit_density1 = "     " + unit_density
+        self.input_ctl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
+        self.unit_input_density = wx.StaticText(self, -1, unit_density1)
+        self.unit_input_volume = wx.StaticText(self, -1, unit_volume)
+        iy = 0
+        ix = 0
+        sizer_input.Add(compound_txt, (iy, ix), (1, 1),
+                             wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer_input.Add(self.compound_ctl, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix += 1
+        sizer_input.Add(self.compound_eg1, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        
+        ix += 1
+        sizer_input.Add(self.compound_eg2, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.compound_eg1.Show(False)
+        iy += 1
+        ix = 0
+        sizer_input.Add(self.input_cb, (iy, ix), (1, 1),
+                             wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer_input.Add(self.input_ctl, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix +=1
+        sizer_input.Add(self.unit_input_density,(iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix +=1
+        self.unit_input_density.Show(False)
+        sizer_input.Add(self.unit_input_volume,(iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        boxsizer1.Add(sizer_input)
+        self.sizer1.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
+        
+        # outputs
+        outputbox = wx.StaticBox(self, -1, "Output")
+        boxsizer2 = wx.StaticBoxSizer(outputbox, wx.VERTICAL)
+        boxsizer2.SetMinSize((_STATICBOX_WIDTH, -1))
+        
+        molar_mass_txt = wx.StaticText(self, -1, 'Molar Mass ')
+        self.molar_mass_ctl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
+        self.molar_mass_ctl.SetEditable(False)
+        self.molar_mass_ctl.SetBackgroundColour(self.ctr_color)
+        self.molar_mass_unit1 = wx.StaticText(self, -1, '     g/mol')
+        self.molar_mass_unit2 = wx.StaticText(self, -1, 'g/mol')
+        
+        self.output_cb = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        wx.EVT_COMBOBOX(self.output_cb, -1, self.on_select_output) 
+        hint_output_name_txt = 'Mass or volume.'
+        self.output_cb.SetToolTipString(hint_output_name_txt) 
+        list = []
+        for item in self._input_list:
+            name = str(item)
+            list.append(name)
+        list.sort()
+        for idx in range(len(list)):
+            self.input_cb.Append(list[idx],idx)
+            self.output_cb.Append(list[idx],idx)
+        self.input_cb.SetStringSelection("Molar Volume") 
+        self.output_cb.SetStringSelection("Mass Density") 
+        unit_volume = "     " + unit_volume
+        self.output_ctl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
+        self.output_ctl.SetEditable(False)
+        self.output_ctl.SetBackgroundColour(self.ctr_color)
+        self.unit_output_density = wx.StaticText(self, -1, unit_density)
+        self.unit_output_volume = wx.StaticText(self, -1, unit_volume)
+        iy = 0
+        ix = 0
+        sizer_output.Add(molar_mass_txt, (iy, ix), (1, 1),
+                             wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer_output.Add(self.molar_mass_ctl, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix += 1
+        sizer_output.Add(self.molar_mass_unit1, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix += 1
+        sizer_output.Add(self.molar_mass_unit2, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.molar_mass_unit1.Show(False)
+        iy += 1
+        ix = 0
+        sizer_output.Add(self.output_cb, (iy, ix), (1, 1),
+                             wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        sizer_output.Add(self.output_ctl, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix +=1
+        sizer_output.Add(self.unit_output_volume,
+                         (iy, ix), (1, 1), wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix += 1
+        sizer_output.Add(self.unit_output_density,
+                         (iy, ix), (1, 1), wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        
+        self.unit_output_volume.Show(False)
+        boxsizer2.Add(sizer_output)
+        self.sizer2.Add(boxsizer2, 0, wx.EXPAND|wx.ALL, 10)
+        
+        # buttons
+        id = wx.NewId()
+        self.button_calculate = wx.Button(self, id, "Calculate")
+        self.button_calculate.SetToolTipString("Calculate.")
+        self.Bind(wx.EVT_BUTTON, self.calculate, id=id)   
+        
+        sizer_button.Add((250, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        sizer_button.Add(self.button_calculate, 0, 
+                                        wx.RIGHT|wx.ADJUST_MINSIZE, 20)
+        sizer3.Add(sizer_button)
+        
+        # layout
+        vbox.Add(self.sizer1)
+        vbox.Add(self.sizer2)
+        vbox.Add(sizer3)
+        vbox.Fit(self) 
+        self.SetSizer(vbox)
+    
+    def on_select_input(self, event):
+        """
+        On selection of input combobox, 
+        update units and output combobox
+        """
+        if event == None:
+            return
+        event.Skip()
+        
+        combo = event.GetEventObject()
+        self._input = combo.GetValue()
+        for name in self._input_list:
+            if self._input != name:
+                self._output = name
+                break
+
+        self.set_values()
+    
+    def on_select_output(self, event):
+        """
+        On selection of output combobox, 
+        update units and input combobox
+        """
+        if event == None:
+            return
+        event.Skip()
+        
+        combo = event.GetEventObject()
+        self._output = combo.GetValue()
+        for name in self._input_list:
+            if self._output != name:
+                self._input = name
+                break
+
+        self.set_values()
+  
+    def set_values(self):
+        """
+        Sets units and combobox values
+        """
+        input, output = self.get_input()
+        if input is None:
+            return
+        # input
+        self.input_cb.SetValue(str(input))
+        # output
+        self.output_cb.SetValue(str(output))
+        # unit
+        if self._input_list.index(input) == 0:
+            self.molar_mass_unit1.Show(True)
+            self.molar_mass_unit2.Show(False)
+            self.compound_eg1.Show(True)
+            self.compound_eg2.Show(False)
+            self.unit_input_density.Show(True)
+            self.unit_output_volume.Show(True)
+            self.unit_input_volume.Show(False)
+            self.unit_output_density.Show(False) 
+        else:
+            self.molar_mass_unit1.Show(False)
+            self.molar_mass_unit2.Show(True)
+            self.compound_eg1.Show(False)
+            self.compound_eg2.Show(True)
+            self.unit_input_volume.Show(True)
+            self.unit_output_density.Show(True)
+            self.unit_input_density.Show(False)
+            self.unit_output_volume.Show(False)
+        # layout    
+        self.clear_outputs()
+        self.sizer1.Layout()  
+        self.sizer2.Layout()    
+        
+    def get_input(self):
+        """
+        Return the current input and output combobox values
+        """
+        return self._input, self._output
+    
+    def check_inputs(self):
+        """
+        Check validity user inputs
+        """
+        flag = True
+        msg = ""
+        if check_float(self.input_ctl):
+            self.input = float(self.input_ctl.GetValue())
+        else:
+            flag = False
+            input_type = str(self.input_cb.GetValue())
+            msg += "Error for %s value :expect float"% input_type
+                
+        self.compound = self.compound_ctl.GetValue().lstrip().rstrip()
+        if self.compound != "":
+            try :
+                Formula(self.compound)
+                self.compound_ctl.SetBackgroundColour(wx.WHITE)
+                self.compound_ctl.Refresh()
+            except:
+                self.compound_ctl.SetBackgroundColour("pink")
+                self.compound_ctl.Refresh()
+                flag = False
+                msg += "Enter correct formula"
+        else:
+            self.compound_ctl.SetBackgroundColour("pink")
+            self.compound_ctl.Refresh()
+            flag = False
+            msg += "Enter Formula"
+        return flag, msg
+        
+
+    def calculate(self, event):
+        """
+        Calculate the mass Density/molar Volume of the molecules
+        """
+        self.clear_outputs()
+        try:
+            #Check validity user inputs
+            flag, msg = self.check_inputs()
+            if self.base is not None and msg.lstrip().rstrip() != "":
+                msg = "Density/Volume Calculator: %s" % str(msg)
+                wx.PostEvent(self.base, StatusEvent(status=msg))
+            if not flag:
+               return 
+            #get ready to compute
+            mol_formula = Formula(self.compound)
+            molar_mass = float(mol_formula.molecular_mass) * AVOGADRO
+            output = self._format_number(molar_mass / self.input)
+            self.molar_mass_ctl.SetValue(str(self._format_number(molar_mass)))
+            self.output_ctl.SetValue(str(output))
+        except:
+            if self.base is not None:
+                msg = "Density/Volume Calculator: %s"%(sys.exc_value)
+                wx.PostEvent(self.base, StatusEvent(status=msg))
+        if event is not None:
+            event.Skip()
+            
+    def clear_outputs(self):
+        """
+        Clear the outputs textctrl
+        """
+        self.molar_mass_ctl.SetValue("")
+        self.output_ctl.SetValue("")
+        
+    def _format_number(self, value=None):
+        """
+        Return a float in a standardized, human-readable formatted string 
+        """
+        try: 
+            value = float(value)
+        except:
+            output = ''
+            return output
+
+        output = "%-12.5f" % value
+        return output.lstrip().rstrip()  
+        
+class DensityWindow(wx.Frame):
+    """
+    """
+    def __init__(self, parent=None, title="Density/Volume Calculator",
+                  base=None, size=(PANEL_SIZE, PANEL_SIZE/1.7), *args, **kwds):
+        """
+        """
+        kwds['title'] = title
+        kwds['size'] = size
+        wx.Frame.__init__(self, parent, *args, **kwds)
+        """
+        """
+        self.panel = DensityPanel(self, base=base)
+        self.Centre()
+        self.Show(True)
+        
+class ViewApp(wx.App):
+    """
+    """
+    def OnInit(self):
+        """
+        """
+        frame = DensityWindow(None, title="Density/Volume Calculator")    
+        frame.Show(True)
+        self.SetTopWindow(frame)
+        return True
+        
+
+if __name__ == "__main__": 
+    app = ViewApp(0)
+    app.MainLoop()     
