Major restructuring to get a better foundation.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_4_BRANCH@20227 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Patrick K. O'Brien
2003-04-15 22:03:03 +00:00
parent 9d5aa5d876
commit 64386c666c
9 changed files with 509 additions and 260 deletions

View File

@@ -4,12 +4,12 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
import base
import buffer
import crust
import dispatcher
import document
import editor
import editwindow
import filling
import frame
import images

View File

@@ -1,4 +1,4 @@
"""Base editor."""
"""EditWindow class."""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
@@ -42,14 +42,14 @@ else: # GTK
}
class Editor(stc.wxStyledTextCtrl):
"""Editor based on StyledTextCtrl."""
class EditWindow(stc.wxStyledTextCtrl):
"""EditWindow based on StyledTextCtrl."""
revision = __revision__
def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN | wx.wxSUNKEN_BORDER):
"""Create an Editor instance."""
"""Create EditWindow instance."""
stc.wxStyledTextCtrl.__init__(self, parent, id, pos, size, style)
self.__config()
stc.EVT_STC_UPDATEUI(self, id, self.OnUpdateUI)

View File

@@ -6,6 +6,7 @@ __revision__ = "$Revision$"[11:-2]
from wxPython import wx
from interpreter import Interpreter
import imp
import os
import sys
@@ -24,13 +25,14 @@ class Buffer:
id = 0
def __init__(self, editor, interp, filename=None):
def __init__(self, filename=None):
"""Create a Buffer instance."""
Buffer.id += 1
self.id = Buffer.id
self.interp = Interpreter(locals={})
self.name = ''
self.editor = editor
self.interp = interp
self.editors = {}
self.editor = None
self.modules = sys.modules.keys()
self.syspath = sys.path[:]
while True:
@@ -45,18 +47,17 @@ class Buffer:
break
self.open(filename)
def getStatus(self):
"""Return (filepath, line, column) status tuple."""
editor = self.editor
pos = editor.GetCurrentPos()
line = editor.LineFromPosition(pos) + 1
col = editor.GetColumn(pos)
status = (self.doc.filepath or self.name, line, col)
return status
def addEditor(self, editor):
"""Add an editor."""
self.editor = editor
self.editors[editor.id] = editor
def hasChanged(self):
"""Return True if text in editor has changed since last save."""
return self.editor.GetModify()
if self.editor:
return self.editor.hasChanged()
else:
return False
def new(self, filepath):
"""New empty buffer."""
@@ -72,14 +73,16 @@ class Buffer:
self.doc = document.Document(filename)
self.name = self.doc.filename or ('Untitled:' + str(self.id))
self.modulename = self.doc.filebase
if self.doc.filepath and os.path.exists(self.doc.filepath):
self.editor.ClearAll()
self.editor.SetText(self.doc.read())
self.editor.EmptyUndoBuffer()
self.editor.SetSavePoint()
self.confirmed = True
# XXX This should really make sure filedir is first item in syspath.
# XXX Or maybe this should be moved to the update namespace method.
if self.doc.filedir and self.doc.filedir not in self.syspath:
# To create the proper context for updateNamespace.
self.syspath.insert(0, self.doc.filedir)
if self.doc.filepath and os.path.exists(self.doc.filepath):
self.confirmed = True
if self.editor:
text = self.doc.read()
self.editor._setBuffer(buffer=self, text=text)
def overwriteConfirm(filepath):
"""Confirm overwriting an existing file."""
@@ -95,43 +98,36 @@ class Buffer:
if not self.confirmed:
self.confirmed = self.overwriteConfirm(filepath)
if self.confirmed:
self.doc.write(self.editor.GetText())
self.editor.SetSavePoint()
self.doc.write(self.editor.getText())
if self.editor:
self.editor.setSavePoint()
def saveAs(self, filename):
"""Save buffer."""
self.doc = document.Document(filename)
self.name = self.doc.filename
self.modulename = self.doc.filebase
filepath = self.doc.filepath
if not filepath:
return # XXX Get filename
## if not os.path.exists(filepath):
self.confirmed = True
if not self.confirmed:
self.confirmed = self.overwriteConfirm(filepath)
if self.confirmed:
self.doc.write(self.editor.GetText())
self.editor.SetSavePoint()
self.save()
def updateNamespace(self):
"""Update the namespace for autocompletion and calltips.
Return True if updated, False if there was an error."""
backup = self.interp.locals
if not self.interp or not hasattr(self.editor, 'getText'):
return False
syspath = sys.path
sys.path = self.syspath
code = self.editor.GetText()
code = self.editor.getText()
module = imp.new_module(str(self.modulename))
namespace = module.__dict__.copy()
try:
try:
exec code in namespace
except:
self.interp.locals = backup
return False
else:
self.interp.locals = namespace
self.interp.locals.clear()
self.interp.locals.update(namespace)
return True
finally:
sys.path = syspath

View File

@@ -38,7 +38,7 @@ class Crust(wx.wxSplitterWindow):
self.shell = Shell(parent=self, introText=intro,
locals=locals, InterpClass=InterpClass,
*args, **kwds)
self.buffer = self.shell.buffer
self.editor = self.shell
if rootObject is None:
rootObject = self.shell.interp.locals
self.notebook = wx.wxNotebook(parent=self, id=-1)

View File

@@ -30,11 +30,14 @@ class Document:
def read(self):
"""Return contents of file."""
if self.filepath and os.path.exists(self.filepath):
f = file(self.filepath, 'rb')
try:
return f.read()
finally:
f.close()
else:
return ''
def write(self, text):
"""Write text to file."""

View File

@@ -6,13 +6,12 @@ __revision__ = "$Revision$"[11:-2]
from wxPython import wx
import base
import buffer
from buffer import Buffer
import crust
import dispatcher
import editwindow
import frame
import interpreter
import shell
from shell import Shell
import version
try:
@@ -28,12 +27,13 @@ class EditorFrame(frame.Frame):
def __init__(self, parent=None, id=-1, title='PyAlaCarte',
pos=wx.wxDefaultPosition, size=(800, 600),
style=wx.wxDEFAULT_FRAME_STYLE, filename=None):
"""Create an EditorFrame instance."""
"""Create EditorFrame instance."""
frame.Frame.__init__(self, parent, id, title, pos, size, style)
self._buffers = {}
self._buffer = None # Current buffer.
self.buffers = {}
self.buffer = None # Current buffer.
self.editor = None
self._statusText = title + ' - the tastiest Python editor.'
self._defaultText = title + ' - the tastiest Python editor.'
self._statusText = self._defaultText
self.SetStatusText(self._statusText)
wx.EVT_IDLE(self, self.OnIdle)
self._setup()
@@ -46,6 +46,11 @@ class EditorFrame(frame.Frame):
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'
@@ -57,8 +62,8 @@ class EditorFrame(frame.Frame):
def OnClose(self, event):
"""Event handler for closing."""
for buffer in self._buffers.values():
self._buffer = buffer
for buffer in self.buffers.values():
self.buffer = buffer
if buffer.hasChanged():
cancel = self.bufferSuggestSave()
if cancel and event.CanVeto():
@@ -74,9 +79,11 @@ class EditorFrame(frame.Frame):
def _updateStatus(self):
"""Show current status information."""
if self._buffer:
status = self._buffer.getStatus()
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
@@ -95,7 +102,7 @@ class EditorFrame(frame.Frame):
def hasBuffer(self):
"""Return True if there is a current buffer."""
if self._buffer:
if self.buffer:
return True
else:
return False
@@ -113,25 +120,26 @@ class EditorFrame(frame.Frame):
def bufferCreate(self, filename=None):
"""Create new buffer."""
self.bufferDestroy()
interp = interpreter.Interpreter(locals={})
self.editor = Editor(interp=interp, parent=self, filename=filename)
self._buffer = self.editor.buffer
self._buffers[self._buffer.id] = self._buffer
self._buffer.editor.SetFocus()
buffer = Buffer()
editor = Editor(parent=self)
buffer.addEditor(editor)
buffer.open(filename)
self.setEditor(editor)
self.editor.setFocus()
def bufferDestroy(self):
"""Destroy the current buffer."""
if self._buffer:
del self._buffers[self._buffer.id]
self._buffer = None
if self.editor:
self.editor.Destroy()
if self.buffer:
for editor in self.buffer.editors.values():
editor.destroy()
self.editor = None
del self.buffers[self.buffer.id]
self.buffer = None
def bufferHasChanged(self):
"""Return True if buffer has changed since last save."""
if self._buffer:
return self._buffer.hasChanged()
if self.buffer:
return self.buffer.hasChanged()
else:
return False
@@ -152,8 +160,8 @@ class EditorFrame(frame.Frame):
if cancel:
return cancel
filedir = ''
if self._buffer and self._buffer.doc.filedir:
filedir = self._buffer.doc.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)
@@ -170,8 +178,8 @@ class EditorFrame(frame.Frame):
def bufferSave(self):
"""Save buffer to its file."""
if self._buffer.doc.filepath:
self._buffer.save()
if self.buffer.doc.filepath:
self.buffer.save()
cancel = False
else:
cancel = self.bufferSaveAs()
@@ -179,16 +187,16 @@ class EditorFrame(frame.Frame):
def bufferSaveAs(self):
"""Save buffer to a new filename."""
if self.bufferHasChanged() and self._buffer.doc.filepath:
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
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)
self.buffer.saveAs(result.path)
cancel = False
else:
cancel = True
@@ -199,7 +207,7 @@ class EditorFrame(frame.Frame):
result = messageDialog(parent=None,
message='%s has changed.\n'
'Would you like to save it first'
'?' % self._buffer.name,
'?' % self.buffer.name,
title='Save current file?')
if result.positive:
cancel = self.bufferSave()
@@ -209,7 +217,7 @@ class EditorFrame(frame.Frame):
def updateNamespace(self):
"""Update the buffer namespace for autocompletion and calltips."""
if self._buffer.updateNamespace():
if self.buffer.updateNamespace():
self.SetStatusText('Namespace updated')
else:
self.SetStatusText('Error executing, unable to update namespace')
@@ -221,24 +229,26 @@ class EditorNotebookFrame(EditorFrame):
def __init__(self, parent=None, id=-1, title='PyAlaMode',
pos=wx.wxDefaultPosition, size=(800, 600),
style=wx.wxDEFAULT_FRAME_STYLE, filename=None):
"""Create an EditorNotebookFrame instance."""
"""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.
Useful for subclasses."""
self._notebook = BufferNotebook(parent=self)
dispatcher.connect(receiver=self._bufferChange,
signal='BufferChange', sender=self._notebook)
Called automatically by base class during init."""
self.notebook = EditorNotebook(parent=self)
intro = 'PyCrust %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.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
@@ -246,14 +256,12 @@ class EditorNotebookFrame(EditorFrame):
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='PyCrust', select=True)
self._buffer = self.crust.buffer
self._buffers[self._buffer.id] = self._buffer
self._buffer.editor.SetFocus()
self.notebook.AddPage(page=self.crust, text='PyCrust', select=True)
self.setEditor(self.crust.editor)
def _bufferChange(self, buffer):
"""Buffer change signal receiver."""
self._buffer = buffer
def _editorChange(self, editor):
"""Editor change signal receiver."""
self.setEditor(editor)
def OnAbout(self, event):
"""Display an About window."""
@@ -278,24 +286,24 @@ class EditorNotebookFrame(EditorFrame):
def bufferCreate(self, filename=None):
"""Create new buffer."""
interp = interpreter.Interpreter(locals={})
editor = Editor(interp=interp, parent=self._notebook,
filename=filename)
self._buffer = editor.buffer
self._buffers[self._buffer.id] = self._buffer
self._notebook.AddPage(page=editor, text=self._buffer.name,
buffer = Buffer()
editor = Editor(parent=self.notebook)
buffer.addEditor(editor)
buffer.open(filename)
self.setEditor(editor)
self.notebook.AddPage(page=editor.window, text=self.buffer.name,
select=True)
self._buffer.editor.SetFocus()
self.editor.setFocus()
def bufferDestroy(self):
"""Destroy the current buffer."""
selection = self._notebook.GetSelection()
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)
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."""
@@ -306,8 +314,8 @@ class EditorNotebookFrame(EditorFrame):
def bufferOpen(self):
"""Open file in buffer."""
filedir = ''
if self._buffer and self._buffer.doc.filedir:
filedir = self._buffer.doc.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)
@@ -315,14 +323,16 @@ class EditorNotebookFrame(EditorFrame):
return cancel
class BufferNotebook(wx.wxNotebook):
"""A notebook containing a page for each buffer."""
class EditorNotebook(wx.wxNotebook):
"""A notebook containing a page for each editor."""
def __init__(self, parent):
"""Create a BufferNotebook instance."""
"""Create EditorNotebook instance."""
wx.wxNotebook.__init__(self, parent, id=-1)
wx.EVT_NOTEBOOK_PAGE_CHANGING(self, self.GetId(), self.OnPageChanging)
wx.EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged)
wx.EVT_NOTEBOOK_PAGE_CHANGING(self, self.GetId(),
self.OnPageChanging)
wx.EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(),
self.OnPageChanged)
def OnPageChanging(self, event):
"""Page changing event handler."""
@@ -338,87 +348,70 @@ class BufferNotebook(wx.wxNotebook):
## print "Changed from:", old
new = event.GetSelection()
## print "Changed to new:", new
page = self.GetPage(new)
buffer = page.buffer
buffer.editor.SetFocus()
dispatcher.send(signal='BufferChange', sender=self, buffer=buffer)
window = self.GetPage(new)
dispatcher.send(signal='EditorChange', sender=self,
editor=window.editor)
window.SetFocus()
event.Skip()
class BufferEditorShellNotebookFrame(EditorFrame):
"""Frame containing one or more editor notebooks."""
class EditorShellNotebookFrame(EditorNotebookFrame):
"""Frame containing a notebook containing EditorShellNotebooks."""
def __init__(self, parent=None, id=-1, title='PyAlaMode',
pos=wx.wxDefaultPosition, size=(600, 400),
style=wx.wxDEFAULT_FRAME_STYLE,
filename=None, singlefile=False):
"""Create a BufferEditorShellNotebookFrame instance."""
"""Create EditorShellNotebookFrame instance."""
self._singlefile = singlefile
EditorFrame.__init__(self, parent, id, title, pos,
EditorNotebookFrame.__init__(self, parent, id, title, pos,
size, style, filename)
def _setup(self):
"""Setup prior to first buffer creation.
Useful for subclasses."""
Called automatically by base class during init."""
if not self._singlefile:
self._notebook = BufferNotebook(parent=self)
dispatcher.connect(receiver=self._bufferChange,
signal='BufferChange', sender=self._notebook)
def _bufferChange(self, buffer):
"""Buffer change signal receiver."""
self._buffer = buffer
self.notebook = EditorNotebook(parent=self)
def OnAbout(self, event):
"""Display an About window."""
title = 'About PyAlaMode'
title = 'About PyAlaModePlus'
text = 'Another fine, flaky program.'
dialog = wx.wxMessageDialog(self, text, title,
wx.wxOK | wx.wxICON_INFORMATION)
dialog.ShowModal()
dialog.Destroy()
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 bufferCreate(self, filename=None):
"""Create new buffer."""
if self._singlefile:
self.bufferDestroy()
notebook = self._notebook = EditorShellNotebook(parent=self,
notebook = EditorShellNotebook(parent=self,
filename=filename)
self.notebook = notebook
else:
notebook = EditorShellNotebook(parent=self._notebook,
notebook = EditorShellNotebook(parent=self.notebook,
filename=filename)
self._buffer = notebook.buffer
self.setEditor(notebook.editor)
if not self._singlefile:
self._notebook.AddPage(page=notebook, text=self._buffer.name,
self.notebook.AddPage(page=notebook, text=self.buffer.name,
select=True)
self._buffers[self._buffer.id] = self._buffer
self._buffer.editor.SetFocus()
self.editor.setFocus()
def bufferDestroy(self):
"""Destroy the current buffer."""
if self._buffer:
del self._buffers[self._buffer.id]
self._buffer = None # Do this before DeletePage().
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
self.notebook.Destroy()
self.notebook = None
else:
selection = self._notebook.GetSelection()
print "Destroy Selection:", selection
self._notebook.DeletePage(selection)
selection = self.notebook.GetSelection()
## print "Destroy Selection:", selection
self.notebook.DeletePage(selection)
def bufferNew(self):
"""Create new buffer."""
@@ -437,8 +430,8 @@ class BufferEditorShellNotebookFrame(EditorFrame):
if cancel:
return cancel
filedir = ''
if self._buffer and self._buffer.doc.filedir:
filedir = self._buffer.doc.filedir
if self.buffer and self.buffer.doc.filedir:
filedir = self.buffer.doc.filedir
if self._singlefile:
result = openSingle(directory=filedir)
if result.path:
@@ -451,94 +444,125 @@ class BufferEditorShellNotebookFrame(EditorFrame):
return cancel
class BufferEditorShellNotebook(wx.wxNotebook):
"""A notebook containing a page for each buffer."""
def __init__(self, parent):
"""Create a BufferEditorShellNotebook instance."""
wx.wxNotebook.__init__(self, parent, id=-1)
wx.EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged)
def OnPageChanged(self, event):
"""Page changed event handler."""
## old = event.GetOldSelection()
## print "Changed from old:", old
new = event.GetSelection()
## print "Changed to new:", new
page = self.GetPage(new)
buffer = page.buffer
subselection = page.GetSelection()
page.focus(subselection)
dispatcher.send(signal='BufferChange', sender=self, buffer=buffer)
event.Skip()
class EditorShellNotebook(wx.wxNotebook):
"""A notebook containing an editor page and a shell page."""
def __init__(self, parent, filename=None):
"""Create an EditorShellNotebook instance."""
"""Create EditorShellNotebook instance."""
wx.wxNotebook.__init__(self, parent, id=-1)
usePanels = True
if usePanels:
shellparent = shellpanel = wx.wxPanel(self, -1)
editorparent = editorpanel = wx.wxPanel(self, -1)
shellparent = shellpanel = wx.wxPanel(self, -1)
else:
shellparent = self
editorparent = self
self.shell = shell.Shell(parent=shellparent,
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.wxCLIP_CHILDREN | wx.wxSUNKEN_BORDER)
self.editor = Editor(interp=self.shell.interp, parent=editorparent,
filename=filename)
self.buffer.interp.locals.clear()
if usePanels:
self.AddPage(page=editorpanel, text='File', select=True)
self.AddPage(page=editorpanel, text='Editor', select=True)
self.AddPage(page=shellpanel, text='Shell')
# Setup sizers
editorsizer = wx.wxBoxSizer(wx.wxVERTICAL)
editorsizer.Add(self.editor.window, 1, wx.wxEXPAND)
editorpanel.SetSizer(editorsizer)
editorpanel.SetAutoLayout(True)
shellsizer = wx.wxBoxSizer(wx.wxVERTICAL)
shellsizer.Add(self.shell, 1, wx.wxEXPAND)
shellpanel.SetSizer(shellsizer)
shellpanel.SetAutoLayout(True)
editorsizer = wx.wxBoxSizer(wx.wxVERTICAL)
editorsizer.Add(self.editor, 1, wx.wxEXPAND)
editorpanel.SetSizer(editorsizer)
editorpanel.SetAutoLayout(True)
else:
self.AddPage(page=self.editor, text='File', select=True)
self.AddPage(page=self.editor.window, text='Editor', select=True)
self.AddPage(page=self.shell, text='Shell')
self.buffer = self.editor.buffer
self.editor.SetFocus()
self.editor.setFocus()
wx.EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged)
def OnPageChanged(self, event):
"""Page changed event handler."""
selection = event.GetSelection()
self.focus(selection)
if selection == 0:
self.editor.setFocus()
else:
self.shell.SetFocus()
event.Skip()
def focus(self, selection):
def SetFocus(self):
wx.wxNotebook.SetFocus(self)
selection = self.GetSelection()
if selection == 0:
self.editor.SetFocus()
self.editor.setFocus()
else:
self.shell.SetFocus()
class Editor(base.Editor):
"""Editor based on StyledTextCtrl."""
class Editor:
"""Editor having an EditWindow."""
def __init__(self, interp, parent, id=-1, pos=wx.wxDefaultPosition,
def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
size=wx.wxDefaultSize,
style=wx.wxCLIP_CHILDREN | wx.wxSUNKEN_BORDER,
filename=None):
"""Create a Editor instance."""
base.Editor.__init__(self, parent, id, pos, size, style)
self.interp = interp
# Find out for which keycodes the interpreter will autocomplete.
self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
style=wx.wxCLIP_CHILDREN | wx.wxSUNKEN_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.
wx.EVT_CHAR(self, self.OnChar)
wx.EVT_KEY_DOWN(self, self.OnKeyDown)
self.buffer = buffer.Buffer(editor=self, interp=self.interp,
filename=filename)
wx.EVT_CHAR(self.window, self.OnChar)
wx.EVT_KEY_DOWN(self.window, 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."""
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
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.
@@ -549,22 +573,22 @@ class Editor(base.Editor):
key = event.KeyCode()
if key in self.autoCompleteKeys:
# Usually the dot (period) key activates auto completion.
if self.AutoCompActive():
self.AutoCompCancel()
self.ReplaceSelection('')
self.AddText(chr(key))
text, pos = self.GetCurLine()
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.autoComplete:
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.AutoCompActive():
self.AutoCompCancel()
self.ReplaceSelection('')
self.AddText('(')
text, pos = self.GetCurLine()
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:
@@ -576,7 +600,7 @@ class Editor(base.Editor):
key = event.KeyCode()
# If the auto-complete window is up let it do its thing.
if self.AutoCompActive():
if self.window.AutoCompActive():
event.Skip()
return
controlDown = event.ControlDown()
@@ -599,46 +623,57 @@ class Editor(base.Editor):
def autoCompleteShow(self, command):
"""Display auto-completion popup list."""
list = self.interp.getAutoCompleteList(command,
includeMagic=self.autoCompleteIncludeMagic,
includeSingle=self.autoCompleteIncludeSingle,
includeDouble=self.autoCompleteIncludeDouble)
list = self.buffer.interp.getAutoCompleteList(command,
includeMagic=self.window.autoCompleteIncludeMagic,
includeSingle=self.window.autoCompleteIncludeSingle,
includeDouble=self.window.autoCompleteIncludeDouble)
if list and len(list) < 2000:
options = ' '.join(list)
offset = 0
self.AutoCompShow(offset, options)
self.window.AutoCompShow(offset, options)
def autoCallTipShow(self, command):
"""Display argument spec and docstring in a popup window."""
if self.CallTipActive():
self.CallTipCancel()
(name, argspec, tip) = self.interp.getCallTip(command)
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.autoCallTip:
if not self.window.autoCallTip:
return
if argspec:
startpos = self.GetCurrentPos()
self.AddText(argspec + ')')
endpos = self.GetCurrentPos()
self.SetSelection(endpos, startpos)
startpos = self.window.GetCurrentPos()
self.window.AddText(argspec + ')')
endpos = self.window.GetCurrentPos()
self.window.SetSelection(endpos, startpos)
if tip:
curpos = self.GetCurrentPos()
curpos = self.window.GetCurrentPos()
size = len(name)
tippos = curpos - (size + 1)
fallback = curpos - self.GetColumn(curpos)
fallback = curpos - self.window.GetColumn(curpos)
# In case there isn't enough room, only go back to the
# fallback.
tippos = max(tippos, fallback)
self.CallTipShow(tippos, tip)
self.CallTipSetHighlight(0, size)
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.wxDefaultPosition,
size=wx.wxDefaultSize,
style=wx.wxCLIP_CHILDREN | wx.wxSUNKEN_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 a wrapper for the results returned by a dialog."""
"""Create wrapper for results returned by dialog."""
self.returned = returned
self.positive = returned in (wx.wxID_OK, wx.wxID_YES)
self.text = self._asString()

View File

@@ -0,0 +1,195 @@
"""EditWindow class."""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
from wxPython import wx
from wxPython import stc
import keyword
import os
import sys
import time
import dispatcher
from version import VERSION
try:
True
except NameError:
True = 1==1
False = 1==0
if wx.wxPlatform == '__WXMSW__':
FACES = { 'times' : 'Times New Roman',
'mono' : 'Courier New',
'helv' : 'Lucida Console',
'lucida' : 'Lucida Console',
'other' : 'Comic Sans MS',
'size' : 10,
'lnsize' : 9,
'backcol': '#FFFFFF',
}
else: # GTK
FACES = { 'times' : 'Times',
'mono' : 'Courier',
'helv' : 'Helvetica',
'other' : 'new century schoolbook',
'size' : 12,
'lnsize' : 10,
'backcol': '#FFFFFF',
}
class EditWindow(stc.wxStyledTextCtrl):
"""EditWindow based on StyledTextCtrl."""
revision = __revision__
def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN | wx.wxSUNKEN_BORDER):
"""Create EditWindow instance."""
stc.wxStyledTextCtrl.__init__(self, parent, id, pos, size, style)
self.__config()
stc.EVT_STC_UPDATEUI(self, id, self.OnUpdateUI)
dispatcher.connect(receiver=self._fontsizer, signal='FontIncrease')
dispatcher.connect(receiver=self._fontsizer, signal='FontDecrease')
dispatcher.connect(receiver=self._fontsizer, signal='FontDefault')
def _fontsizer(self, signal):
"""Receiver for Font* signals."""
size = self.GetZoom()
if signal == 'FontIncrease':
size += 1
elif signal == 'FontDecrease':
size -= 1
elif signal == 'FontDefault':
size = 0
self.SetZoom(size)
def __config(self):
"""Configure shell based on user preferences."""
self.SetMarginType(1, stc.wxSTC_MARGIN_NUMBER)
self.SetMarginWidth(1, 40)
self.SetLexer(stc.wxSTC_LEX_PYTHON)
self.SetKeyWords(0, ' '.join(keyword.kwlist))
self.setStyles(FACES)
self.SetViewWhiteSpace(False)
self.SetTabWidth(4)
self.SetUseTabs(False)
# Do we want to automatically pop up command completion options?
self.autoComplete = True
self.autoCompleteIncludeMagic = True
self.autoCompleteIncludeSingle = True
self.autoCompleteIncludeDouble = True
self.autoCompleteCaseInsensitive = True
self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
self.AutoCompSetAutoHide(False)
self.AutoCompStops(' .,;:([)]}\'"\\<>%^&+-=*/|`')
# Do we want to automatically pop up command argument help?
self.autoCallTip = True
self.CallTipSetBackground(wx.wxColour(255, 255, 232))
self.SetWrapMode(False)
try:
self.SetEndAtLastLine(False)
except AttributeError:
pass
def setStyles(self, faces):
"""Configure font size, typeface and color for lexer."""
# Default style
self.StyleSetSpec(stc.wxSTC_STYLE_DEFAULT,
"face:%(mono)s,size:%(size)d,back:%(backcol)s" % \
faces)
self.StyleClearAll()
# Built in styles
self.StyleSetSpec(stc.wxSTC_STYLE_LINENUMBER,
"back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces)
self.StyleSetSpec(stc.wxSTC_STYLE_CONTROLCHAR,
"face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_STYLE_BRACELIGHT,
"fore:#0000FF,back:#FFFF88")
self.StyleSetSpec(stc.wxSTC_STYLE_BRACEBAD,
"fore:#FF0000,back:#FFFF88")
# Python styles
self.StyleSetSpec(stc.wxSTC_P_DEFAULT,
"face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_COMMENTLINE,
"fore:#007F00,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_NUMBER,
"")
self.StyleSetSpec(stc.wxSTC_P_STRING,
"fore:#7F007F,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_CHARACTER,
"fore:#7F007F,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_WORD,
"fore:#00007F,bold")
self.StyleSetSpec(stc.wxSTC_P_TRIPLE,
"fore:#7F0000")
self.StyleSetSpec(stc.wxSTC_P_TRIPLEDOUBLE,
"fore:#000033,back:#FFFFE8")
self.StyleSetSpec(stc.wxSTC_P_CLASSNAME,
"fore:#0000FF,bold")
self.StyleSetSpec(stc.wxSTC_P_DEFNAME,
"fore:#007F7F,bold")
self.StyleSetSpec(stc.wxSTC_P_OPERATOR,
"")
self.StyleSetSpec(stc.wxSTC_P_IDENTIFIER,
"")
self.StyleSetSpec(stc.wxSTC_P_COMMENTBLOCK,
"fore:#7F7F7F")
self.StyleSetSpec(stc.wxSTC_P_STRINGEOL,
"fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces)
def OnUpdateUI(self, event):
"""Check for matching braces."""
# If the auto-complete window is up let it do its thing.
if self.AutoCompActive() or self.CallTipActive():
return
braceAtCaret = -1
braceOpposite = -1
charBefore = None
caretPos = self.GetCurrentPos()
if caretPos > 0:
charBefore = self.GetCharAt(caretPos - 1)
styleBefore = self.GetStyleAt(caretPos - 1)
# Check before.
if charBefore and chr(charBefore) in '[]{}()' \
and styleBefore == stc.wxSTC_P_OPERATOR:
braceAtCaret = caretPos - 1
# Check after.
if braceAtCaret < 0:
charAfter = self.GetCharAt(caretPos)
styleAfter = self.GetStyleAt(caretPos)
if charAfter and chr(charAfter) in '[]{}()' \
and styleAfter == stc.wxSTC_P_OPERATOR:
braceAtCaret = caretPos
if braceAtCaret >= 0:
braceOpposite = self.BraceMatch(braceAtCaret)
if braceAtCaret != -1 and braceOpposite == -1:
self.BraceBadLight(braceAtCaret)
else:
self.BraceHighlight(braceAtCaret, braceOpposite)
def CanCut(self):
"""Return true if text is selected and can be cut."""
return self.CanCopy()
def CanCopy(self):
"""Return true if text is selected and can be copied."""
return self.GetSelectionStart() != self.GetSelectionEnd()
def CanEdit(self):
"""Return true if editing should succeed."""
return True

View File

@@ -7,8 +7,8 @@ __revision__ = "$Revision$"[11:-2]
from wxPython import wx
import base
import dispatcher
import editwindow
import inspect
import introspect
import keyword
@@ -245,7 +245,7 @@ class FillingTree(wx.wxTreeCtrl):
print text
class FillingText(base.Editor):
class FillingText(editwindow.EditWindow):
"""FillingText based on StyledTextCtrl."""
name = 'PyFilling Text'
@@ -255,7 +255,7 @@ class FillingText(base.Editor):
size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN,
static=False):
"""Create a FillingText instance."""
base.Editor.__init__(self, parent, id, pos, size, style)
editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
# Configure various defaults and user preferences.
self.SetReadOnly(True)
self.SetWrapMode(True)
@@ -269,7 +269,7 @@ class FillingText(base.Editor):
def SetText(self, *args, **kwds):
self.SetReadOnly(False)
base.Editor.SetText(self, *args, **kwds)
editwindow.EditWindow.SetText(self, *args, **kwds)
self.SetReadOnly(True)

View File

@@ -18,9 +18,9 @@ import os
import sys
import time
import base
import buffer
from buffer import Buffer
import dispatcher
import editwindow
import frame
from pseudo import PseudoFileIn
from pseudo import PseudoFileOut
@@ -71,6 +71,23 @@ class ShellFrame(frame.Frame):
self.shell.destroy()
self.Destroy()
def OnAbout(self, event):
"""Display an About window."""
title = 'About PyShell'
text = 'PyShell %s\n\n' % VERSION + \
'Yet another Python shell, only flakier.\n\n' + \
'Half-baked by Patrick K. O\'Brien,\n' + \
'the other half is still in the oven.\n\n' + \
'Shell Revision: %s\n' % self.shell.revision + \
'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
'Python Version: %s\n' % sys.version.split()[0] + \
'wxPython Version: %s\n' % wx.__version__ + \
'Platform: %s\n' % sys.platform
dialog = wx.wxMessageDialog(self, text, title,
wx.wxOK | wx.wxICON_INFORMATION)
dialog.ShowModal()
dialog.Destroy()
class ShellFacade:
"""Simplified interface to all shell-related functionality.
@@ -156,7 +173,7 @@ Ctrl+= Default font size.
return list
class Shell(base.Editor):
class Shell(editwindow.EditWindow):
"""PyCrust Shell based on StyledTextCtrl."""
name = 'PyCrust Shell'
@@ -166,7 +183,7 @@ class Shell(base.Editor):
size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN,
introText='', locals=None, InterpClass=None, *args, **kwds):
"""Create a PyCrust Shell instance."""
base.Editor.__init__(self, parent, id, pos, size, style)
editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
self.wrap()
if locals is None:
locals = {}
@@ -193,8 +210,7 @@ class Shell(base.Editor):
stderr=PseudoFileErr(self.writeErr),
*args, **kwds)
# Set up the buffer.
self.buffer = buffer.Buffer(editor=self, interp=self.interp,
filename=None)
self.buffer = Buffer()
# Find out for which keycodes the interpreter will autocomplete.
self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
# Keep track of the last non-continuation prompt positions.
@@ -231,6 +247,10 @@ class Shell(base.Editor):
def destroy(self):
del self.interp
def setFocus(self):
"""Set focus to the shell."""
self.SetFocus()
def OnIdle(self, event):
"""Free the CPU to do other things."""
if self.waiting:
@@ -878,7 +898,7 @@ Platform: %s""" % \
def CanPaste(self):
"""Return true if a paste should succeed."""
if self.CanEdit() and base.Editor.CanPaste(self):
if self.CanEdit() and editwindow.EditWindow.CanPaste(self):
return True
else:
return False