git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41077 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			842 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			842 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """PyAlaCarte and PyAlaMode editors."""
 | |
| 
 | |
| __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
 | |
| __cvsid__ = "$Id$"
 | |
| __revision__ = "$Revision$"[11:-2]
 | |
| 
 | |
| import wx
 | |
| 
 | |
| from buffer import Buffer
 | |
| import crust
 | |
| import dispatcher
 | |
| import editwindow
 | |
| import frame
 | |
| from shell import Shell
 | |
| import version
 | |
| 
 | |
| 
 | |
| class EditorFrame(frame.Frame):
 | |
|     """Frame containing one editor."""
 | |
| 
 | |
|     def __init__(self, parent=None, id=-1, title='PyAlaCarte',
 | |
|                  pos=wx.DefaultPosition, size=(800, 600), 
 | |
|                  style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE,
 | |
|                  filename=None):
 | |
|         """Create EditorFrame instance."""
 | |
|         frame.Frame.__init__(self, parent, id, title, pos, size, style)
 | |
|         self.buffers = {}
 | |
|         self.buffer = None  # Current buffer.
 | |
|         self.editor = None
 | |
|         self._defaultText = title + ' - the tastiest Python editor.'
 | |
|         self._statusText = self._defaultText
 | |
|         self.SetStatusText(self._statusText)
 | |
|         self.Bind(wx.EVT_IDLE, self.OnIdle)
 | |
|         self._setup()
 | |
|         if filename:
 | |
|             self.bufferCreate(filename)
 | |
| 
 | |
|     def _setup(self):
 | |
|         """Setup prior to first buffer creation.
 | |
| 
 | |
|         Useful for subclasses."""
 | |
|         pass
 | |
| 
 | |
|     def setEditor(self, editor):
 | |
|         self.editor = editor
 | |
|         self.buffer = self.editor.buffer
 | |
|         self.buffers[self.buffer.id] = self.buffer
 | |
| 
 | |
|     def OnAbout(self, event):
 | |
|         """Display an About window."""
 | |
|         title = 'About PyAlaCarte'
 | |
|         text = 'Another fine, flaky program.'
 | |
|         dialog = wx.MessageDialog(self, text, title,
 | |
|                                   wx.OK | wx.ICON_INFORMATION)
 | |
|         dialog.ShowModal()
 | |
|         dialog.Destroy()
 | |
| 
 | |
|     def OnClose(self, event):
 | |
|         """Event handler for closing."""
 | |
|         for buffer in self.buffers.values():
 | |
|             self.buffer = buffer
 | |
|             if buffer.hasChanged():
 | |
|                 cancel = self.bufferSuggestSave()
 | |
|                 if cancel and event.CanVeto():
 | |
|                     event.Veto()
 | |
|                     return
 | |
|         self.Destroy()
 | |
| 
 | |
|     def OnIdle(self, event):
 | |
|         """Event handler for idle time."""
 | |
|         self._updateStatus()
 | |
|         if hasattr(self, 'notebook'):
 | |
|             self._updateTabText()
 | |
|         self._updateTitle()
 | |
|         event.Skip()
 | |
| 
 | |
|     def _updateStatus(self):
 | |
|         """Show current status information."""
 | |
|         if self.editor and hasattr(self.editor, 'getStatus'):
 | |
|             status = self.editor.getStatus()
 | |
|             text = 'File: %s  |  Line: %d  |  Column: %d' % status
 | |
|         else:
 | |
|             text = self._defaultText
 | |
|         if text != self._statusText:
 | |
|             self.SetStatusText(text)
 | |
|             self._statusText = text
 | |
| 
 | |
|     def _updateTabText(self):
 | |
|         """Show current buffer information on notebook tab."""
 | |
| ##         suffix = ' **'
 | |
| ##         notebook = self.notebook
 | |
| ##         selection = notebook.GetSelection()
 | |
| ##         if selection == -1:
 | |
| ##             return
 | |
| ##         text = notebook.GetPageText(selection)
 | |
| ##         window = notebook.GetPage(selection)
 | |
| ##         if window.editor and window.editor.buffer.hasChanged():
 | |
| ##             if text.endswith(suffix):
 | |
| ##                 pass
 | |
| ##             else:
 | |
| ##                 notebook.SetPageText(selection, text + suffix)
 | |
| ##         else:
 | |
| ##             if text.endswith(suffix):
 | |
| ##                 notebook.SetPageText(selection, text[:len(suffix)])
 | |
| 
 | |
|     def _updateTitle(self):
 | |
|         """Show current title information."""
 | |
|         title = self.GetTitle()
 | |
|         if self.bufferHasChanged():
 | |
|             if title.startswith('* '):
 | |
|                 pass
 | |
|             else:
 | |
|                 self.SetTitle('* ' + title)
 | |
|         else:
 | |
|             if title.startswith('* '):
 | |
|                 self.SetTitle(title[2:])
 | |
|         
 | |
|     def hasBuffer(self):
 | |
|         """Return True if there is a current buffer."""
 | |
|         if self.buffer:
 | |
|             return True
 | |
|         else:
 | |
|             return False
 | |
| 
 | |
|     def bufferClose(self):
 | |
|         """Close buffer."""
 | |
|         if self.bufferHasChanged():
 | |
|             cancel = self.bufferSuggestSave()
 | |
|             if cancel:
 | |
|                 return cancel
 | |
|         self.bufferDestroy()
 | |
|         cancel = False
 | |
|         return cancel
 | |
| 
 | |
|     def bufferCreate(self, filename=None):
 | |
|         """Create new buffer."""
 | |
|         self.bufferDestroy()
 | |
|         buffer = Buffer()
 | |
|         self.panel = panel = wx.Panel(parent=self, id=-1)
 | |
|         panel.Bind (wx.EVT_ERASE_BACKGROUND, lambda x: x)        
 | |
|         editor = Editor(parent=panel)
 | |
|         panel.editor = editor
 | |
|         sizer = wx.BoxSizer(wx.VERTICAL)
 | |
|         sizer.Add(editor.window, 1, wx.EXPAND)
 | |
|         panel.SetSizer(sizer)
 | |
|         panel.SetAutoLayout(True)
 | |
|         sizer.Layout()
 | |
|         buffer.addEditor(editor)
 | |
|         buffer.open(filename)
 | |
|         self.setEditor(editor)
 | |
|         self.editor.setFocus()
 | |
|         self.SendSizeEvent()
 | |
|         
 | |
| 
 | |
|     def bufferDestroy(self):
 | |
|         """Destroy the current buffer."""
 | |
|         if self.buffer:
 | |
|             for editor in self.buffer.editors.values():
 | |
|                 editor.destroy()
 | |
|             self.editor = None
 | |
|             del self.buffers[self.buffer.id]
 | |
|             self.buffer = None
 | |
|             self.panel.Destroy()
 | |
| 
 | |
| 
 | |
|     def bufferHasChanged(self):
 | |
|         """Return True if buffer has changed since last save."""
 | |
|         if self.buffer:
 | |
|             return self.buffer.hasChanged()
 | |
|         else:
 | |
|             return False
 | |
| 
 | |
|     def bufferNew(self):
 | |
|         """Create new buffer."""
 | |
|         if self.bufferHasChanged():
 | |
|             cancel = self.bufferSuggestSave()
 | |
|             if cancel:
 | |
|                 return cancel
 | |
|         self.bufferCreate()
 | |
|         cancel = False
 | |
|         return cancel
 | |
| 
 | |
|     def bufferOpen(self):
 | |
|         """Open file in buffer."""
 | |
|         if self.bufferHasChanged():
 | |
|             cancel = self.bufferSuggestSave()
 | |
|             if cancel:
 | |
|                 return cancel
 | |
|         filedir = ''
 | |
|         if self.buffer and self.buffer.doc.filedir:
 | |
|             filedir = self.buffer.doc.filedir
 | |
|         result = openSingle(directory=filedir)
 | |
|         if result.path:
 | |
|             self.bufferCreate(result.path)
 | |
|         cancel = False
 | |
|         return cancel
 | |
| 
 | |
| ##     def bufferPrint(self):
 | |
| ##         """Print buffer."""
 | |
| ##         pass
 | |
| 
 | |
| ##     def bufferRevert(self):
 | |
| ##         """Revert buffer to version of file on disk."""
 | |
| ##         pass
 | |
| 
 | |
|     def bufferSave(self):
 | |
|         """Save buffer to its file."""
 | |
|         if self.buffer.doc.filepath:
 | |
|             self.buffer.save()
 | |
|             cancel = False
 | |
|         else:
 | |
|             cancel = self.bufferSaveAs()
 | |
|         return cancel
 | |
| 
 | |
|     def bufferSaveAs(self):
 | |
|         """Save buffer to a new filename."""
 | |
|         if self.bufferHasChanged() and self.buffer.doc.filepath:
 | |
|             cancel = self.bufferSuggestSave()
 | |
|             if cancel:
 | |
|                 return cancel
 | |
|         filedir = ''
 | |
|         if self.buffer and self.buffer.doc.filedir:
 | |
|             filedir = self.buffer.doc.filedir
 | |
|         result = saveSingle(directory=filedir)
 | |
|         if result.path:
 | |
|             self.buffer.saveAs(result.path)
 | |
|             cancel = False
 | |
|         else:
 | |
|             cancel = True
 | |
|         return cancel
 | |
| 
 | |
|     def bufferSuggestSave(self):
 | |
|         """Suggest saving changes.  Return True if user selected Cancel."""
 | |
|         result = messageDialog(parent=None,
 | |
|                                message='%s has changed.\n'
 | |
|                                        'Would you like to save it first'
 | |
|                                        '?' % self.buffer.name,
 | |
|                                title='Save current file?')
 | |
|         if result.positive:
 | |
|             cancel = self.bufferSave()
 | |
|         else:
 | |
|             cancel = result.text == 'Cancel'
 | |
|         return cancel
 | |
| 
 | |
|     def updateNamespace(self):
 | |
|         """Update the buffer namespace for autocompletion and calltips."""
 | |
|         if self.buffer.updateNamespace():
 | |
|             self.SetStatusText('Namespace updated')
 | |
|         else:
 | |
|             self.SetStatusText('Error executing, unable to update namespace')
 | |
| 
 | |
| 
 | |
| class EditorNotebookFrame(EditorFrame):
 | |
|     """Frame containing one or more editors in a notebook."""
 | |
| 
 | |
|     def __init__(self, parent=None, id=-1, title='PyAlaMode',
 | |
|                  pos=wx.DefaultPosition, size=(800, 600), 
 | |
|                  style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE,
 | |
|                  filename=None):
 | |
|         """Create EditorNotebookFrame instance."""
 | |
|         self.notebook = None
 | |
|         EditorFrame.__init__(self, parent, id, title, pos,
 | |
|                              size, style, filename)
 | |
|         if self.notebook:
 | |
|             dispatcher.connect(receiver=self._editorChange,
 | |
|                                signal='EditorChange', sender=self.notebook)
 | |
| 
 | |
|     def _setup(self):
 | |
|         """Setup prior to first buffer creation.
 | |
| 
 | |
|         Called automatically by base class during init."""
 | |
|         self.notebook = EditorNotebook(parent=self)
 | |
|         intro = 'Py %s' % version.VERSION
 | |
|         import imp
 | |
|         module = imp.new_module('__main__')
 | |
|         import __builtin__
 | |
|         module.__dict__['__builtins__'] = __builtin__
 | |
|         namespace = module.__dict__.copy()
 | |
|         self.crust = crust.Crust(parent=self.notebook, intro=intro, locals=namespace)
 | |
|         self.shell = self.crust.shell
 | |
|         # Override the filling so that status messages go to the status bar.
 | |
|         self.crust.filling.tree.setStatusText = self.SetStatusText
 | |
|         # Override the shell so that status messages go to the status bar.
 | |
|         self.shell.setStatusText = self.SetStatusText
 | |
|         # Fix a problem with the sash shrinking to nothing.
 | |
|         self.crust.filling.SetSashPosition(200)
 | |
|         self.notebook.AddPage(page=self.crust, text='*Shell*', select=True)
 | |
|         self.setEditor(self.crust.editor)
 | |
|         self.crust.editor.SetFocus()
 | |
| 
 | |
|     def _editorChange(self, editor):
 | |
|         """Editor change signal receiver."""
 | |
|         self.setEditor(editor)
 | |
| 
 | |
|     def OnAbout(self, event):
 | |
|         """Display an About window."""
 | |
|         title = 'About PyAlaMode'
 | |
|         text = 'Another fine, flaky program.'
 | |
|         dialog = wx.MessageDialog(self, text, title,
 | |
|                                   wx.OK | wx.ICON_INFORMATION)
 | |
|         dialog.ShowModal()
 | |
|         dialog.Destroy()
 | |
| 
 | |
|     def _updateTitle(self):
 | |
|         """Show current title information."""
 | |
|         pass
 | |
| ##         title = self.GetTitle()
 | |
| ##         if self.bufferHasChanged():
 | |
| ##             if title.startswith('* '):
 | |
| ##                 pass
 | |
| ##             else:
 | |
| ##                 self.SetTitle('* ' + title)
 | |
| ##         else:
 | |
| ##             if title.startswith('* '):
 | |
| ##                 self.SetTitle(title[2:])
 | |
|         
 | |
|     def bufferCreate(self, filename=None):
 | |
|         """Create new buffer."""
 | |
|         buffer = Buffer()
 | |
|         panel = wx.Panel(parent=self.notebook, id=-1)
 | |
|         panel.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: x)        
 | |
|         editor = Editor(parent=panel)
 | |
|         panel.editor = editor
 | |
|         sizer = wx.BoxSizer(wx.VERTICAL)
 | |
|         sizer.Add(editor.window, 1, wx.EXPAND)
 | |
|         panel.SetSizer(sizer)
 | |
|         panel.SetAutoLayout(True)
 | |
|         sizer.Layout()
 | |
|         buffer.addEditor(editor)
 | |
|         buffer.open(filename)
 | |
|         self.setEditor(editor)
 | |
|         self.notebook.AddPage(page=panel, text=self.buffer.name, select=True)
 | |
|         self.editor.setFocus()
 | |
| 
 | |
|     def bufferDestroy(self):
 | |
|         """Destroy the current buffer."""
 | |
|         selection = self.notebook.GetSelection()
 | |
| ##         print "Destroy Selection:", selection
 | |
|         if selection > 0:  # Don't destroy the PyCrust tab.
 | |
|             if self.buffer:
 | |
|                 del self.buffers[self.buffer.id]
 | |
|                 self.buffer = None  # Do this before DeletePage().
 | |
|             self.notebook.DeletePage(selection)
 | |
| 
 | |
|     def bufferNew(self):
 | |
|         """Create new buffer."""
 | |
|         self.bufferCreate()
 | |
|         cancel = False
 | |
|         return cancel
 | |
| 
 | |
|     def bufferOpen(self):
 | |
|         """Open file in buffer."""
 | |
|         filedir = ''
 | |
|         if self.buffer and self.buffer.doc.filedir:
 | |
|             filedir = self.buffer.doc.filedir
 | |
|         result = openMultiple(directory=filedir)
 | |
|         for path in result.paths:
 | |
|             self.bufferCreate(path)
 | |
|         cancel = False
 | |
|         return cancel
 | |
| 
 | |
| 
 | |
| class EditorNotebook(wx.Notebook):
 | |
|     """A notebook containing a page for each editor."""
 | |
| 
 | |
|     def __init__(self, parent):
 | |
|         """Create EditorNotebook instance."""
 | |
|         wx.Notebook.__init__(self, parent, id=-1, style=wx.CLIP_CHILDREN)
 | |
|         self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging, id=self.GetId())
 | |
|         self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged, id=self.GetId())
 | |
|         self.Bind(wx.EVT_IDLE, self.OnIdle)
 | |
| 
 | |
|     def OnIdle(self, event):
 | |
|         """Event handler for idle time."""
 | |
|         self._updateTabText()
 | |
|         event.Skip()
 | |
| 
 | |
|     def _updateTabText(self):
 | |
|         """Show current buffer display name on all but first tab."""
 | |
|         size = 3
 | |
|         changed = ' **'
 | |
|         unchanged = ' --'
 | |
|         selection = self.GetSelection()
 | |
|         if selection < 1:
 | |
|             return
 | |
|         text = self.GetPageText(selection)
 | |
|         window = self.GetPage(selection)
 | |
|         if not window.editor:
 | |
|             return
 | |
|         if text.endswith(changed) or text.endswith(unchanged):
 | |
|             name = text[:-size]
 | |
|         else:
 | |
|             name = text
 | |
|         if name != window.editor.buffer.name:
 | |
|             text = window.editor.buffer.name
 | |
|         if window.editor.buffer.hasChanged():
 | |
|             if text.endswith(changed):
 | |
|                 text = None
 | |
|             elif text.endswith(unchanged):
 | |
|                 text = text[:-size] + changed
 | |
|             else:
 | |
|                 text += changed
 | |
|         else:
 | |
|             if text.endswith(changed):
 | |
|                 text = text[:-size] + unchanged
 | |
|             elif text.endswith(unchanged):
 | |
|                 text = None
 | |
|             else:
 | |
|                 text += unchanged
 | |
|         if text is not None:
 | |
|             self.SetPageText(selection, text)
 | |
|             self.Refresh()  # Needed on Win98.
 | |
| 
 | |
|     def OnPageChanging(self, event):
 | |
|         """Page changing event handler."""
 | |
|         event.Skip()
 | |
| 
 | |
|     def OnPageChanged(self, event):
 | |
|         """Page changed event handler."""
 | |
|         new = event.GetSelection()
 | |
|         window = self.GetPage(new)
 | |
|         dispatcher.send(signal='EditorChange', sender=self,
 | |
|                         editor=window.editor)
 | |
|         window.SetFocus()
 | |
|         event.Skip()
 | |
| 
 | |
| 
 | |
| class EditorShellNotebookFrame(EditorNotebookFrame):
 | |
|     """Frame containing a notebook containing EditorShellNotebooks."""
 | |
| 
 | |
|     def __init__(self, parent=None, id=-1, title='PyAlaModeTest',
 | |
|                  pos=wx.DefaultPosition, size=(600, 400), 
 | |
|                  style=wx.DEFAULT_FRAME_STYLE,
 | |
|                  filename=None, singlefile=False):
 | |
|         """Create EditorShellNotebookFrame instance."""
 | |
|         self._singlefile = singlefile
 | |
|         EditorNotebookFrame.__init__(self, parent, id, title, pos,
 | |
|                                      size, style, filename)
 | |
| 
 | |
|     def _setup(self):
 | |
|         """Setup prior to first buffer creation.
 | |
| 
 | |
|         Called automatically by base class during init."""
 | |
|         if not self._singlefile:
 | |
|             self.notebook = EditorNotebook(parent=self)
 | |
| 
 | |
|     def OnAbout(self, event):
 | |
|         """Display an About window."""
 | |
|         title = 'About PyAlaModePlus'
 | |
|         text = 'Another fine, flaky program.'
 | |
|         dialog = wx.MessageDialog(self, text, title,
 | |
|                                   wx.OK | wx.ICON_INFORMATION)
 | |
|         dialog.ShowModal()
 | |
|         dialog.Destroy()
 | |
| 
 | |
|     def bufferCreate(self, filename=None):
 | |
|         """Create new buffer."""
 | |
|         if self._singlefile:
 | |
|             self.bufferDestroy()
 | |
|             notebook = EditorShellNotebook(parent=self,
 | |
|                                            filename=filename)
 | |
|             self.notebook = notebook
 | |
|         else:
 | |
|             notebook = EditorShellNotebook(parent=self.notebook,
 | |
|                                            filename=filename)
 | |
|         self.setEditor(notebook.editor)
 | |
|         if not self._singlefile:
 | |
|             self.notebook.AddPage(page=notebook, text=self.buffer.name,
 | |
|                                   select=True)
 | |
|         self.editor.setFocus()
 | |
| 
 | |
|     def bufferDestroy(self):
 | |
|         """Destroy the current buffer."""
 | |
|         if self.buffer:
 | |
|             self.editor = None
 | |
|             del self.buffers[self.buffer.id]
 | |
|             self.buffer = None  # Do this before DeletePage().
 | |
|         if self._singlefile:
 | |
|             self.notebook.Destroy()
 | |
|             self.notebook = None
 | |
|         else:
 | |
|             selection = self.notebook.GetSelection()
 | |
| ##             print "Destroy Selection:", selection
 | |
|             self.notebook.DeletePage(selection)
 | |
| 
 | |
|     def bufferNew(self):
 | |
|         """Create new buffer."""
 | |
|         if self._singlefile and self.bufferHasChanged():
 | |
|             cancel = self.bufferSuggestSave()
 | |
|             if cancel:
 | |
|                 return cancel
 | |
|         self.bufferCreate()
 | |
|         cancel = False
 | |
|         return cancel
 | |
| 
 | |
|     def bufferOpen(self):
 | |
|         """Open file in buffer."""
 | |
|         if self._singlefile and self.bufferHasChanged():
 | |
|             cancel = self.bufferSuggestSave()
 | |
|             if cancel:
 | |
|                 return cancel
 | |
|         filedir = ''
 | |
|         if self.buffer and self.buffer.doc.filedir:
 | |
|             filedir = self.buffer.doc.filedir
 | |
|         if self._singlefile:
 | |
|             result = openSingle(directory=filedir)
 | |
|             if result.path:
 | |
|                 self.bufferCreate(result.path)
 | |
|         else:
 | |
|             result = openMultiple(directory=filedir)
 | |
|             for path in result.paths:
 | |
|                 self.bufferCreate(path)
 | |
|         cancel = False
 | |
|         return cancel
 | |
| 
 | |
| 
 | |
| class EditorShellNotebook(wx.Notebook):
 | |
|     """A notebook containing an editor page and a shell page."""
 | |
| 
 | |
|     def __init__(self, parent, filename=None):
 | |
|         """Create EditorShellNotebook instance."""
 | |
|         wx.Notebook.__init__(self, parent, id=-1)
 | |
|         usePanels = True
 | |
|         if usePanels:
 | |
|             editorparent = editorpanel = wx.Panel(self, -1)
 | |
|             shellparent = shellpanel = wx.Panel(self, -1)
 | |
|         else:
 | |
|             editorparent = self
 | |
|             shellparent = self
 | |
|         self.buffer = Buffer()
 | |
|         self.editor = Editor(parent=editorparent)
 | |
|         self.buffer.addEditor(self.editor)
 | |
|         self.buffer.open(filename)
 | |
|         self.shell = Shell(parent=shellparent, locals=self.buffer.interp.locals,
 | |
|                            style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER)
 | |
|         self.buffer.interp.locals.clear()
 | |
|         if usePanels:
 | |
|             self.AddPage(page=editorpanel, text='Editor', select=True)
 | |
|             self.AddPage(page=shellpanel, text='Shell')
 | |
|             # Setup sizers
 | |
|             editorsizer = wx.BoxSizer(wx.VERTICAL)
 | |
|             editorsizer.Add(self.editor.window, 1, wx.EXPAND)
 | |
|             editorpanel.SetSizer(editorsizer)
 | |
|             editorpanel.SetAutoLayout(True)
 | |
|             shellsizer = wx.BoxSizer(wx.VERTICAL)
 | |
|             shellsizer.Add(self.shell, 1, wx.EXPAND)
 | |
|             shellpanel.SetSizer(shellsizer)
 | |
|             shellpanel.SetAutoLayout(True)
 | |
|         else:
 | |
|             self.AddPage(page=self.editor.window, text='Editor', select=True)
 | |
|             self.AddPage(page=self.shell, text='Shell')
 | |
|         self.editor.setFocus()
 | |
|         self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged, id=self.GetId())
 | |
| 
 | |
|     def OnPageChanged(self, event):
 | |
|         """Page changed event handler."""
 | |
|         selection = event.GetSelection()
 | |
|         if selection == 0:
 | |
|             self.editor.setFocus()
 | |
|         else:
 | |
|             self.shell.SetFocus()
 | |
|         event.Skip()
 | |
| 
 | |
|     def SetFocus(self):
 | |
|         wx.Notebook.SetFocus(self)
 | |
|         selection = self.GetSelection()
 | |
|         if selection == 0:
 | |
|             self.editor.setFocus()
 | |
|         else:
 | |
|             self.shell.SetFocus()
 | |
| 
 | |
| 
 | |
| class Editor:
 | |
|     """Editor having an EditWindow."""
 | |
| 
 | |
|     def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
 | |
|                  size=wx.DefaultSize,
 | |
|                  style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER):
 | |
|         """Create Editor instance."""
 | |
|         self.window = EditWindow(self, parent, id, pos, size, style)
 | |
|         self.id = self.window.GetId()
 | |
|         self.buffer = None
 | |
|         # Assign handlers for keyboard events.
 | |
|         self.window.Bind(wx.EVT_CHAR, self.OnChar)
 | |
|         self.window.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
 | |
| 
 | |
|     def _setBuffer(self, buffer, text):
 | |
|         """Set the editor to a buffer.  Private callback called by buffer."""
 | |
|         self.buffer = buffer
 | |
|         self.autoCompleteKeys = buffer.interp.getAutoCompleteKeys()
 | |
|         self.clearAll()
 | |
|         self.setText(text)
 | |
|         self.emptyUndoBuffer()
 | |
|         self.setSavePoint()
 | |
| 
 | |
|     def destroy(self):
 | |
|         """Destroy all editor objects."""
 | |
|         self.window.Destroy()
 | |
| 
 | |
|     def clearAll(self):
 | |
|         self.window.ClearAll()
 | |
| 
 | |
|     def emptyUndoBuffer(self):
 | |
|         self.window.EmptyUndoBuffer()
 | |
| 
 | |
|     def getStatus(self):
 | |
|         """Return (filepath, line, column) status tuple."""
 | |
|         if self.window:
 | |
|             pos = self.window.GetCurrentPos()
 | |
|             line = self.window.LineFromPosition(pos) + 1
 | |
|             col = self.window.GetColumn(pos)
 | |
|             if self.buffer:
 | |
|                 name = self.buffer.doc.filepath or self.buffer.name
 | |
|             else:
 | |
|                 name = ''
 | |
|             status = (name, line, col)
 | |
|             return status
 | |
|         else:
 | |
|             return ('', 0, 0)
 | |
| 
 | |
|     def getText(self):
 | |
|         """Return contents of editor."""
 | |
|         return self.window.GetText()
 | |
| 
 | |
|     def hasChanged(self):
 | |
|         """Return True if contents have changed."""
 | |
|         return self.window.GetModify()
 | |
| 
 | |
|     def setFocus(self):
 | |
|         """Set the input focus to the editor window."""
 | |
|         self.window.SetFocus()
 | |
| 
 | |
|     def setSavePoint(self):
 | |
|         self.window.SetSavePoint()
 | |
| 
 | |
|     def setText(self, text):
 | |
|         """Set contents of editor."""
 | |
|         self.window.SetText(text)
 | |
| 
 | |
|     def OnChar(self, event):
 | |
|         """Keypress event handler.
 | |
|         
 | |
|         Only receives an event if OnKeyDown calls event.Skip() for the
 | |
|         corresponding event."""
 | |
| 
 | |
|         key = event.GetKeyCode()
 | |
|         if key in self.autoCompleteKeys:
 | |
|             # Usually the dot (period) key activates auto completion.
 | |
|             if self.window.AutoCompActive(): 
 | |
|                 self.window.AutoCompCancel()
 | |
|             self.window.ReplaceSelection('')
 | |
|             self.window.AddText(chr(key))
 | |
|             text, pos = self.window.GetCurLine()
 | |
|             text = text[:pos]
 | |
|             if self.window.autoComplete: 
 | |
|                 self.autoCompleteShow(text)
 | |
|         elif key == ord('('):
 | |
|             # The left paren activates a call tip and cancels an
 | |
|             # active auto completion.
 | |
|             if self.window.AutoCompActive(): 
 | |
|                 self.window.AutoCompCancel()
 | |
|             self.window.ReplaceSelection('')
 | |
|             self.window.AddText('(')
 | |
|             text, pos = self.window.GetCurLine()
 | |
|             text = text[:pos]
 | |
|             self.autoCallTipShow(text)
 | |
|         else:
 | |
|             # Allow the normal event handling to take place.
 | |
|             event.Skip()
 | |
| 
 | |
|     def OnKeyDown(self, event):
 | |
|         """Key down event handler."""
 | |
| 
 | |
|         key = event.GetKeyCode()
 | |
|         # If the auto-complete window is up let it do its thing.
 | |
|         if self.window.AutoCompActive():
 | |
|             event.Skip()
 | |
|             return
 | |
|         controlDown = event.ControlDown()
 | |
|         altDown = event.AltDown()
 | |
|         shiftDown = event.ShiftDown()
 | |
|         # Let Ctrl-Alt-* get handled normally.
 | |
|         if controlDown and altDown:
 | |
|             event.Skip()
 | |
|         # Increase font size.
 | |
|         elif controlDown and key in (ord(']'),):
 | |
|             dispatcher.send(signal='FontIncrease')
 | |
|         # Decrease font size.
 | |
|         elif controlDown and key in (ord('['),):
 | |
|             dispatcher.send(signal='FontDecrease')
 | |
|         # Default font size.
 | |
|         elif controlDown and key in (ord('='),):
 | |
|             dispatcher.send(signal='FontDefault')
 | |
|         else:
 | |
|             event.Skip()
 | |
| 
 | |
|     def autoCompleteShow(self, command):
 | |
|         """Display auto-completion popup list."""
 | |
|         list = self.buffer.interp.getAutoCompleteList(command, 
 | |
|                     includeMagic=self.window.autoCompleteIncludeMagic, 
 | |
|                     includeSingle=self.window.autoCompleteIncludeSingle, 
 | |
|                     includeDouble=self.window.autoCompleteIncludeDouble)
 | |
|         if list:
 | |
|             options = ' '.join(list)
 | |
|             offset = 0
 | |
|             self.window.AutoCompShow(offset, options)
 | |
| 
 | |
|     def autoCallTipShow(self, command):
 | |
|         """Display argument spec and docstring in a popup window."""
 | |
|         if self.window.CallTipActive():
 | |
|             self.window.CallTipCancel()
 | |
|         (name, argspec, tip) = self.buffer.interp.getCallTip(command)
 | |
|         if tip:
 | |
|             dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip)
 | |
|         if not self.window.autoCallTip:
 | |
|             return
 | |
|         if argspec:
 | |
|             startpos = self.window.GetCurrentPos()
 | |
|             self.window.AddText(argspec + ')')
 | |
|             endpos = self.window.GetCurrentPos()
 | |
|             self.window.SetSelection(endpos, startpos)
 | |
|         if tip:
 | |
|             curpos = self.window.GetCurrentPos()
 | |
|             size = len(name)
 | |
|             tippos = curpos - (size + 1)
 | |
|             fallback = curpos - self.window.GetColumn(curpos)
 | |
|             # In case there isn't enough room, only go back to the
 | |
|             # fallback.
 | |
|             tippos = max(tippos, fallback)
 | |
|             self.window.CallTipShow(tippos, tip)
 | |
|             self.window.CallTipSetHighlight(0, size)
 | |
| 
 | |
| 
 | |
| class EditWindow(editwindow.EditWindow):
 | |
|     """EditWindow based on StyledTextCtrl."""
 | |
| 
 | |
|     def __init__(self, editor, parent, id=-1, pos=wx.DefaultPosition,
 | |
|                  size=wx.DefaultSize,
 | |
|                  style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER):
 | |
|         """Create EditWindow instance."""
 | |
|         editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
 | |
|         self.editor = editor
 | |
| 
 | |
| 
 | |
| class DialogResults:
 | |
|     """DialogResults class."""
 | |
| 
 | |
|     def __init__(self, returned):
 | |
|         """Create wrapper for results returned by dialog."""
 | |
|         self.returned = returned
 | |
|         self.positive = returned in (wx.ID_OK, wx.ID_YES)
 | |
|         self.text = self._asString()
 | |
|         
 | |
| 
 | |
|     def __repr__(self):
 | |
|         return str(self.__dict__)
 | |
| 
 | |
|     def _asString(self):
 | |
|         returned = self.returned
 | |
|         if returned == wx.ID_OK:
 | |
|             return "Ok"
 | |
|         elif returned == wx.ID_CANCEL:
 | |
|             return "Cancel"
 | |
|         elif returned == wx.ID_YES:
 | |
|             return "Yes"
 | |
|         elif returned == wx.ID_NO:
 | |
|             return "No"
 | |
| 
 | |
| 
 | |
| def fileDialog(parent=None, title='Open', directory='', filename='',
 | |
|                wildcard='All Files (*.*)|*.*',
 | |
|                style=wx.OPEN | wx.MULTIPLE):
 | |
|     """File dialog wrapper function."""
 | |
|     dialog = wx.FileDialog(parent, title, directory, filename,
 | |
|                            wildcard, style)
 | |
|     result = DialogResults(dialog.ShowModal())
 | |
|     if result.positive:
 | |
|         result.paths = dialog.GetPaths()
 | |
|     else:
 | |
|         result.paths = []
 | |
|     dialog.Destroy()
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def openSingle(parent=None, title='Open', directory='', filename='',
 | |
|                wildcard='All Files (*.*)|*.*', style=wx.OPEN):
 | |
|     """File dialog wrapper function."""
 | |
|     dialog = wx.FileDialog(parent, title, directory, filename,
 | |
|                            wildcard, style)
 | |
|     result = DialogResults(dialog.ShowModal())
 | |
|     if result.positive:
 | |
|         result.path = dialog.GetPath()
 | |
|     else:
 | |
|         result.path = None
 | |
|     dialog.Destroy()
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def openMultiple(parent=None, title='Open', directory='', filename='',
 | |
|                  wildcard='All Files (*.*)|*.*',
 | |
|                  style=wx.OPEN | wx.MULTIPLE):
 | |
|     """File dialog wrapper function."""
 | |
|     return fileDialog(parent, title, directory, filename, wildcard, style)
 | |
| 
 | |
| 
 | |
| def saveSingle(parent=None, title='Save', directory='', filename='',
 | |
|                wildcard='All Files (*.*)|*.*',
 | |
|                style=wx.SAVE | wx.OVERWRITE_PROMPT):
 | |
|     """File dialog wrapper function."""
 | |
|     dialog = wx.FileDialog(parent, title, directory, filename,
 | |
|                            wildcard, style)
 | |
|     result = DialogResults(dialog.ShowModal())
 | |
|     if result.positive:
 | |
|         result.path = dialog.GetPath()
 | |
|     else:
 | |
|         result.path = None
 | |
|     dialog.Destroy()
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def directory(parent=None, message='Choose a directory', path='', style=0,
 | |
|               pos=wx.DefaultPosition, size=wx.DefaultSize):
 | |
|     """Dir dialog wrapper function."""
 | |
|     dialog = wx.DirDialog(parent, message, path, style, pos, size)
 | |
|     result = DialogResults(dialog.ShowModal())
 | |
|     if result.positive:
 | |
|         result.path = dialog.GetPath()
 | |
|     else:
 | |
|         result.path = None
 | |
|     dialog.Destroy()
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def messageDialog(parent=None, message='', title='Message box',
 | |
|                   style=wx.YES_NO | wx.CANCEL | wx.CENTRE | wx.ICON_QUESTION,
 | |
|                   pos=wx.DefaultPosition):
 | |
|     """Message dialog wrapper function."""
 | |
|     dialog = wx.MessageDialog(parent, message, title, style, pos)
 | |
|     result = DialogResults(dialog.ShowModal())
 | |
|     dialog.Destroy()
 | |
|     return result
 |