]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/lib/editor/editor.py
Moved config.py to the root because if wxPython hasn't been built yet
[wxWidgets.git] / wxPython / wx / lib / editor / editor.py
index 5b9cde871480a382a4917a0b2aa06c1f2b480a69..ac2ec28545104067ac10b6f4f7d58ad6f5072870 100644 (file)
@@ -1,8 +1,975 @@
+#----------------------------------------------------------------------
+# Name:        wxPython.lib.editor.Editor
+# Purpose:     An intelligent text editor with colorization capabilities.
+#
+# Original
+# Authors:      Dirk Holtwic, Robin Dunn
+#
+# New
+# Authors:      Adam Feuer, Steve Howell
+#
+# History:
+#   This code used to support a fairly complex subclass that did
+#   syntax coloring and outliner collapse mode.  Adam and Steve
+#   inherited the code, and added a lot of basic editor
+#   functionality that had not been there before, such as cut-and-paste.
+#
+#
+# Created:     15-Dec-1999
+# RCS-ID:      $Id$
+# Copyright:   (c) 1999 by Dirk Holtwick, 1999
+# Licence:     wxWindows license
+#----------------------------------------------------------------------
+# 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net)
+#
+# o 2.5 compatability update.
+#
+# 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net)
+#
+# o wxEditor -> Editor
+#
 
-"""Renamer stub: provides a way to drop the wx prefix from wxPython objects."""
+import  os
+import  time
+
+import  wx
+
+import  selection
+import  images
+
+#----------------------------
+
+def ForceBetween(min, val, max):
+    if val  > max:
+        return max
+    if val < min:
+        return min
+    return val
+
+
+def LineTrimmer(lineOfText):
+    if len(lineOfText) == 0:
+        return ""
+    elif lineOfText[-1] == '\r':
+        return lineOfText[:-1]
+    else:
+        return lineOfText
+
+def LineSplitter(text):
+    return map (LineTrimmer, text.split('\n'))
+
+
+#----------------------------
+
+class Scroller:
+    def __init__(self, parent):
+        self.parent = parent
+        self.ow = 0
+        self.oh = 0
+        self.ox = 0
+        self.oy = 0
+
+    def SetScrollbars(self, fw, fh, w, h, x, y):
+        if (self.ow != w or self.oh != h or self.ox != x or self.oy != y):
+            self.parent.SetScrollbars(fw, fh, w, h, x, y)
+            self.ow = w
+            self.oh = h
+            self.ox = x
+            self.oy = y
+
+#----------------------------------------------------------------------
+
+class Editor(wx.ScrolledWindow):
+
+    def __init__(self, parent, id,
+                 pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
+
+        wx.ScrolledWindow.__init__(self, parent, id,
+                                  pos, size,
+                                  style|wx.WANTS_CHARS)
+
+        self.isDrawing = False
+        
+        self.InitCoords()
+        self.InitFonts()
+        self.SetColors()
+        self.MapEvents()
+        self.LoadImages()
+        self.InitDoubleBuffering()
+        self.InitScrolling()
+        self.SelectOff()
+        self.SetFocus()
+        self.SetText([""])
+        self.SpacesPerTab = 4
+
+##------------------ Init stuff
+
+    def InitCoords(self):
+        self.cx = 0
+        self.cy = 0
+        self.oldCx = 0
+        self.oldCy = 0
+        self.sx = 0
+        self.sy = 0
+        self.sw = 0
+        self.sh = 0
+        self.sco_x = 0
+        self.sco_y = 0
+
+    def MapEvents(self):
+        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
+        self.Bind(wx.EVT_MOTION, self.OnMotion)
+        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
+        self.Bind(wx.EVT_CHAR, self.OnChar)
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
+        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+
+##------------------- Platform-specific stuff
+
+    def NiceFontForPlatform(self):
+        if wx.Platform == "__WXMSW__":
+            font = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL)
+        else:
+            font = wx.Font(12, wx.MODERN, wx.NORMAL, wx.NORMAL, False)
+        if wx.Platform == "__WXMAC__":
+            font.SetNoAntiAliasing()
+        return font
+
+    def UnixKeyHack(self, key):
+        #
+        # this will be obsolete when we get the new wxWindows patch
+        #
+        # 12/14/03 - jmg
+        #
+        # Which patch? I don't know if this is needed, but I don't know
+        # why it's here either. Play it safe; leave it in.
+        #
+        if key <= 26:
+            key += ord('a') - 1
+        return key
+
+##-------------------- UpdateView/Cursor code
+
+    def OnSize(self, event):
+        self.AdjustScrollbars()
+        self.SetFocus()
+
+    def SetCharDimensions(self):
+        # TODO: We need a code review on this.  It appears that Linux
+        # improperly reports window dimensions when the scrollbar's there.
+        self.bw, self.bh = self.GetClientSize()
+
+        if wx.Platform == "__WXMSW__":
+            self.sh = self.bh / self.fh
+            self.sw = (self.bw / self.fw) - 1
+        else:
+            self.sh = self.bh / self.fh
+            if self.LinesInFile() >= self.sh:
+                self.bw = self.bw - wx.SystemSettings_GetMetric(wx.SYS_VSCROLL_X)
+                self.sw = (self.bw / self.fw) - 1
+
+            self.sw = (self.bw / self.fw) - 1
+            if self.CalcMaxLineLen() >= self.sw:
+                self.bh = self.bh - wx.SystemSettings_GetMetric(wx.SYS_HSCROLL_Y)
+                self.sh = self.bh / self.fh
+
+
+    def UpdateView(self, dc = None):
+        if dc is None:
+            dc = wx.ClientDC(self)
+        if dc.Ok():
+            self.SetCharDimensions()
+            self.KeepCursorOnScreen()
+            self.DrawSimpleCursor(0,0, dc, True)
+            self.Draw(dc)
+
+    def OnPaint(self, event):
+        dc = wx.PaintDC(self)
+        if self.isDrawing:
+            return
+        self.isDrawing = True
+        self.UpdateView(dc)
+        wx.CallAfter(self.AdjustScrollbars)
+        self.isDrawing = False
+
+    def OnEraseBackground(self, evt):
+        pass
+
+##-------------------- Drawing code
+
+    def InitFonts(self):
+        dc = wx.ClientDC(self)
+        self.font = self.NiceFontForPlatform()
+        dc.SetFont(self.font)
+        self.fw = dc.GetCharWidth()
+        self.fh = dc.GetCharHeight()
+
+    def SetColors(self):
+        self.fgColor = wx.NamedColour('black')
+        self.bgColor = wx.NamedColour('white')
+        self.selectColor = wx.Colour(238, 220, 120)  # r, g, b = emacsOrange
+
+    def InitDoubleBuffering(self):
+        pass
+
+    def DrawEditText(self, t, x, y, dc):
+        dc.DrawText(t, (x * self.fw, y * self.fh))
+
+    def DrawLine(self, line, dc):
+        if self.IsLine(line):
+            l   = line
+            t   = self.lines[l]
+            dc.SetTextForeground(self.fgColor)
+            fragments = selection.Selection(
+                self.SelectBegin, self.SelectEnd,
+                self.sx, self.sw, line, t)
+            x = 0
+            for (data, selected) in fragments:
+                if selected:
+                    dc.SetTextBackground(self.selectColor)
+                    if x == 0 and len(data) == 0 and len(fragments) == 1:
+                        data = ' '
+                else:
+                    dc.SetTextBackground(self.bgColor)
+                self.DrawEditText(data, x, line - self.sy, dc)
+                x += len(data)
+
+    def Draw(self, odc=None):
+        if not odc:
+            odc = wx.ClientDC(self)
+
+        bmp = wx.EmptyBitmap(max(1,self.bw), max(1,self.bh))
+        dc = wx.BufferedDC(odc, bmp)
+        if dc.Ok():
+            dc.SetFont(self.font)
+            dc.SetBackgroundMode(wx.SOLID)
+            dc.SetTextBackground(self.bgColor)
+            dc.SetTextForeground(self.fgColor)
+            dc.Clear()
+            for line in range(self.sy, self.sy + self.sh):
+                self.DrawLine(line, dc)
+            if len(self.lines) < self.sh + self.sy:
+                self.DrawEofMarker(dc)
+            self.DrawCursor(dc)
+
+##------------------ eofMarker stuff
+
+    def LoadImages(self):
+        self.eofMarker = images.GetBitmap(images.EofImageData)
+
+    def DrawEofMarker(self,dc):
+        x = 0
+        y = (len(self.lines) - self.sy) * self.fh
+        hasTransparency = 1
+        dc.DrawBitmap(self.eofMarker, (x, y), hasTransparency)
+
+##------------------ cursor-related functions
+
+    def DrawCursor(self, dc = None):
+        if not dc:
+            dc = wx.ClientDC(self)
+
+        if (self.LinesInFile())<self.cy: #-1 ?
+            self.cy = self.LinesInFile()-1
+        s = self.lines[self.cy]
+
+        x = self.cx - self.sx
+        y = self.cy - self.sy
+        self.DrawSimpleCursor(x, y, dc)
+
+
+    def DrawSimpleCursor(self, xp, yp, dc = None, old=False):
+        if not dc:
+            dc = wx.ClientDC(self)
+
+        if old:
+            xp = self.sco_x
+            yp = self.sco_y
+
+        szx = self.fw
+        szy = self.fh
+        x = xp * szx
+        y = yp * szy
+        dc.Blit((x,y), (szx,szy), dc, (x,y), wx.SRC_INVERT)
+        self.sco_x = xp
+        self.sco_y = yp
+
+##-------- Enforcing screen boundaries, cursor movement
+
+    def CalcMaxLineLen(self):
+        """get length of longest line on screen"""
+        maxlen = 0
+        for line in self.lines[self.sy:self.sy+self.sh]:
+            if len(line) >maxlen:
+                maxlen = len(line)
+        return maxlen
+
+    def KeepCursorOnScreen(self):
+        self.sy = ForceBetween(max(0, self.cy-self.sh), self.sy, self.cy)
+        self.sx = ForceBetween(max(0, self.cx-self.sw), self.sx, self.cx)
+        self.AdjustScrollbars()
+
+    def HorizBoundaries(self):
+        self.SetCharDimensions()
+        maxLineLen = self.CalcMaxLineLen()
+        self.sx = ForceBetween(0, self.sx, max(self.sw, maxLineLen - self.sw + 1))
+        self.cx = ForceBetween(self.sx, self.cx, self.sx + self.sw - 1)
+
+    def VertBoundaries(self):
+        self.SetCharDimensions()
+        self.sy = ForceBetween(0, self.sy, max(self.sh, self.LinesInFile() - self.sh + 1))
+        self.cy = ForceBetween(self.sy, self.cy, self.sy + self.sh - 1)
+
+    def cVert(self, num):
+        self.cy = self.cy + num
+        self.cy = ForceBetween(0, self.cy, self.LinesInFile() - 1)
+        self.sy = ForceBetween(self.cy - self.sh + 1, self.sy, self.cy)
+        self.cx = min(self.cx, self.CurrentLineLength())
+
+    def cHoriz(self, num):
+        self.cx = self.cx + num
+        self.cx = ForceBetween(0, self.cx, self.CurrentLineLength())
+        self.sx = ForceBetween(self.cx - self.sw + 1, self.sx, self.cx)
+
+    def AboveScreen(self, row):
+        return row < self.sy
+
+    def BelowScreen(self, row):
+        return row >= self.sy + self.sh
+
+    def LeftOfScreen(self, col):
+        return col < self.sx
+
+    def RightOfScreen(self, col):
+        return col >= self.sx + self.sw
+
+##----------------- data structure helper functions
+
+    def GetText(self):
+        return self.lines
+
+    def SetText(self, lines):
+        self.InitCoords()
+        self.lines = lines
+        self.UnTouchBuffer()
+        self.SelectOff()
+        self.AdjustScrollbars()
+        self.UpdateView(None)
+
+    def IsLine(self, lineNum):
+        return (0<=lineNum) and (lineNum<self.LinesInFile())
+
+    def GetTextLine(self, lineNum):
+        if self.IsLine(lineNum):
+            return self.lines[lineNum]
+        return ""
+
+    def SetTextLine(self, lineNum, text):
+        if self.IsLine(lineNum):
+            self.lines[lineNum] = text
+
+    def CurrentLineLength(self):
+        return len(self.lines[self.cy])
+
+    def LinesInFile(self):
+        return len(self.lines)
+
+    def UnTouchBuffer(self):
+        self.bufferTouched = False
+
+    def BufferWasTouched(self):
+        return self.bufferTouched
+
+    def TouchBuffer(self):
+        self.bufferTouched = True
+
+
+##-------------------------- Mouse scroll timing functions
+
+    def InitScrolling(self):
+        # we don't rely on the windows system to scroll for us; we just
+        # redraw the screen manually every time
+        self.EnableScrolling(False, False)
+        self.nextScrollTime = 0
+        self.SCROLLDELAY = 0.050 # seconds
+        self.scrollTimer = wx.Timer(self)
+        self.scroller = Scroller(self)
+
+    def CanScroll(self):
+       if time.time() >  self.nextScrollTime:
+           self.nextScrollTime = time.time() + self.SCROLLDELAY
+           return True
+       else:
+           return False
+
+    def SetScrollTimer(self):
+        oneShot = True
+        self.scrollTimer.Start(1000*self.SCROLLDELAY/2, oneShot)
+        self.Bind(wx.EVT_TIMER, self.OnTimer)
+
+    def OnTimer(self, event):
+        screenX, screenY = wx.GetMousePosition()
+        x, y = self.ScreenToClientXY(screenX, screenY)
+        self.MouseToRow(y)
+        self.MouseToCol(x)
+        self.SelectUpdate()
+
+##-------------------------- Mouse off screen functions
+
+    def HandleAboveScreen(self, row):
+        self.SetScrollTimer()
+        if self.CanScroll():
+            row = self.sy - 1
+            row = max(0, row)
+            self.cy = row
+
+    def HandleBelowScreen(self, row):
+        self.SetScrollTimer()
+        if self.CanScroll():
+            row = self.sy + self.sh
+            row  = min(row, self.LinesInFile() - 1)
+            self.cy = row
+
+    def HandleLeftOfScreen(self, col):
+        self.SetScrollTimer()
+        if self.CanScroll():
+            col = self.sx - 1
+            col = max(0,col)
+            self.cx = col
+
+    def HandleRightOfScreen(self, col):
+        self.SetScrollTimer()
+        if self.CanScroll():
+            col = self.sx + self.sw
+            col = min(col, self.CurrentLineLength())
+            self.cx = col
+
+##------------------------ mousing functions
+
+    def MouseToRow(self, mouseY):
+        row  = self.sy + (mouseY/ self.fh)
+        if self.AboveScreen(row):
+            self.HandleAboveScreen(row)
+        elif self.BelowScreen(row):
+            self.HandleBelowScreen(row)
+        else:
+            self.cy  = min(row, self.LinesInFile() - 1)
+
+    def MouseToCol(self, mouseX):
+        col = self.sx + (mouseX / self.fw)
+        if self.LeftOfScreen(col):
+            self.HandleLeftOfScreen(col)
+        elif self.RightOfScreen(col):
+            self.HandleRightOfScreen(col)
+        else:
+            self.cx = min(col, self.CurrentLineLength())
+
+    def MouseToCursor(self, event):
+        self.MouseToRow(event.GetY())
+        self.MouseToCol(event.GetX())
+
+    def OnMotion(self, event):
+        if event.LeftIsDown() and self.HasCapture():
+            self.Selecting = True
+            self.MouseToCursor(event)
+            self.SelectUpdate()
+
+    def OnLeftDown(self, event):
+        self.MouseToCursor(event)
+        self.SelectBegin = (self.cy, self.cx)
+        self.SelectEnd = None
+        self.UpdateView()
+        self.CaptureMouse()
+
+    def OnLeftUp(self, event):
+        if not self.HasCapture():
+            return
+
+        if self.SelectEnd is None:
+            self.OnClick()
+        else:
+            self.Selecting = False
+            self.SelectNotify(False, self.SelectBegin, self.SelectEnd)
+
+        self.ReleaseMouse()
+        self.scrollTimer.Stop()
+
+
+#------------------------- Scrolling
+
+    def HorizScroll(self, event, eventType):
+        maxLineLen = self.CalcMaxLineLen()
+
+        if eventType == wx.EVT_SCROLLWIN_LINEUP:
+            self.sx -= 1
+        elif eventType == wx.EVT_SCROLLWIN_LINEDOWN:
+            self.sx += 1
+        elif eventType == wx.EVT_SCROLLWIN_PAGEUP:
+            self.sx -= self.sw
+        elif eventType == wx.EVT_SCROLLWIN_PAGEDOWN:
+            self.sx += self.sw
+        elif eventType == wx.EVT_SCROLLWIN_TOP:
+            self.sx = self.cx = 0
+        elif eventType == wx.EVT_SCROLLWIN_BOTTOM:
+            self.sx = maxLineLen - self.sw
+            self.cx = maxLineLen
+        else:
+            self.sx = event.GetPosition()
+
+        self.HorizBoundaries()
+
+    def VertScroll(self, event, eventType):
+        if   eventType == wx.EVT_SCROLLWIN_LINEUP:
+            self.sy -= 1
+        elif eventType == wx.EVT_SCROLLWIN_LINEDOWN:
+            self.sy += 1
+        elif eventType == wx.EVT_SCROLLWIN_PAGEUP:
+            self.sy -= self.sh
+        elif eventType == wx.EVT_SCROLLWIN_PAGEDOWN:
+            self.sy += self.sh
+        elif eventType == wx.EVT_SCROLLWIN_TOP:
+            self.sy = self.cy = 0
+        elif eventType == wx.EVT_SCROLLWIN_BOTTOM:
+            self.sy = self.LinesInFile() - self.sh
+            self.cy = self.LinesInFile()
+        else:
+            self.sy = event.GetPosition()
+
+        self.VertBoundaries()
+
+    def OnScroll(self, event):
+        dir = event.GetOrientation()
+        eventType = event.GetEventType()
+        if dir == wx.HORIZONTAL:
+            self.HorizScroll(event, eventType)
+        else:
+            self.VertScroll(event, eventType)
+        self.UpdateView()
+
+
+    def AdjustScrollbars(self):
+        for i in range(2):
+            self.SetCharDimensions()
+            self.scroller.SetScrollbars(
+                self.fw, self.fh,
+                self.CalcMaxLineLen()+3, max(self.LinesInFile()+1, self.sh),
+                self.sx, self.sy)
+
+#------------ backspace, delete, return
+
+    def BreakLine(self, event):
+        if self.IsLine(self.cy):
+            t = self.lines[self.cy]
+            self.lines = self.lines[:self.cy] + [t[:self.cx],t[self.cx:]] + self.lines[self.cy+1:]
+            self.cVert(1)
+            self.cx = 0
+            self.TouchBuffer()
+
+    def InsertChar(self,char):
+        if self.IsLine(self.cy):
+            t = self.lines[self.cy]
+            t = t[:self.cx] + char + t[self.cx:]
+            self.SetTextLine(self.cy, t)
+            self.cHoriz(1)
+            self.TouchBuffer()
+
+    def JoinLines(self):
+        t1 = self.lines[self.cy]
+        t2 = self.lines[self.cy+1]
+        self.cx = len(t1)
+        self.lines = self.lines[:self.cy] + [t1 + t2] + self.lines[self.cy+2:]
+        self.TouchBuffer()
+
+
+    def DeleteChar(self,x,y,oldtext):
+        newtext = oldtext[:x] + oldtext[x+1:]
+        self.SetTextLine(y, newtext)
+        self.TouchBuffer()
+
+
+    def BackSpace(self, event):
+        t = self.GetTextLine(self.cy)
+        if self.cx>0:
+            self.DeleteChar(self.cx-1,self.cy,t)
+            self.cHoriz(-1)
+            self.TouchBuffer()
+        elif self.cx == 0:
+            if self.cy > 0:
+                self.cy -= 1
+                self.JoinLines()
+                self.TouchBuffer()
+            else:
+                wx.Bell()
+
+    def Delete(self, event):
+        t = self.GetTextLine(self.cy)
+        if self.cx<len(t):
+            self.DeleteChar(self.cx,self.cy,t)
+            self.TouchBuffer()
+        else:
+            if self.cy < len(self.lines) - 1:
+                self.JoinLines()
+                self.TouchBuffer()
+
+    def Escape(self, event):
+        self.SelectOff()
+
+    def TabKey(self, event):
+        numSpaces = self.SpacesPerTab - (self.cx % self.SpacesPerTab)
+        self.SingleLineInsert(' ' * numSpaces)
+
+##----------- selection routines
+
+    def SelectUpdate(self):
+        self.SelectEnd = (self.cy, self.cx)
+        self.SelectNotify(self.Selecting, self.SelectBegin, self.SelectEnd)
+        self.UpdateView()
+
+    def NormalizedSelect(self):
+        (begin, end) = (self.SelectBegin, self.SelectEnd)
+        (bRow, bCol) = begin
+        (eRow, eCol) = end
+        if (bRow < eRow):
+            return (begin, end)
+        elif (eRow < bRow):
+            return (end, begin)
+        else:
+            if (bCol < eCol):
+                return (begin, end)
+            else:
+                return (end, begin)
+
+    def FindSelection(self):
+        if self.SelectEnd is None or self.SelectBegin is None:
+            wx.Bell()
+            return None
+        (begin, end) =  self.NormalizedSelect()
+        (bRow, bCol) = begin
+        (eRow, eCol) = end
+        return (bRow, bCol, eRow, eCol)
+
+    def SelectOff(self):
+        self.SelectBegin = None
+        self.SelectEnd = None
+        self.Selecting = False
+        self.SelectNotify(False,None,None)
+
+    def CopySelection(self, event):
+        selection = self.FindSelection()
+        if selection is None:
+            return
+        (bRow, bCol, eRow, eCol) = selection
+
+        if bRow == eRow:
+            self.SingleLineCopy(bRow, bCol, eCol)
+        else:
+            self.MultipleLineCopy(bRow, bCol, eRow, eCol)
+
+    def OnCopySelection(self, event):
+        self.CopySelection(event)
+        self.SelectOff()
+
+    def CopyToClipboard(self, linesOfText):
+        do = wx.TextDataObject()
+        do.SetText(os.linesep.join(linesOfText))
+        wx.TheClipboard.Open()
+        wx.TheClipboard.SetData(do)
+        wx.TheClipboard.Close()
+
+    def SingleLineCopy(self, Row, bCol, eCol):
+        Line = self.GetTextLine(Row)
+        self.CopyToClipboard([Line[bCol:eCol]])
+
+    def MultipleLineCopy(self, bRow, bCol, eRow, eCol):
+        bLine = self.GetTextLine(bRow)[bCol:]
+        eLine = self.GetTextLine(eRow)[:eCol]
+        self.CopyToClipboard([bLine] + [l for l in self.lines[bRow + 1:eRow]] + [eLine])
+
+    def OnDeleteSelection(self, event):
+        selection = self.FindSelection()
+        if selection is None:
+            return
+        (bRow, bCol, eRow, eCol) = selection
+
+        if bRow == eRow:
+            self.SingleLineDelete(bRow, bCol, eCol)
+        else:
+            self.MultipleLineDelete(bRow, bCol, eRow, eCol)
+
+        self.TouchBuffer()
+
+        self.cy = bRow
+        self.cx = bCol
+        self.SelectOff()
+        self.UpdateView()
+
+
+    def SingleLineDelete(self, Row, bCol, eCol):
+        ModLine = self.GetTextLine(Row)
+        ModLine = ModLine[:bCol] + ModLine[eCol:]
+        self.SetTextLine(Row,ModLine)
+
+    def MultipleLineDelete(self, bRow, bCol, eRow, eCol):
+        bLine = self.GetTextLine(bRow)
+        eLine = self.GetTextLine(eRow)
+        ModLine = bLine[:bCol] + eLine[eCol:]
+        self.lines[bRow:eRow + 1] = [ModLine]
+
+    def OnPaste(self, event):
+        do = wx.TextDataObject()
+        wx.TheClipboard.Open()
+        success = wx.TheClipboard.GetData(do)
+        wx.TheClipboard.Close()
+        if success:
+            pastedLines = LineSplitter(do.GetText())
+        else:
+            wx.Bell()
+            return
+        if len(pastedLines) == 0:
+            wx.Bell()
+            return
+        elif len(pastedLines) == 1:
+            self.SingleLineInsert(pastedLines[0])
+        else:
+            self.MultipleLinePaste(pastedLines)
+
+    def SingleLineInsert(self, newText):
+        ModLine = self.GetTextLine(self.cy)
+        ModLine = ModLine[:self.cx] + newText + ModLine[self.cx:]
+        self.SetTextLine(self.cy, ModLine)
+        self.cHoriz(len(newText))
+        self.TouchBuffer()
+        self.UpdateView()
+
+    def MultipleLinePaste(self, pastedLines):
+        FirstLine = LastLine = self.GetTextLine(self.cy)
+        FirstLine = FirstLine[:self.cx] + pastedLines[0]
+        LastLine = pastedLines[-1] + LastLine[self.cx:]
+
+        NewSlice = [FirstLine]
+        NewSlice += [l for l in pastedLines[1:-1]]
+        NewSlice += [LastLine]
+        self.lines[self.cy:self.cy + 1] = NewSlice
+
+        self.cy = self.cy + len(pastedLines)-1
+        self.cx = len(pastedLines[-1])
+        self.TouchBuffer()
+        self.UpdateView()
+
+    def OnCutSelection(self,event):
+        self.CopySelection(event)
+        self.OnDeleteSelection(event)
+
+#-------------- Keyboard movement implementations
+
+    def MoveDown(self, event):
+        self.cVert(+1)
+
+    def MoveUp(self, event):
+        self.cVert(-1)
+
+    def MoveLeft(self, event):
+        if self.cx == 0:
+            if self.cy == 0:
+                wx.Bell()
+            else:
+                self.cVert(-1)
+                self.cx = self.CurrentLineLength()
+        else:
+            self.cx -= 1
+
+    def MoveRight(self, event):
+        linelen = self.CurrentLineLength()
+        if self.cx == linelen:
+            if self.cy == len(self.lines) - 1:
+                wx.Bell()
+            else:
+                self.cx = 0
+                self.cVert(1)
+        else:
+            self.cx += 1
+
+
+    def MovePageDown(self, event):
+        self.cVert(self.sh)
+
+    def MovePageUp(self, event):
+        self.cVert(-self.sh)
+
+    def MoveHome(self, event):
+        self.cx = 0
+
+    def MoveEnd(self, event):
+        self.cx = self.CurrentLineLength()
+
+    def MoveStartOfFile(self, event):
+        self.cy = 0
+        self.cx = 0
+
+    def MoveEndOfFile(self, event):
+        self.cy = len(self.lines) - 1
+        self.cx = self.CurrentLineLength()
+
+#-------------- Key handler mapping tables
+
+    def SetMoveSpecialFuncs(self, action):
+        action[wx.WXK_DOWN]  = self.MoveDown
+        action[wx.WXK_UP]    = self.MoveUp
+        action[wx.WXK_LEFT]  = self.MoveLeft
+        action[wx.WXK_RIGHT] = self.MoveRight
+        action[wx.WXK_NEXT]  = self.MovePageDown
+        action[wx.WXK_PRIOR] = self.MovePageUp
+        action[wx.WXK_HOME]  = self.MoveHome
+        action[wx.WXK_END]   = self.MoveEnd
+
+    def SetMoveSpecialControlFuncs(self, action):
+        action[wx.WXK_HOME] = self.MoveStartOfFile
+        action[wx.WXK_END]  = self.MoveEndOfFile
+
+    def SetAltFuncs(self, action):
+        # subclass implements
+        pass
+
+    def SetControlFuncs(self, action):
+        action['c'] = self.OnCopySelection
+        action['d'] = self.OnDeleteSelection
+        action['v'] = self.OnPaste
+        action['x'] = self.OnCutSelection
+
+    def SetSpecialControlFuncs(self, action):
+        action[wx.WXK_INSERT] = self.OnCopySelection
+
+    def SetShiftFuncs(self, action):
+        action[wx.WXK_DELETE] = self.OnCutSelection
+        action[wx.WXK_INSERT] = self.OnPaste
+
+    def SetSpecialFuncs(self, action):
+        action[wx.WXK_BACK]   = self.BackSpace
+        action[wx.WXK_DELETE] = self.Delete
+        action[wx.WXK_RETURN] = self.BreakLine
+        action[wx.WXK_ESCAPE] = self.Escape
+        action[wx.WXK_TAB]    = self.TabKey
+
+##-------------- Logic for key handlers
+
+
+    def Move(self, keySettingFunction, key, event):
+        action = {}
+        keySettingFunction(action)
+
+        if not action.has_key(key):
+            return False
+
+        if event.ShiftDown():
+            if not self.Selecting:
+                self.Selecting = True
+                self.SelectBegin = (self.cy, self.cx)
+            action[key](event)
+            self.SelectEnd = (self.cy, self.cx)
+        else:
+            action[key](event)
+            if self.Selecting:
+                self.Selecting = False
+
+        self.SelectNotify(self.Selecting, self.SelectBegin, self.SelectEnd)
+        self.UpdateView()
+        return True
+
+    def MoveSpecialKey(self, event, key):
+        return self.Move(self.SetMoveSpecialFuncs, key, event)
+
+    def MoveSpecialControlKey(self, event, key):
+        if not event.ControlDown():
+            return False
+        return self.Move(self.SetMoveSpecialControlFuncs, key, event)
+
+    def Dispatch(self, keySettingFunction, key, event):
+        action = {}
+        keySettingFunction(action)
+        if action.has_key(key):
+            action[key](event)
+            self.UpdateView()
+            return True
+        return False
+
+    def ModifierKey(self, key, event, modifierKeyDown, MappingFunc):
+        if not modifierKeyDown:
+            return False
+
+        key = self.UnixKeyHack(key)
+        try:
+            key = chr(key)
+        except:
+            return False
+        if not self.Dispatch(MappingFunc, key, event):
+            wx.Bell()
+        return True
+
+    def ControlKey(self, event, key):
+        return self.ModifierKey(key, event, event.ControlDown(), self.SetControlFuncs)
+
+    def AltKey(self, event, key):
+        return self.ModifierKey(key, event, event.AltDown(), self.SetAltFuncs)
+
+    def SpecialControlKey(self, event, key):
+        if not event.ControlDown():
+            return False
+        if not self.Dispatch(self.SetSpecialControlFuncs, key, event):
+            wx.Bell()
+        return True
+
+    def ShiftKey(self, event, key):
+        if not event.ShiftDown():
+            return False
+        return self.Dispatch(self.SetShiftFuncs, key, event)
+
+    def NormalChar(self, event, key):
+        self.SelectOff()
+
+        # regular ascii
+        if not self.Dispatch(self.SetSpecialFuncs, key, event):
+            if (key>31) and (key<256):
+                self.InsertChar(chr(key))
+            else:
+                wx.Bell()
+                return
+        self.UpdateView()
+        self.AdjustScrollbars()
+
+    def OnChar(self, event):
+        key = event.KeyCode()
+        filters = [self.AltKey,
+                   self.MoveSpecialControlKey,
+                   self.ControlKey,
+                   self.SpecialControlKey,
+                   self.MoveSpecialKey,
+                   self.ShiftKey,
+                   self.NormalChar]
+        for filter in filters:
+            if filter(event,key):
+                break
+        return 0
+
+#----------------------- Eliminate memory leaks
+
+    def OnDestroy(self, event):
+        self.mdc = None
+        self.odc = None
+        self.bgColor = None
+        self.fgColor = None
+        self.font = None
+        self.selectColor = None
+        self.scrollTimer = None
+        self.eofMarker = None
+
+#--------------------  Abstract methods for subclasses
+
+    def OnClick(self):
+        pass
+
+    def SelectNotify(self, Selecting, SelectionBegin, SelectionEnd):
+        pass
 
-from wx import _rename
-from wxPython.lib.editor import editor
-_rename(globals(), editor.__dict__, modulename='lib.editor.editor')
-del editor
-del _rename