Index: calculatorview/src/sans/perspectives/calculator/pyconsole.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/pyconsole.py	(revision fb582341ff7dff654d86cb90d74f4d5842bb9a71)
+++ calculatorview/src/sans/perspectives/calculator/pyconsole.py	(revision 5d1c1f400b7b591137fab18f8544efe0ba5d9e12)
@@ -3,7 +3,11 @@
 """
 import sys
+import os
 import wx
-#import wx.py.crust as crust
+import wx.lib.dialogs
 import wx.py.editor as editor
+import wx.py.frame as frame
+import py_compile
+
 if sys.platform.count("win32")>0:
     PANEL_WIDTH = 800
@@ -14,37 +18,81 @@
     PANEL_HEIGHT = 620
     FONT_VARIANT = 1
-    
+ID_COMPILE = wx.NewId() 
+ID_RUN = wx.NewId()  
+
+def compile_file(path):
+    """
+    Compile a python file
+    """
+    try:
+        import py_compile
+        py_compile.compile(file=path, doraise=True)
+    except:
+        type, value, traceback = sys.exc_info()
+        return value
+    return None 
+
 class PyConsole(editor.EditorNotebookFrame):
     ## Internal nickname for the window, used by the AUI manager
-    window_name = "Python Shell/Editor"
+    window_name = "Custom Model Editor"
     ## Name to appear on the window title bar
-    window_caption = "Python Shell/Editor"
+    window_caption = "Custom Model Editor"
     ## Flag to tell the AUI manager to put this panel in the center pane
     CENTER_PANE = False
-    def __init__(self, parent=None, manager=None,
+    def __init__(self, parent=None, manager=None, panel=None,
                     title='Python Shell/Editor', filename=None,
                     size=(PANEL_WIDTH, PANEL_HEIGHT)):
-        #if parent != None:
-        #    dataDir = parent._default_save_location
-        #else:
-        #     dataDir = None
-        #wx.py.crust.CrustFrame.__init__(self, parent=parent, 
-        #                                title=title, size=size,
-        #                                dataDir=dataDir)
         self.config = None
         editor.EditorNotebookFrame.__init__(self, parent=parent, 
                                         title=title, size=size,
                                         filename=filename)
-        self._import_site()
         self.parent = parent
         self._manager = manager
+        self.panel = panel
+        self._add_menu()
+        if filename != None:
+            dataDir = os.path.dirname(filename)
+        elif self.parent != None:
+            dataDir = self.parent._default_save_location
+        else:
+             dataDir = None
+        self.dataDir = dataDir
         self.Centre()
-
-    def _import_site(self):
-        """
-        Import site for exe
-        """
-        import site
-        
+        self.fileMenu.FindItemById(wx.ID_NEW).Enable(False)
+        self.fileMenu.Enable(wx.ID_OPEN, False)
+        self.Bind(wx.EVT_MENU, self.OnSaveFile, id=wx.ID_SAVE)
+        self.Bind(wx.EVT_MENU, self.OnSaveAsFile, id=wx.ID_SAVEAS)
+        self.Bind(wx.EVT_MENU, self.OnCompile, id=ID_COMPILE)
+        self.Bind(wx.EVT_MENU, self.OnRun, id=ID_RUN)
+        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateCompileMenu, id=ID_COMPILE)
+        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateCompileMenu, id=ID_RUN)
+        
+    def _add_menu(self):
+        """
+        Add menu
+        """
+        self.compileMenu = wx.Menu()
+        self.compileMenu.Append(ID_COMPILE, 'Compile',
+                 'Compile the file')
+        self.compileMenu.AppendSeparator()
+        self.compileMenu.Append(ID_RUN, 'Run',
+                 'Run the file in the Python Shell')
+        self.MenuBar.Insert(3, self.compileMenu, '&Compile')
+    
+    def OnHelp(self, event):
+        """
+        Show a help dialog.
+        """
+        import  wx.lib.dialogs
+        title = 'Help on key bindings'
+        text = wx.py.shell.HELP_TEXT
+        dlg = wx.lib.dialogs.ScrolledMessageDialog(self, text, title,
+                                                   size = ((700, 540)))
+        fnt = wx.Font(10, wx.TELETYPE, wx.NORMAL, wx.NORMAL)
+        dlg.GetChildren()[0].SetFont(fnt)
+        dlg.GetChildren()[0].SetInsertionPoint(0)
+        dlg.ShowModal()
+        dlg.Destroy()
+
     def set_manager(self, manager):
         """
@@ -61,7 +109,151 @@
                            wx.OK|wx.ICON_INFORMATION)  
         dial.ShowModal()
+        
+    def OnSaveFile(self, event):
+        """
+        OnFileSave overwrite   
+        """
+        self.OnFileSave(event)
+        self.Show(False)
+        self.Show(True)
+        
+    def OnSaveAsFile(self, event):
+        """
+        OnFileSaveAs overwrite   
+        """
+        self.OnFileSaveAs(event)
+        self.Show(False)
+        self.Show(True)
+
+    def bufferSaveAs(self):
+        """
+        Save buffer to a new filename: Bypass the annoying suggest save 
+        """
+        filedir = ''
+        if self.buffer and self.buffer.doc.filedir:
+            filedir = self.buffer.doc.filedir
+        result = editor.saveSingle(directory=filedir)
+        if result.path:
+            self.buffer.saveAs(result.path)
+            cancel = False
+        else:
+            cancel = True
+        return cancel
+    
+    def update_custom_combo(self):
+        """
+        Update custom model combo box in fit_panel
+        """
+        try:
+            page = self.panel.get_current_page()
+            temp = self.panel.update_model_list()
+            if temp:
+                page.model_list_box = temp
+                current_val = page.formfactorbox.GetValue()
+                pos = page.formfactorbox.GetSelection()
+                page._show_combox_helper()
+                page.formfactorbox.SetSelection(pos)
+                page.formfactorbox.SetValue(current_val)
+        except:
+            pass
+        
+    def OnRun(self, event):
+        """
+        Run
+        """
+        if self._check_changed():
+            return True
+        if self.buffer and self.buffer.doc.filepath:
+            self.editor.setFocus()
+            # Why we have to do this (Otherwise problems on Windows)?
+            forward_path = self.buffer.doc.filepath.replace('\\', '/')
+            self.shell.Execute("execfile('%s')"% forward_path)
+            self.shell.Hide()
+            self.shell.Show(True)
+            self.shell.SetFocus()
+        else:
+            mssg = "\n This is not a python file."
+            title = 'Error'
+            icon = wx.ICON_ERROR
+            wx.MessageBox(str(mssg), title, style=icon)
+        
+    def OnCompile(self, event):
+        """
+        Compile
+        """
+        if self._check_changed():
+            return True
+        if self._get_err_msg():
+            if self._manager != None and self.panel != None:
+                self._manager.set_edit_menu(self.parent)
+                wx.CallAfter(self.update_custom_combo)
+    
+    def _check_changed(self):   
+        """
+        If content was changed, suggest to save it first
+        """
+        if self.bufferHasChanged() and self.buffer.doc.filepath:
+            cancel = self.bufferSuggestSave()
+            if cancel:
+                return cancel
              
+    def _get_err_msg(self):
+        """
+        Get err_msg
+        """
+        name = None
+        mssg = "\n This is not a python file."
+        title = 'Error'
+        icon = wx.ICON_ERROR
+        try:
+            fname = self.editor.getStatus()[0]
+            name = os.path.basename(fname)
+            if name.split('.')[-1] != 'py':
+                wx.MessageBox(str(mssg), title, style=icon)
+                return False
+            msg = compile_file(fname)
+        except:
+            msg = None
+        if name == None:
+            wx.MessageBox(str(mssg), title, style=icon)
+            return False
+        mssg = "Compiling '%s'...\n"% name
+        if msg != None:
+            mssg += "Error occurred:\n"
+            mssg += str(msg)
+            title = 'Warning'
+            icon = wx.ICON_WARNING
+        else:
+            mssg += "Successful."
+            title = 'Info'
+            icon = wx.ICON_INFORMATION
+        dlg = wx.lib.dialogs.ScrolledMessageDialog(self, mssg, title, 
+                                                   size = ((550, 250)))
+        fnt = wx.Font(10, wx.TELETYPE, wx.NORMAL, wx.NORMAL)
+        dlg.GetChildren()[0].SetFont(fnt)
+        dlg.GetChildren()[0].SetInsertionPoint(0)
+        dlg.ShowModal()
+        dlg.Destroy()
+        return True
+    
+    def OnUpdateCompileMenu(self, event):
+        """
+        Update Compile menu items based on current tap.
+        """
+        win = wx.Window.FindFocus()
+        id = event.GetId()
+        event.Enable(True)
+        try:
+            if id == ID_COMPILE or id == ID_RUN:
+                menu_on = False
+                if self.buffer and self.buffer.doc.filepath:
+                    menu_on = True
+                event.Enable(menu_on)
+        except AttributeError:
+            # This menu option is not supported in the current context.
+            event.Enable(False)
+            
 ABOUT =  "Welcome to Python %s! \n\n"% sys.version.split()[0]
-ABOUT += "This uses Py Shell in wx (developed by Patrick K. O'Brien).\n"
+ABOUT += "This uses Py Shell/Editor in wx (developed by Patrick K. O'Brien).\n"
 ABOUT += "If this is your first time using Python, \n"
 ABOUT += "you should definitely check out the tutorial "
Index: fittingview/src/sans/perspectives/fitting/fitting.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/fitting.py	(revision 7b1ca974fd92da9d0ead56ca96eebdf53efe92e7)
+++ fittingview/src/sans/perspectives/fitting/fitting.py	(revision 5d1c1f400b7b591137fab18f8544efe0ba5d9e12)
@@ -219,10 +219,15 @@
         
         self.menu1.AppendSeparator()
-        self.id_editmodel = wx.NewId()
+        self.edit_model_menu = wx.Menu()
+        # Find and put files name in menu
+        try:
+            self.set_edit_menu(owner=owner)
+        except:
+            raise
+        
+        self.id_edit = wx.NewId()
         editmodel_help = "Edit cusomized model sample file" 
-        self.menu1.Append(self.id_editmodel, "Edit Custom Model", 
-                                   editmodel_help) 
-        wx.EVT_MENU(owner, self.id_editmodel,  self.edit_custom_model)
-        
+        self.menu1.AppendMenu(self.id_edit, "Edit Custom Model", 
+                              self.edit_model_menu, editmodel_help)
         #create  menubar items
         return [(self.menu1, self.sub_menu)]
@@ -232,10 +237,32 @@
         Get the python editor panel
         """
-        from editmodel import PyConsole
-        filename = os.path.join("plugins", "testmodel.py")
-        frame = PyConsole(parent=self.parent, filename=filename)
+        id = event.GetId()
+        label = self.edit_model_menu.GetLabel(id)
+        from sans.perspectives.calculator.pyconsole import PyConsole
+        filename = os.path.join(models.find_plugins_dir(), label)
+        frame = PyConsole(parent=self.parent, manager=self, panel= self.fit_panel,
+                          title='Custom Model Editor', filename=filename)
         self.put_icon(frame)
         frame.Show(True) 
         
+    def set_edit_menu(self, owner):    
+        """
+        Set list of the edit model menu labels
+        """
+        list_fnames = os.listdir(models.find_plugins_dir())
+        for item in list_fnames:
+            name = os.path.basename(item)
+            toks = os.path.splitext(name)
+            if toks[1]=='.py' and not toks[0]=='__init__':
+                has_file = False
+                for item in self.edit_model_menu.GetMenuItems():
+                    if name == self.edit_model_menu.GetLabel(item.GetId()):
+                        has_file = True
+                if not has_file:
+                    id = wx.NewId()
+                    self.edit_model_menu.Append(id, name) 
+                    wx.EVT_MENU(owner, id,  self.edit_custom_model)
+                    has_file = False
+
     def put_icon(self, frame):
         """
Index: fittingview/src/sans/perspectives/fitting/models.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/models.py	(revision 4ad076b534008e8f1faa2d8cc353c1a74b413607)
+++ fittingview/src/sans/perspectives/fitting/models.py	(revision 5d1c1f400b7b591137fab18f8544efe0ba5d9e12)
@@ -10,5 +10,5 @@
 import time
 import logging
-
+import py_compile
 from sans.guiframe.events import StatusEvent  
 # Explicitly import from the pluginmodel module so that py2exe
@@ -69,13 +69,8 @@
     return model
   
-  
-def _findModels(dir):
-    """
-    """
-    # List of plugin objects
-    plugins = {}
-    # Go through files in plug-in directory
-    #always recompile the folder plugin
-    import compileall
+def find_plugins_dir():
+    """
+    Find path of the plugins dir
+    """
     dir = os.path.abspath(PLUGIN_DIR)
     if not os.path.isdir(dir):
@@ -85,4 +80,37 @@
     if not os.path.isdir(dir):
         dir = os.path.join(os.path.dirname(os.path.sys.path[0]), PLUGIN_DIR)
+    return dir
+
+class ReportProblem:
+    def __nonzero__(self):
+        type, value, traceback = sys.exc_info()
+        if type is not None and issubclass(type, py_compile.PyCompileError):
+            print "Problem with", repr(value)
+            raise type, value, traceback
+        return 1
+    
+report_problem = ReportProblem()
+
+def compile_file(dir):
+    """
+    Compile a py file
+    """
+    try:
+        import compileall
+        compileall.compile_dir(dir=dir, ddir=dir, force=1, quiet=report_problem)
+    except:
+        type, value, traceback = sys.exc_info()
+        return value
+    return None
+
+def _findModels(dir):
+    """
+    """
+    # List of plugin objects
+    plugins = {}
+    # Go through files in plug-in directory
+    #always recompile the folder plugin
+    import compileall
+    dir = find_plugins_dir()
     if not os.path.isdir(dir):
         msg = "SansView couldn't locate Model plugin folder."
@@ -92,5 +120,5 @@
     else:
         log("looking for models in: %s" % str(dir))
-        compileall.compile_dir(dir=dir, ddir=dir, force=1, quiet=True)
+        compile_file(dir)
         logging.info("pluging model dir: %s\n" % str(dir))
     try:
@@ -438,5 +466,6 @@
         is_modified = False
         if os.path.isdir(PLUGIN_DIR):
-            temp =  os.path.getmtime(PLUGIN_DIR)
+            # getmtime doesn't seem to work well: use getatime
+            temp =  os.path.getatime(PLUGIN_DIR)
             if  self.last_time_dir_modified != temp:
                 is_modified = True
@@ -460,5 +489,6 @@
         else:
             return {}
-        
+    
+             
     def populate_menu(self, modelmenu, event_owner):
         """
