1 #---------------------------------------------------------------------- 
   2 # Name:        wxPython.lib.editor.Editor 
   3 # Purpose:     An intelligent text editor with colorization capabilities. 
   6 # Authors:      Dirk Holtwic, Robin Dunn 
   9 # Authors:      Adam Feuer, Steve Howell 
  12 #   This code used to support a fairly complex subclass that did 
  13 #   syntax coloring and outliner collapse mode.  Adam and Steve 
  14 #   inherited the code, and added a lot of basic editor 
  15 #   functionality that had not been there before, such as cut-and-paste. 
  18 # Created:     15-Dec-1999 
  20 # Copyright:   (c) 1999 by Dirk Holtwick, 1999 
  21 # Licence:     wxWindows license 
  22 #---------------------------------------------------------------------- 
  23 # 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net) 
  25 # o 2.5 compatability update. 
  27 # 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net) 
  29 # o wxEditor -> Editor 
  40 #---------------------------- 
  42 def ForceBetween(min, val
, max): 
  50 def LineTrimmer(lineOfText
): 
  51     if len(lineOfText
) == 0: 
  53     elif lineOfText
[-1] == '\r': 
  54         return lineOfText
[:-1] 
  58 def LineSplitter(text
): 
  59     return map (LineTrimmer
, text
.split('\n')) 
  62 #---------------------------- 
  65     def __init__(self
, parent
): 
  72     def SetScrollbars(self
, fw
, fh
, w
, h
, x
, y
): 
  73         if (self
.ow 
!= w 
or self
.oh 
!= h 
or self
.ox 
!= x 
or self
.oy 
!= y
): 
  74             self
.parent
.SetScrollbars(fw
, fh
, w
, h
, x
, y
) 
  80 #---------------------------------------------------------------------- 
  82 class Editor(wx
.ScrolledWindow
): 
  84     def __init__(self
, parent
, id, 
  85                  pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
, style
=0): 
  87         wx
.ScrolledWindow
.__init
__(self
, parent
, id, 
  91         self
.isDrawing 
= False 
  98         self
.InitDoubleBuffering() 
 103         self
.SpacesPerTab 
= 4 
 105 ##------------------ Init stuff 
 107     def InitCoords(self
): 
 120         self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
) 
 121         self
.Bind(wx
.EVT_LEFT_UP
, self
.OnLeftUp
) 
 122         self
.Bind(wx
.EVT_MOTION
, self
.OnMotion
) 
 123         self
.Bind(wx
.EVT_SCROLLWIN
, self
.OnScroll
) 
 124         self
.Bind(wx
.EVT_CHAR
, self
.OnChar
) 
 125         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 126         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 127         self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.OnDestroy
) 
 128         self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
) 
 130 ##------------------- Platform-specific stuff 
 132     def NiceFontForPlatform(self
): 
 133         if wx
.Platform 
== "__WXMSW__": 
 134             font 
= wx
.Font(10, wx
.MODERN
, wx
.NORMAL
, wx
.NORMAL
) 
 136             font 
= wx
.Font(12, wx
.MODERN
, wx
.NORMAL
, wx
.NORMAL
, False) 
 137         if wx
.Platform 
== "__WXMAC__": 
 138             font
.SetNoAntiAliasing() 
 141     def UnixKeyHack(self
, key
): 
 143         # this will be obsolete when we get the new wxWindows patch 
 147         # Which patch? I don't know if this is needed, but I don't know 
 148         # why it's here either. Play it safe; leave it in. 
 154 ##-------------------- UpdateView/Cursor code 
 156     def OnSize(self
, event
): 
 157         self
.AdjustScrollbars() 
 160     def SetCharDimensions(self
): 
 161         # TODO: We need a code review on this.  It appears that Linux 
 162         # improperly reports window dimensions when the scrollbar's there. 
 163         self
.bw
, self
.bh 
= self
.GetClientSize() 
 165         if wx
.Platform 
== "__WXMSW__": 
 166             self
.sh 
= self
.bh 
/ self
.fh
 
 167             self
.sw 
= (self
.bw 
/ self
.fw
) - 1 
 169             self
.sh 
= self
.bh 
/ self
.fh
 
 170             if self
.LinesInFile() >= self
.sh
: 
 171                 self
.bw 
= self
.bw 
- wx
.SystemSettings_GetMetric(wx
.SYS_VSCROLL_X
) 
 172                 self
.sw 
= (self
.bw 
/ self
.fw
) - 1 
 174             self
.sw 
= (self
.bw 
/ self
.fw
) - 1 
 175             if self
.CalcMaxLineLen() >= self
.sw
: 
 176                 self
.bh 
= self
.bh 
- wx
.SystemSettings_GetMetric(wx
.SYS_HSCROLL_Y
) 
 177                 self
.sh 
= self
.bh 
/ self
.fh
 
 180     def UpdateView(self
, dc 
= None): 
 182             dc 
= wx
.ClientDC(self
) 
 184             self
.SetCharDimensions() 
 185             self
.KeepCursorOnScreen() 
 186             self
.DrawSimpleCursor(0,0, dc
, True) 
 189     def OnPaint(self
, event
): 
 190         dc 
= wx
.PaintDC(self
) 
 193         self
.isDrawing 
= True 
 195         wx
.CallAfter(self
.AdjustScrollbars
) 
 196         self
.isDrawing 
= False 
 198     def OnEraseBackground(self
, evt
): 
 201 ##-------------------- Drawing code 
 204         dc 
= wx
.ClientDC(self
) 
 205         self
.font 
= self
.NiceFontForPlatform() 
 206         dc
.SetFont(self
.font
) 
 207         self
.fw 
= dc
.GetCharWidth() 
 208         self
.fh 
= dc
.GetCharHeight() 
 211         self
.fgColor 
= wx
.NamedColour('black') 
 212         self
.bgColor 
= wx
.NamedColour('white') 
 213         self
.selectColor 
= wx
.Colour(238, 220, 120)  # r, g, b = emacsOrange 
 215     def InitDoubleBuffering(self
): 
 218     def DrawEditText(self
, t
, x
, y
, dc
): 
 219         dc
.DrawText(t
, x 
* self
.fw
, y 
* self
.fh
) 
 221     def DrawLine(self
, line
, dc
): 
 222         if self
.IsLine(line
): 
 225             dc
.SetTextForeground(self
.fgColor
) 
 226             fragments 
= selection
.Selection( 
 227                 self
.SelectBegin
, self
.SelectEnd
, 
 228                 self
.sx
, self
.sw
, line
, t
) 
 230             for (data
, selected
) in fragments
: 
 232                     dc
.SetTextBackground(self
.selectColor
) 
 233                     if x 
== 0 and len(data
) == 0 and len(fragments
) == 1: 
 236                     dc
.SetTextBackground(self
.bgColor
) 
 237                 self
.DrawEditText(data
, x
, line 
- self
.sy
, dc
) 
 240     def Draw(self
, odc
=None): 
 242             odc 
= wx
.ClientDC(self
) 
 244         bmp 
= wx
.EmptyBitmap(max(1,self
.bw
), max(1,self
.bh
)) 
 245         dc 
= wx
.BufferedDC(odc
, bmp
) 
 247             dc
.SetFont(self
.font
) 
 248             dc
.SetBackgroundMode(wx
.SOLID
) 
 249             dc
.SetTextBackground(self
.bgColor
) 
 250             dc
.SetTextForeground(self
.fgColor
) 
 252             for line 
in range(self
.sy
, self
.sy 
+ self
.sh
): 
 253                 self
.DrawLine(line
, dc
) 
 254             if len(self
.lines
) < self
.sh 
+ self
.sy
: 
 255                 self
.DrawEofMarker(dc
) 
 258 ##------------------ eofMarker stuff 
 260     def LoadImages(self
): 
 261         self
.eofMarker 
= images
.GetBitmap(images
.EofImageData
) 
 263     def DrawEofMarker(self
,dc
): 
 265         y 
= (len(self
.lines
) - self
.sy
) * self
.fh
 
 267         dc
.DrawBitmap(self
.eofMarker
, x
, y
, hasTransparency
) 
 269 ##------------------ cursor-related functions 
 271     def DrawCursor(self
, dc 
= None): 
 273             dc 
= wx
.ClientDC(self
) 
 275         if (self
.LinesInFile())<self
.cy
: #-1 ? 
 276             self
.cy 
= self
.LinesInFile()-1 
 277         s 
= self
.lines
[self
.cy
] 
 279         x 
= self
.cx 
- self
.sx
 
 280         y 
= self
.cy 
- self
.sy
 
 281         self
.DrawSimpleCursor(x
, y
, dc
) 
 284     def DrawSimpleCursor(self
, xp
, yp
, dc 
= None, old
=False): 
 286             dc 
= wx
.ClientDC(self
) 
 296         dc
.Blit(x
,y
, szx
,szy
, dc
, x
,y
, wx
.SRC_INVERT
) 
 300 ##-------- Enforcing screen boundaries, cursor movement 
 302     def CalcMaxLineLen(self
): 
 303         """get length of longest line on screen""" 
 305         for line 
in self
.lines
[self
.sy
:self
.sy
+self
.sh
]: 
 306             if len(line
) >maxlen
: 
 310     def KeepCursorOnScreen(self
): 
 311         self
.sy 
= ForceBetween(max(0, self
.cy
-self
.sh
), self
.sy
, self
.cy
) 
 312         self
.sx 
= ForceBetween(max(0, self
.cx
-self
.sw
), self
.sx
, self
.cx
) 
 313         self
.AdjustScrollbars() 
 315     def HorizBoundaries(self
): 
 316         self
.SetCharDimensions() 
 317         maxLineLen 
= self
.CalcMaxLineLen() 
 318         self
.sx 
= ForceBetween(0, self
.sx
, max(self
.sw
, maxLineLen 
- self
.sw 
+ 1)) 
 319         self
.cx 
= ForceBetween(self
.sx
, self
.cx
, self
.sx 
+ self
.sw 
- 1) 
 321     def VertBoundaries(self
): 
 322         self
.SetCharDimensions() 
 323         self
.sy 
= ForceBetween(0, self
.sy
, max(self
.sh
, self
.LinesInFile() - self
.sh 
+ 1)) 
 324         self
.cy 
= ForceBetween(self
.sy
, self
.cy
, self
.sy 
+ self
.sh 
- 1) 
 326     def cVert(self
, num
): 
 327         self
.cy 
= self
.cy 
+ num
 
 328         self
.cy 
= ForceBetween(0, self
.cy
, self
.LinesInFile() - 1) 
 329         self
.sy 
= ForceBetween(self
.cy 
- self
.sh 
+ 1, self
.sy
, self
.cy
) 
 330         self
.cx 
= min(self
.cx
, self
.CurrentLineLength()) 
 332     def cHoriz(self
, num
): 
 333         self
.cx 
= self
.cx 
+ num
 
 334         self
.cx 
= ForceBetween(0, self
.cx
, self
.CurrentLineLength()) 
 335         self
.sx 
= ForceBetween(self
.cx 
- self
.sw 
+ 1, self
.sx
, self
.cx
) 
 337     def AboveScreen(self
, row
): 
 340     def BelowScreen(self
, row
): 
 341         return row 
>= self
.sy 
+ self
.sh
 
 343     def LeftOfScreen(self
, col
): 
 346     def RightOfScreen(self
, col
): 
 347         return col 
>= self
.sx 
+ self
.sw
 
 349 ##----------------- data structure helper functions 
 354     def SetText(self
, lines
): 
 359         self
.AdjustScrollbars() 
 360         self
.UpdateView(None) 
 362     def IsLine(self
, lineNum
): 
 363         return (0<=lineNum
) and (lineNum
<self
.LinesInFile()) 
 365     def GetTextLine(self
, lineNum
): 
 366         if self
.IsLine(lineNum
): 
 367             return self
.lines
[lineNum
] 
 370     def SetTextLine(self
, lineNum
, text
): 
 371         if self
.IsLine(lineNum
): 
 372             self
.lines
[lineNum
] = text
 
 374     def CurrentLineLength(self
): 
 375         return len(self
.lines
[self
.cy
]) 
 377     def LinesInFile(self
): 
 378         return len(self
.lines
) 
 380     def UnTouchBuffer(self
): 
 381         self
.bufferTouched 
= False 
 383     def BufferWasTouched(self
): 
 384         return self
.bufferTouched
 
 386     def TouchBuffer(self
): 
 387         self
.bufferTouched 
= True 
 390 ##-------------------------- Mouse scroll timing functions 
 392     def InitScrolling(self
): 
 393         # we don't rely on the windows system to scroll for us; we just 
 394         # redraw the screen manually every time 
 395         self
.EnableScrolling(False, False) 
 396         self
.nextScrollTime 
= 0 
 397         self
.SCROLLDELAY 
= 0.050 # seconds 
 398         self
.scrollTimer 
= wx
.Timer(self
) 
 399         self
.scroller 
= Scroller(self
) 
 402        if time
.time() >  self
.nextScrollTime
: 
 403            self
.nextScrollTime 
= time
.time() + self
.SCROLLDELAY
 
 408     def SetScrollTimer(self
): 
 410         self
.scrollTimer
.Start(1000*self
.SCROLLDELAY
/2, oneShot
) 
 411         self
.Bind(wx
.EVT_TIMER
, self
.OnTimer
) 
 413     def OnTimer(self
, event
): 
 414         screenX
, screenY 
= wx
.GetMousePosition() 
 415         x
, y 
= self
.ScreenToClientXY(screenX
, screenY
) 
 420 ##-------------------------- Mouse off screen functions 
 422     def HandleAboveScreen(self
, row
): 
 423         self
.SetScrollTimer() 
 429     def HandleBelowScreen(self
, row
): 
 430         self
.SetScrollTimer() 
 432             row 
= self
.sy 
+ self
.sh
 
 433             row  
= min(row
, self
.LinesInFile() - 1) 
 436     def HandleLeftOfScreen(self
, col
): 
 437         self
.SetScrollTimer() 
 443     def HandleRightOfScreen(self
, col
): 
 444         self
.SetScrollTimer() 
 446             col 
= self
.sx 
+ self
.sw
 
 447             col 
= min(col
, self
.CurrentLineLength()) 
 450 ##------------------------ mousing functions 
 452     def MouseToRow(self
, mouseY
): 
 453         row  
= self
.sy 
+ (mouseY
/ self
.fh
) 
 454         if self
.AboveScreen(row
): 
 455             self
.HandleAboveScreen(row
) 
 456         elif self
.BelowScreen(row
): 
 457             self
.HandleBelowScreen(row
) 
 459             self
.cy  
= min(row
, self
.LinesInFile() - 1) 
 461     def MouseToCol(self
, mouseX
): 
 462         col 
= self
.sx 
+ (mouseX 
/ self
.fw
) 
 463         if self
.LeftOfScreen(col
): 
 464             self
.HandleLeftOfScreen(col
) 
 465         elif self
.RightOfScreen(col
): 
 466             self
.HandleRightOfScreen(col
) 
 468             self
.cx 
= min(col
, self
.CurrentLineLength()) 
 470     def MouseToCursor(self
, event
): 
 471         self
.MouseToRow(event
.GetY()) 
 472         self
.MouseToCol(event
.GetX()) 
 474     def OnMotion(self
, event
): 
 475         if event
.LeftIsDown() and self
.HasCapture(): 
 476             self
.Selecting 
= True 
 477             self
.MouseToCursor(event
) 
 480     def OnLeftDown(self
, event
): 
 481         self
.MouseToCursor(event
) 
 482         self
.SelectBegin 
= (self
.cy
, self
.cx
) 
 483         self
.SelectEnd 
= None 
 488     def OnLeftUp(self
, event
): 
 489         if not self
.HasCapture(): 
 492         if self
.SelectEnd 
is None: 
 495             self
.Selecting 
= False 
 496             self
.SelectNotify(False, self
.SelectBegin
, self
.SelectEnd
) 
 499         self
.scrollTimer
.Stop() 
 502 #------------------------- Scrolling 
 504     def HorizScroll(self
, event
, eventType
): 
 505         maxLineLen 
= self
.CalcMaxLineLen() 
 507         if eventType 
== wx
.EVT_SCROLLWIN_LINEUP
: 
 509         elif eventType 
== wx
.EVT_SCROLLWIN_LINEDOWN
: 
 511         elif eventType 
== wx
.EVT_SCROLLWIN_PAGEUP
: 
 513         elif eventType 
== wx
.EVT_SCROLLWIN_PAGEDOWN
: 
 515         elif eventType 
== wx
.EVT_SCROLLWIN_TOP
: 
 516             self
.sx 
= self
.cx 
= 0 
 517         elif eventType 
== wx
.EVT_SCROLLWIN_BOTTOM
: 
 518             self
.sx 
= maxLineLen 
- self
.sw
 
 521             self
.sx 
= event
.GetPosition() 
 523         self
.HorizBoundaries() 
 525     def VertScroll(self
, event
, eventType
): 
 526         if   eventType 
== wx
.EVT_SCROLLWIN_LINEUP
: 
 528         elif eventType 
== wx
.EVT_SCROLLWIN_LINEDOWN
: 
 530         elif eventType 
== wx
.EVT_SCROLLWIN_PAGEUP
: 
 532         elif eventType 
== wx
.EVT_SCROLLWIN_PAGEDOWN
: 
 534         elif eventType 
== wx
.EVT_SCROLLWIN_TOP
: 
 535             self
.sy 
= self
.cy 
= 0 
 536         elif eventType 
== wx
.EVT_SCROLLWIN_BOTTOM
: 
 537             self
.sy 
= self
.LinesInFile() - self
.sh
 
 538             self
.cy 
= self
.LinesInFile() 
 540             self
.sy 
= event
.GetPosition() 
 542         self
.VertBoundaries() 
 544     def OnScroll(self
, event
): 
 545         dir = event
.GetOrientation() 
 546         eventType 
= event
.GetEventType() 
 547         if dir == wx
.HORIZONTAL
: 
 548             self
.HorizScroll(event
, eventType
) 
 550             self
.VertScroll(event
, eventType
) 
 554     def AdjustScrollbars(self
): 
 556             self
.SetCharDimensions() 
 557             self
.scroller
.SetScrollbars( 
 559                 self
.CalcMaxLineLen()+3, max(self
.LinesInFile()+1, self
.sh
), 
 562 #------------ backspace, delete, return 
 564     def BreakLine(self
, event
): 
 565         if self
.IsLine(self
.cy
): 
 566             t 
= self
.lines
[self
.cy
] 
 567             self
.lines 
= self
.lines
[:self
.cy
] + [t
[:self
.cx
],t
[self
.cx
:]] + self
.lines
[self
.cy
+1:] 
 572     def InsertChar(self
,char
): 
 573         if self
.IsLine(self
.cy
): 
 574             t 
= self
.lines
[self
.cy
] 
 575             t 
= t
[:self
.cx
] + char 
+ t
[self
.cx
:] 
 576             self
.SetTextLine(self
.cy
, t
) 
 581         t1 
= self
.lines
[self
.cy
] 
 582         t2 
= self
.lines
[self
.cy
+1] 
 584         self
.lines 
= self
.lines
[:self
.cy
] + [t1 
+ t2
] + self
.lines
[self
.cy
+2:] 
 588     def DeleteChar(self
,x
,y
,oldtext
): 
 589         newtext 
= oldtext
[:x
] + oldtext
[x
+1:] 
 590         self
.SetTextLine(y
, newtext
) 
 594     def BackSpace(self
, event
): 
 595         t 
= self
.GetTextLine(self
.cy
) 
 597             self
.DeleteChar(self
.cx
-1,self
.cy
,t
) 
 608     def Delete(self
, event
): 
 609         t 
= self
.GetTextLine(self
.cy
) 
 611             self
.DeleteChar(self
.cx
,self
.cy
,t
) 
 614             if self
.cy 
< len(self
.lines
) - 1: 
 618     def Escape(self
, event
): 
 621     def TabKey(self
, event
): 
 622         numSpaces 
= self
.SpacesPerTab 
- (self
.cx 
% self
.SpacesPerTab
) 
 623         self
.SingleLineInsert(' ' * numSpaces
) 
 625 ##----------- selection routines 
 627     def SelectUpdate(self
): 
 628         self
.SelectEnd 
= (self
.cy
, self
.cx
) 
 629         self
.SelectNotify(self
.Selecting
, self
.SelectBegin
, self
.SelectEnd
) 
 632     def NormalizedSelect(self
): 
 633         (begin
, end
) = (self
.SelectBegin
, self
.SelectEnd
) 
 646     def FindSelection(self
): 
 647         if self
.SelectEnd 
is None or self
.SelectBegin 
is None: 
 650         (begin
, end
) =  self
.NormalizedSelect() 
 653         return (bRow
, bCol
, eRow
, eCol
) 
 656         self
.SelectBegin 
= None 
 657         self
.SelectEnd 
= None 
 658         self
.Selecting 
= False 
 659         self
.SelectNotify(False,None,None) 
 661     def CopySelection(self
, event
): 
 662         selection 
= self
.FindSelection() 
 663         if selection 
is None: 
 665         (bRow
, bCol
, eRow
, eCol
) = selection
 
 668             self
.SingleLineCopy(bRow
, bCol
, eCol
) 
 670             self
.MultipleLineCopy(bRow
, bCol
, eRow
, eCol
) 
 672     def OnCopySelection(self
, event
): 
 673         self
.CopySelection(event
) 
 676     def CopyToClipboard(self
, linesOfText
): 
 677         do 
= wx
.TextDataObject() 
 678         do
.SetText(os
.linesep
.join(linesOfText
)) 
 679         wx
.TheClipboard
.Open() 
 680         wx
.TheClipboard
.SetData(do
) 
 681         wx
.TheClipboard
.Close() 
 683     def SingleLineCopy(self
, Row
, bCol
, eCol
): 
 684         Line 
= self
.GetTextLine(Row
) 
 685         self
.CopyToClipboard([Line
[bCol
:eCol
]]) 
 687     def MultipleLineCopy(self
, bRow
, bCol
, eRow
, eCol
): 
 688         bLine 
= self
.GetTextLine(bRow
)[bCol
:] 
 689         eLine 
= self
.GetTextLine(eRow
)[:eCol
] 
 690         self
.CopyToClipboard([bLine
] + [l 
for l 
in self
.lines
[bRow 
+ 1:eRow
]] + [eLine
]) 
 692     def OnDeleteSelection(self
, event
): 
 693         selection 
= self
.FindSelection() 
 694         if selection 
is None: 
 696         (bRow
, bCol
, eRow
, eCol
) = selection
 
 699             self
.SingleLineDelete(bRow
, bCol
, eCol
) 
 701             self
.MultipleLineDelete(bRow
, bCol
, eRow
, eCol
) 
 711     def SingleLineDelete(self
, Row
, bCol
, eCol
): 
 712         ModLine 
= self
.GetTextLine(Row
) 
 713         ModLine 
= ModLine
[:bCol
] + ModLine
[eCol
:] 
 714         self
.SetTextLine(Row
,ModLine
) 
 716     def MultipleLineDelete(self
, bRow
, bCol
, eRow
, eCol
): 
 717         bLine 
= self
.GetTextLine(bRow
) 
 718         eLine 
= self
.GetTextLine(eRow
) 
 719         ModLine 
= bLine
[:bCol
] + eLine
[eCol
:] 
 720         self
.lines
[bRow
:eRow 
+ 1] = [ModLine
] 
 722     def OnPaste(self
, event
): 
 723         do 
= wx
.TextDataObject() 
 724         wx
.TheClipboard
.Open() 
 725         success 
= wx
.TheClipboard
.GetData(do
) 
 726         wx
.TheClipboard
.Close() 
 728             pastedLines 
= LineSplitter(do
.GetText()) 
 732         if len(pastedLines
) == 0: 
 735         elif len(pastedLines
) == 1: 
 736             self
.SingleLineInsert(pastedLines
[0]) 
 738             self
.MultipleLinePaste(pastedLines
) 
 740     def SingleLineInsert(self
, newText
): 
 741         ModLine 
= self
.GetTextLine(self
.cy
) 
 742         ModLine 
= ModLine
[:self
.cx
] + newText 
+ ModLine
[self
.cx
:] 
 743         self
.SetTextLine(self
.cy
, ModLine
) 
 744         self
.cHoriz(len(newText
)) 
 748     def MultipleLinePaste(self
, pastedLines
): 
 749         FirstLine 
= LastLine 
= self
.GetTextLine(self
.cy
) 
 750         FirstLine 
= FirstLine
[:self
.cx
] + pastedLines
[0] 
 751         LastLine 
= pastedLines
[-1] + LastLine
[self
.cx
:] 
 753         NewSlice 
= [FirstLine
] 
 754         NewSlice 
+= [l 
for l 
in pastedLines
[1:-1]] 
 755         NewSlice 
+= [LastLine
] 
 756         self
.lines
[self
.cy
:self
.cy 
+ 1] = NewSlice
 
 758         self
.cy 
= self
.cy 
+ len(pastedLines
)-1 
 759         self
.cx 
= len(pastedLines
[-1]) 
 763     def OnCutSelection(self
,event
): 
 764         self
.CopySelection(event
) 
 765         self
.OnDeleteSelection(event
) 
 767 #-------------- Keyboard movement implementations 
 769     def MoveDown(self
, event
): 
 772     def MoveUp(self
, event
): 
 775     def MoveLeft(self
, event
): 
 781                 self
.cx 
= self
.CurrentLineLength() 
 785     def MoveRight(self
, event
): 
 786         linelen 
= self
.CurrentLineLength() 
 787         if self
.cx 
== linelen
: 
 788             if self
.cy 
== len(self
.lines
) - 1: 
 797     def MovePageDown(self
, event
): 
 800     def MovePageUp(self
, event
): 
 803     def MoveHome(self
, event
): 
 806     def MoveEnd(self
, event
): 
 807         self
.cx 
= self
.CurrentLineLength() 
 809     def MoveStartOfFile(self
, event
): 
 813     def MoveEndOfFile(self
, event
): 
 814         self
.cy 
= len(self
.lines
) - 1 
 815         self
.cx 
= self
.CurrentLineLength() 
 817 #-------------- Key handler mapping tables 
 819     def SetMoveSpecialFuncs(self
, action
): 
 820         action
[wx
.WXK_DOWN
]  = self
.MoveDown
 
 821         action
[wx
.WXK_UP
]    = self
.MoveUp
 
 822         action
[wx
.WXK_LEFT
]  = self
.MoveLeft
 
 823         action
[wx
.WXK_RIGHT
] = self
.MoveRight
 
 824         action
[wx
.WXK_NEXT
]  = self
.MovePageDown
 
 825         action
[wx
.WXK_PRIOR
] = self
.MovePageUp
 
 826         action
[wx
.WXK_HOME
]  = self
.MoveHome
 
 827         action
[wx
.WXK_END
]   = self
.MoveEnd
 
 829     def SetMoveSpecialControlFuncs(self
, action
): 
 830         action
[wx
.WXK_HOME
] = self
.MoveStartOfFile
 
 831         action
[wx
.WXK_END
]  = self
.MoveEndOfFile
 
 833     def SetAltFuncs(self
, action
): 
 834         # subclass implements 
 837     def SetControlFuncs(self
, action
): 
 838         action
['c'] = self
.OnCopySelection
 
 839         action
['d'] = self
.OnDeleteSelection
 
 840         action
['v'] = self
.OnPaste
 
 841         action
['x'] = self
.OnCutSelection
 
 843     def SetSpecialControlFuncs(self
, action
): 
 844         action
[wx
.WXK_INSERT
] = self
.OnCopySelection
 
 846     def SetShiftFuncs(self
, action
): 
 847         action
[wx
.WXK_DELETE
] = self
.OnCutSelection
 
 848         action
[wx
.WXK_INSERT
] = self
.OnPaste
 
 850     def SetSpecialFuncs(self
, action
): 
 851         action
[wx
.WXK_BACK
]   = self
.BackSpace
 
 852         action
[wx
.WXK_DELETE
] = self
.Delete
 
 853         action
[wx
.WXK_RETURN
] = self
.BreakLine
 
 854         action
[wx
.WXK_ESCAPE
] = self
.Escape
 
 855         action
[wx
.WXK_TAB
]    = self
.TabKey
 
 857 ##-------------- Logic for key handlers 
 860     def Move(self
, keySettingFunction
, key
, event
): 
 862         keySettingFunction(action
) 
 864         if not action
.has_key(key
): 
 867         if event
.ShiftDown(): 
 868             if not self
.Selecting
: 
 869                 self
.Selecting 
= True 
 870                 self
.SelectBegin 
= (self
.cy
, self
.cx
) 
 872             self
.SelectEnd 
= (self
.cy
, self
.cx
) 
 876                 self
.Selecting 
= False 
 878         self
.SelectNotify(self
.Selecting
, self
.SelectBegin
, self
.SelectEnd
) 
 882     def MoveSpecialKey(self
, event
, key
): 
 883         return self
.Move(self
.SetMoveSpecialFuncs
, key
, event
) 
 885     def MoveSpecialControlKey(self
, event
, key
): 
 886         if not event
.ControlDown(): 
 888         return self
.Move(self
.SetMoveSpecialControlFuncs
, key
, event
) 
 890     def Dispatch(self
, keySettingFunction
, key
, event
): 
 892         keySettingFunction(action
) 
 893         if action
.has_key(key
): 
 899     def ModifierKey(self
, key
, event
, modifierKeyDown
, MappingFunc
): 
 900         if not modifierKeyDown
: 
 903         key 
= self
.UnixKeyHack(key
) 
 908         if not self
.Dispatch(MappingFunc
, key
, event
): 
 912     def ControlKey(self
, event
, key
): 
 913         return self
.ModifierKey(key
, event
, event
.ControlDown(), self
.SetControlFuncs
) 
 915     def AltKey(self
, event
, key
): 
 916         return self
.ModifierKey(key
, event
, event
.AltDown(), self
.SetAltFuncs
) 
 918     def SpecialControlKey(self
, event
, key
): 
 919         if not event
.ControlDown(): 
 921         if not self
.Dispatch(self
.SetSpecialControlFuncs
, key
, event
): 
 925     def ShiftKey(self
, event
, key
): 
 926         if not event
.ShiftDown(): 
 928         return self
.Dispatch(self
.SetShiftFuncs
, key
, event
) 
 930     def NormalChar(self
, event
, key
): 
 934         if not self
.Dispatch(self
.SetSpecialFuncs
, key
, event
): 
 935             if (key
>31) and (key
<256): 
 936                 self
.InsertChar(chr(key
)) 
 941         self
.AdjustScrollbars() 
 943     def OnChar(self
, event
): 
 944         key 
= event
.KeyCode() 
 945         filters 
= [self
.AltKey
, 
 946                    self
.MoveSpecialControlKey
, 
 948                    self
.SpecialControlKey
, 
 952         for filter in filters
: 
 953             if filter(event
,key
): 
 957 #----------------------- Eliminate memory leaks 
 959     def OnDestroy(self
, event
): 
 965         self
.selectColor 
= None 
 966         self
.scrollTimer 
= None 
 967         self
.eofMarker 
= None 
 969 #--------------------  Abstract methods for subclasses 
 974     def SelectNotify(self
, Selecting
, SelectionBegin
, SelectionEnd
):