]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/editor/editor.py
3f7c57ae889517c2d2950656d24424ab0ec25b21
[wxWidgets.git] / wxPython / wxPython / lib / editor / editor.py
1 #----------------------------------------------------------------------
2 # Name: wxPython.lib.editor.wxEditor
3 # Purpose: An intelligent text editor with colorization capabilities.
4 #
5 # Original
6 # Authors: Dirk Holtwic, Robin Dunn
7 #
8 # New
9 # Authors: Adam Feuer, Steve Howell
10 #
11 # History:
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.
16 #
17 #
18 # Created: 15-Dec-1999
19 # RCS-ID: $Id$
20 # Copyright: (c) 1999 by Dirk Holtwick, 1999
21 # Licence: wxWindows license
22 #----------------------------------------------------------------------
23
24 import os, time
25
26 from wxPython.wx import *
27 from string import *
28
29 import selection
30 import images
31
32 #----------------------------
33
34 def ForceBetween(min, val, max):
35 if val > max:
36 return max
37 if val < min:
38 return min
39 return val
40
41
42 def LineTrimmer(lineOfText):
43 if len(lineOfText) == 0:
44 return ""
45 elif lineOfText[-1] == '\r':
46 return lineOfText[:-1]
47 else:
48 return lineOfText
49
50 def LineSplitter(text):
51 return map (LineTrimmer, text.split('\n'))
52
53
54 #----------------------------
55
56 class Scroller:
57 def __init__(self, parent):
58 self.parent = parent
59 self.ow = 0
60 self.oh = 0
61 self.ox = 0
62 self.oy = 0
63
64 def SetScrollbars(self, fw, fh, w, h, x, y):
65 if (self.ow != w or self.oh != h or self.ox != x or self.oy != y):
66 self.parent.SetScrollbars(fw, fh, w, h, x, y)
67 self.ow = w
68 self.oh = h
69 self.ox = x
70 self.oy = y
71
72 #----------------------------------------------------------------------
73
74 class wxEditor(wxScrolledWindow):
75
76 def __init__(self, parent, id,
77 pos=wxDefaultPosition, size=wxDefaultSize, style=0):
78
79 wxScrolledWindow.__init__(self, parent, id,
80 pos, size,
81 style|wxWANTS_CHARS)
82
83 self.InitCoords()
84 self.InitFonts()
85 self.SetColors()
86 self.MapEvents()
87 self.LoadImages()
88 self.InitDoubleBuffering()
89 self.InitScrolling()
90 self.SelectOff()
91 self.SetFocus()
92 self.SetText([""])
93 self.SpacesPerTab = 4
94
95 ##------------------ Init stuff
96
97 def InitCoords(self):
98 self.cx = 0
99 self.cy = 0
100 self.oldCx = 0
101 self.oldCy = 0
102 self.sx = 0
103 self.sy = 0
104 self.sw = 0
105 self.sh = 0
106 self.sco_x = 0
107 self.sco_y = 0
108
109 def MapEvents(self):
110 EVT_LEFT_DOWN(self, self.OnLeftDown)
111 EVT_LEFT_UP(self, self.OnLeftUp)
112 EVT_MOTION(self, self.OnMotion)
113 EVT_SCROLLWIN(self, self.OnScroll)
114 EVT_CHAR(self, self.OnChar)
115 EVT_PAINT(self, self.OnPaint)
116 EVT_SIZE(self, self.OnSize)
117 EVT_WINDOW_DESTROY(self, self.OnDestroy)
118 EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
119
120 ##------------------- Platform-specific stuff
121
122 def NiceFontForPlatform(self):
123 if wxPlatform == "__WXMSW__":
124 return wxFont(10, wxMODERN, wxNORMAL, wxNORMAL)
125 else:
126 return wxFont(12, wxMODERN, wxNORMAL, wxNORMAL, false)
127
128 def UnixKeyHack(self, key):
129 # this will be obsolete when we get the new wxWindows patch
130 if key <= 26:
131 key += ord('a') - 1
132 return key
133
134 ##-------------------- UpdateView/Cursor code
135
136 def OnSize(self, event):
137 self.AdjustScrollbars()
138 self.SetFocus()
139
140 def SetCharDimensions(self):
141 # TODO: We need a code review on this. It appears that Linux
142 # improperly reports window dimensions when the scrollbar's there.
143 self.bw, self.bh = self.GetClientSizeTuple()
144
145 if wxPlatform == "__WXMSW__":
146 self.sh = self.bh / self.fh
147 self.sw = (self.bw / self.fw) - 1
148 else:
149 self.sh = self.bh / self.fh
150 if self.LinesInFile() >= self.sh:
151 self.bw = self.bw - wxSystemSettings_GetSystemMetric(wxSYS_VSCROLL_X)
152 self.sw = (self.bw / self.fw) - 1
153
154 self.sw = (self.bw / self.fw) - 1
155 if self.CalcMaxLineLen() >= self.sw:
156 self.bh = self.bh - wxSystemSettings_GetSystemMetric(wxSYS_HSCROLL_Y)
157 self.sh = self.bh / self.fh
158
159
160 def UpdateView(self, dc = None):
161 if not dc:
162 dc = wxClientDC(self)
163 self.SetCharDimensions()
164 self.KeepCursorOnScreen()
165 self.DrawSimpleCursor(0,0,dc, true)
166 self.Draw(dc)
167
168 def OnPaint(self, event):
169 dc = wxPaintDC(self)
170 self.UpdateView(dc)
171 self.AdjustScrollbars()
172
173 def OnEraseBackground(self, evt):
174 pass
175
176 ##-------------------- Drawing code
177
178 def InitFonts(self):
179 dc = wxClientDC(self)
180 self.font = self.NiceFontForPlatform()
181 dc.SetFont(self.font)
182 self.fw = dc.GetCharWidth()
183 self.fh = dc.GetCharHeight()
184
185 def SetColors(self):
186 self.fgColor = wxNamedColour('black')
187 self.bgColor = wxNamedColour('white')
188 self.selectColor = wxColour(238, 220, 120) # r, g, b = emacsOrange
189
190 def InitDoubleBuffering(self):
191 bw,bh = self.GetClientSizeTuple()
192 self.mdc = wxMemoryDC()
193 self.mdc.SelectObject(wxEmptyBitmap(bw,bh))
194
195 def DrawEditText(self, t, x, y, dc):
196 dc.DrawText(t, x * self.fw, y * self.fh)
197
198 def DrawLine(self, line, dc):
199 if self.IsLine(line):
200 l = line
201 t = self.lines[l]
202 dc.SetTextForeground(self.fgColor)
203 fragments = selection.Selection(
204 self.SelectBegin, self.SelectEnd,
205 self.sx, self.sw, line, t)
206 x = 0
207 for (data, selected) in fragments:
208 if selected:
209 dc.SetTextBackground(self.selectColor)
210 if x == 0 and len(data) == 0 and len(fragments) == 1:
211 data = ' '
212 else:
213 dc.SetTextBackground(self.bgColor)
214 self.DrawEditText(data, x, line - self.sy, dc)
215 x += len(data)
216
217 def Draw(self, odc=None):
218 if not odc:
219 odc = wxClientDC(self)
220
221 dc = self.mdc
222 dc.SetFont(self.font)
223 dc.SelectObject(wxEmptyBitmap(self.bw,self.bh))
224 dc.SetBackgroundMode(wxSOLID)
225 dc.SetTextBackground(self.bgColor)
226 dc.SetTextForeground(self.fgColor)
227 dc.Clear()
228 for line in range(self.sy, self.sy + self.sh):
229 self.DrawLine(line, dc)
230 if len(self.lines) < self.sh + self.sy:
231 self.DrawEofMarker(dc)
232 odc.Blit(0,0,self.bw,self.bh,dc,0,0,wxCOPY)
233 self.DrawCursor(odc)
234
235 ##------------------ eofMarker stuff
236
237 def LoadImages(self):
238 self.eofMarker = images.GetBitmap(images.EofImageData)
239
240 def DrawEofMarker(self,dc):
241 x = 0
242 y = (len(self.lines) - self.sy) * self.fh
243 hasTransparency = 1
244 dc.DrawBitmap(self.eofMarker, x, y, hasTransparency)
245
246 ##------------------ cursor-related functions
247
248 def DrawCursor(self, dc = None):
249 if not dc:
250 dc = wxClientDC(self)
251
252 if (self.LinesInFile())<self.cy: #-1 ?
253 self.cy = self.LinesInFile()-1
254 s = self.lines[self.cy]
255
256 x = self.cx - self.sx
257 y = self.cy - self.sy
258 self.DrawSimpleCursor(x, y, dc)
259
260
261 def DrawSimpleCursor(self, xp, yp, dc = None, old=false):
262 if not dc:
263 dc = wxClientDC(self)
264
265 if old:
266 xp = self.sco_x
267 yp = self.sco_y
268
269 szx = self.fw
270 szy = self.fh
271 x = xp * szx
272 y = yp * szy
273 dc.Blit(x,y,szx,szy,dc,x,y,wxSRC_INVERT)
274 self.sco_x = xp
275 self.sco_y = yp
276
277 ##-------- Enforcing screen boundaries, cursor movement
278
279 def CalcMaxLineLen(self):
280 """get length of longest line on screen"""
281 maxlen = 0
282 for line in self.lines[self.sy:self.sy+self.sh]:
283 if len(line) >maxlen:
284 maxlen = len(line)
285 return maxlen
286
287 def KeepCursorOnScreen(self):
288 self.sy = ForceBetween(max(0, self.cy-self.sh), self.sy, self.cy)
289 self.sx = ForceBetween(max(0, self.cx-self.sw), self.sx, self.cx)
290 self.AdjustScrollbars()
291
292 def HorizBoundaries(self):
293 self.SetCharDimensions()
294 maxLineLen = self.CalcMaxLineLen()
295 self.sx = ForceBetween(0, self.sx, max(self.sw, maxLineLen - self.sw + 1))
296 self.cx = ForceBetween(self.sx, self.cx, self.sx + self.sw - 1)
297
298 def VertBoundaries(self):
299 self.SetCharDimensions()
300 self.sy = ForceBetween(0, self.sy, max(self.sh, self.LinesInFile() - self.sh + 1))
301 self.cy = ForceBetween(self.sy, self.cy, self.sy + self.sh - 1)
302
303 def cVert(self, num):
304 self.cy = self.cy + num
305 self.cy = ForceBetween(0, self.cy, self.LinesInFile() - 1)
306 self.sy = ForceBetween(self.cy - self.sh + 1, self.sy, self.cy)
307 self.cx = min(self.cx, self.CurrentLineLength())
308
309 def cHoriz(self, num):
310 self.cx = self.cx + num
311 self.cx = ForceBetween(0, self.cx, self.CurrentLineLength())
312 self.sx = ForceBetween(self.cx - self.sw + 1, self.sx, self.cx)
313
314 def AboveScreen(self, row):
315 return row < self.sy
316
317 def BelowScreen(self, row):
318 return row >= self.sy + self.sh
319
320 def LeftOfScreen(self, col):
321 return col < self.sx
322
323 def RightOfScreen(self, col):
324 return col >= self.sx + self.sw
325
326 ##----------------- data structure helper functions
327
328 def GetText(self):
329 return self.lines
330
331 def SetText(self, lines):
332 self.InitCoords()
333 self.lines = lines
334 self.UnTouchBuffer()
335 self.SelectOff()
336 self.AdjustScrollbars()
337 self.UpdateView(None)
338
339 def IsLine(self, lineNum):
340 return (0<=lineNum) and (lineNum<self.LinesInFile())
341
342 def GetTextLine(self, lineNum):
343 if self.IsLine(lineNum):
344 return self.lines[lineNum]
345 return ""
346
347 def SetTextLine(self, lineNum, text):
348 if self.IsLine(lineNum):
349 self.lines[lineNum] = text
350
351 def CurrentLineLength(self):
352 return len(self.lines[self.cy])
353
354 def LinesInFile(self):
355 return len(self.lines)
356
357 def UnTouchBuffer(self):
358 self.bufferTouched = FALSE
359
360 def BufferWasTouched(self):
361 return self.bufferTouched
362
363 def TouchBuffer(self):
364 self.bufferTouched = TRUE
365
366
367 ##-------------------------- Mouse scroll timing functions
368
369 def InitScrolling(self):
370 # we don't rely on the windows system to scroll for us; we just
371 # redraw the screen manually every time
372 self.EnableScrolling(FALSE, FALSE)
373 self.nextScrollTime = 0
374 self.SCROLLDELAY = 0.050 # seconds
375 self.scrollTimer = wxTimer(self)
376 self.scroller = Scroller(self)
377
378 def CanScroll(self):
379 if time.time() > self.nextScrollTime:
380 self.nextScrollTime = time.time() + self.SCROLLDELAY
381 return true
382 else:
383 return false
384
385 def SetScrollTimer(self):
386 oneShot = true
387 self.scrollTimer.Start(1000*self.SCROLLDELAY/2, oneShot)
388 EVT_TIMER(self, -1, self.OnTimer)
389
390 def OnTimer(self, event):
391 screenX, screenY = wxGetMousePosition()
392 x, y = self.ScreenToClientXY(screenX, screenY)
393 self.MouseToRow(y)
394 self.MouseToCol(x)
395 self.SelectUpdate()
396
397 ##-------------------------- Mouse off screen functions
398
399 def HandleAboveScreen(self, row):
400 self.SetScrollTimer()
401 if self.CanScroll():
402 row = self.sy - 1
403 row = max(0, row)
404 self.cy = row
405
406 def HandleBelowScreen(self, row):
407 self.SetScrollTimer()
408 if self.CanScroll():
409 row = self.sy + self.sh
410 row = min(row, self.LinesInFile() - 1)
411 self.cy = row
412
413 def HandleLeftOfScreen(self, col):
414 self.SetScrollTimer()
415 if self.CanScroll():
416 col = self.sx - 1
417 col = max(0,col)
418 self.cx = col
419
420 def HandleRightOfScreen(self, col):
421 self.SetScrollTimer()
422 if self.CanScroll():
423 col = self.sx + self.sw
424 col = min(col, self.CurrentLineLength())
425 self.cx = col
426
427 ##------------------------ mousing functions
428
429 def MouseToRow(self, mouseY):
430 row = self.sy + (mouseY/ self.fh)
431 if self.AboveScreen(row):
432 self.HandleAboveScreen(row)
433 elif self.BelowScreen(row):
434 self.HandleBelowScreen(row)
435 else:
436 self.cy = min(row, self.LinesInFile() - 1)
437
438 def MouseToCol(self, mouseX):
439 col = self.sx + (mouseX / self.fw)
440 if self.LeftOfScreen(col):
441 self.HandleLeftOfScreen(col)
442 elif self.RightOfScreen(col):
443 self.HandleRightOfScreen(col)
444 else:
445 self.cx = min(col, self.CurrentLineLength())
446
447 def MouseToCursor(self, event):
448 self.MouseToRow(event.GetY())
449 self.MouseToCol(event.GetX())
450
451 def OnMotion(self, event):
452 if event.LeftIsDown() and self.HasCapture():
453 self.Selecting = true
454 self.MouseToCursor(event)
455 self.SelectUpdate()
456
457 def OnLeftDown(self, event):
458 self.MouseToCursor(event)
459 self.SelectBegin = (self.cy, self.cx)
460 self.SelectEnd = None
461 self.UpdateView()
462 self.CaptureMouse()
463
464 def OnLeftUp(self, event):
465 if not self.HasCapture():
466 return
467
468 if self.SelectEnd is None:
469 self.OnClick()
470 else:
471 self.Selecting = false
472 self.SelectNotify(false, self.SelectBegin, self.SelectEnd)
473
474 self.ReleaseMouse()
475 self.scrollTimer.Stop()
476
477
478 #------------------------- Scrolling
479
480 def HorizScroll(self, event, eventType):
481 maxLineLen = self.CalcMaxLineLen()
482
483 if eventType == wxEVT_SCROLLWIN_LINEUP:
484 self.sx -= 1
485 elif eventType == wxEVT_SCROLLWIN_LINEDOWN:
486 self.sx += 1
487 elif eventType == wxEVT_SCROLLWIN_PAGEUP:
488 self.sx -= self.sw
489 elif eventType == wxEVT_SCROLLWIN_PAGEDOWN:
490 self.sx += self.sw
491 elif eventType == wxEVT_SCROLLWIN_TOP:
492 self.sx = self.cx = 0
493 elif eventType == wxEVT_SCROLLWIN_BOTTOM:
494 self.sx = maxLineLen - self.sw
495 self.cx = maxLineLen
496 else:
497 self.sx = event.GetPosition()
498
499 self.HorizBoundaries()
500
501 def VertScroll(self, event, eventType):
502 if eventType == wxEVT_SCROLLWIN_LINEUP:
503 self.sy -= 1
504 elif eventType == wxEVT_SCROLLWIN_LINEDOWN:
505 self.sy += 1
506 elif eventType == wxEVT_SCROLLWIN_PAGEUP:
507 self.sy -= self.sh
508 elif eventType == wxEVT_SCROLLWIN_PAGEDOWN:
509 self.sy += self.sh
510 elif eventType == wxEVT_SCROLLWIN_TOP:
511 self.sy = self.cy = 0
512 elif eventType == wxEVT_SCROLLWIN_BOTTOM:
513 self.sy = self.LinesInFile() - self.sh
514 self.cy = self.LinesInFile()
515 else:
516 self.sy = event.GetPosition()
517
518 self.VertBoundaries()
519
520 def OnScroll(self, event):
521 dir = event.GetOrientation()
522 eventType = event.GetEventType()
523 if dir == wxHORIZONTAL:
524 self.HorizScroll(event, eventType)
525 else:
526 self.VertScroll(event, eventType)
527 self.UpdateView()
528
529
530 def AdjustScrollbars(self):
531 for i in range(2):
532 self.SetCharDimensions()
533 self.scroller.SetScrollbars(
534 self.fw, self.fh,
535 self.CalcMaxLineLen()+3, max(self.LinesInFile()+1, self.sh),
536 self.sx, self.sy)
537
538 #------------ backspace, delete, return
539
540 def BreakLine(self, event):
541 if self.IsLine(self.cy):
542 t = self.lines[self.cy]
543 self.lines = self.lines[:self.cy] + [t[:self.cx],t[self.cx:]] + self.lines[self.cy+1:]
544 self.cVert(1)
545 self.cx = 0
546 self.TouchBuffer()
547
548 def InsertChar(self,char):
549 if self.IsLine(self.cy):
550 t = self.lines[self.cy]
551 t = t[:self.cx] + char + t[self.cx:]
552 self.SetTextLine(self.cy, t)
553 self.cHoriz(1)
554 self.TouchBuffer()
555
556 def JoinLines(self):
557 t1 = self.lines[self.cy]
558 t2 = self.lines[self.cy+1]
559 self.cx = len(t1)
560 self.lines = self.lines[:self.cy] + [t1 + t2] + self.lines[self.cy+2:]
561 self.TouchBuffer()
562
563
564 def DeleteChar(self,x,y,oldtext):
565 newtext = oldtext[:x] + oldtext[x+1:]
566 self.SetTextLine(y, newtext)
567 self.TouchBuffer()
568
569
570 def BackSpace(self, event):
571 t = self.GetTextLine(self.cy)
572 if self.cx>0:
573 self.DeleteChar(self.cx-1,self.cy,t)
574 self.cHoriz(-1)
575 self.TouchBuffer()
576 elif self.cx == 0:
577 if self.cy > 0:
578 self.cy -= 1
579 self.JoinLines()
580 self.TouchBuffer()
581 else:
582 wxBell()
583
584 def Delete(self, event):
585 t = self.GetTextLine(self.cy)
586 if self.cx<len(t):
587 self.DeleteChar(self.cx,self.cy,t)
588 self.TouchBuffer()
589 else:
590 if self.cy < len(self.lines) - 1:
591 self.JoinLines()
592 self.TouchBuffer()
593
594 def Escape(self, event):
595 self.SelectOff()
596
597 def TabKey(self, event):
598 numSpaces = self.SpacesPerTab - (self.cx % self.SpacesPerTab)
599 self.SingleLineInsert(' ' * numSpaces)
600
601 ##----------- selection routines
602
603 def SelectUpdate(self):
604 self.SelectEnd = (self.cy, self.cx)
605 self.SelectNotify(self.Selecting, self.SelectBegin, self.SelectEnd)
606 self.UpdateView()
607
608 def NormalizedSelect(self):
609 (begin, end) = (self.SelectBegin, self.SelectEnd)
610 (bRow, bCol) = begin
611 (eRow, eCol) = end
612 if (bRow < eRow):
613 return (begin, end)
614 elif (eRow < bRow):
615 return (end, begin)
616 else:
617 if (bCol < eCol):
618 return (begin, end)
619 else:
620 return (end, begin)
621
622 def FindSelection(self):
623 if self.SelectEnd is None or self.SelectBegin is None:
624 wxBell()
625 return None
626 (begin, end) = self.NormalizedSelect()
627 (bRow, bCol) = begin
628 (eRow, eCol) = end
629 return (bRow, bCol, eRow, eCol)
630
631 def SelectOff(self):
632 self.SelectBegin = None
633 self.SelectEnd = None
634 self.Selecting = false
635 self.SelectNotify(false,None,None)
636
637 def CopySelection(self, event):
638 selection = self.FindSelection()
639 if selection is None:
640 return
641 (bRow, bCol, eRow, eCol) = selection
642
643 if bRow == eRow:
644 self.SingleLineCopy(bRow, bCol, eCol)
645 else:
646 self.MultipleLineCopy(bRow, bCol, eRow, eCol)
647
648 def OnCopySelection(self, event):
649 self.CopySelection(event)
650 self.SelectOff()
651
652 def CopyToClipboard(self, linesOfText):
653 do = wxTextDataObject()
654 do.SetText(string.join(linesOfText, os.linesep))
655 wxTheClipboard.Open()
656 wxTheClipboard.SetData(do)
657 wxTheClipboard.Close()
658
659 def SingleLineCopy(self, Row, bCol, eCol):
660 Line = self.GetTextLine(Row)
661 self.CopyToClipboard([Line[bCol:eCol]])
662
663 def MultipleLineCopy(self, bRow, bCol, eRow, eCol):
664 bLine = self.GetTextLine(bRow)[bCol:]
665 eLine = self.GetTextLine(eRow)[:eCol]
666 self.CopyToClipboard([bLine] + [l for l in self.lines[bRow + 1:eRow]] + [eLine])
667
668 def OnDeleteSelection(self, event):
669 selection = self.FindSelection()
670 if selection is None:
671 return
672 (bRow, bCol, eRow, eCol) = selection
673
674 if bRow == eRow:
675 self.SingleLineDelete(bRow, bCol, eCol)
676 else:
677 self.MultipleLineDelete(bRow, bCol, eRow, eCol)
678
679 self.TouchBuffer()
680
681 self.cy = bRow
682 self.cx = bCol
683 self.SelectOff()
684 self.UpdateView()
685
686
687 def SingleLineDelete(self, Row, bCol, eCol):
688 ModLine = self.GetTextLine(Row)
689 ModLine = ModLine[:bCol] + ModLine[eCol:]
690 self.SetTextLine(Row,ModLine)
691
692 def MultipleLineDelete(self, bRow, bCol, eRow, eCol):
693 bLine = self.GetTextLine(bRow)
694 eLine = self.GetTextLine(eRow)
695 ModLine = bLine[:bCol] + eLine[eCol:]
696 self.lines[bRow:eRow + 1] = [ModLine]
697
698 def OnPaste(self, event):
699 do = wxTextDataObject()
700 wxTheClipboard.Open()
701 success = wxTheClipboard.GetData(do)
702 wxTheClipboard.Close()
703 if success:
704 pastedLines = LineSplitter(do.GetText())
705 else:
706 wxBell()
707 return
708 if len(pastedLines) == 0:
709 wxBell()
710 return
711 elif len(pastedLines) == 1:
712 self.SingleLineInsert(pastedLines[0])
713 else:
714 self.MultipleLinePaste(pastedLines)
715
716 def SingleLineInsert(self, newText):
717 ModLine = self.GetTextLine(self.cy)
718 ModLine = ModLine[:self.cx] + newText + ModLine[self.cx:]
719 self.SetTextLine(self.cy, ModLine)
720 self.cHoriz(len(newText))
721 self.TouchBuffer()
722 self.UpdateView()
723
724 def MultipleLinePaste(self, pastedLines):
725 FirstLine = LastLine = self.GetTextLine(self.cy)
726 FirstLine = FirstLine[:self.cx] + pastedLines[0]
727 LastLine = pastedLines[-1] + LastLine[self.cx:]
728
729 NewSlice = [FirstLine]
730 NewSlice += [l for l in pastedLines[1:-1]]
731 NewSlice += [LastLine]
732 self.lines[self.cy:self.cy + 1] = NewSlice
733
734 self.cy = self.cy + len(pastedLines)-1
735 self.cx = len(pastedLines[-1])
736 self.TouchBuffer()
737 self.UpdateView()
738
739 def OnCutSelection(self,event):
740 self.CopySelection(event)
741 self.OnDeleteSelection(event)
742
743 #-------------- Keyboard movement implementations
744
745 def MoveDown(self, event):
746 self.cVert(+1)
747
748 def MoveUp(self, event):
749 self.cVert(-1)
750
751 def MoveLeft(self, event):
752 if self.cx == 0:
753 if self.cy == 0:
754 wxBell()
755 else:
756 self.cVert(-1)
757 self.cx = self.CurrentLineLength()
758 else:
759 self.cx -= 1
760
761 def MoveRight(self, event):
762 linelen = self.CurrentLineLength()
763 if self.cx == linelen:
764 if self.cy == len(self.lines) - 1:
765 wxBell()
766 else:
767 self.cx = 0
768 self.cVert(1)
769 else:
770 self.cx += 1
771
772
773 def MovePageDown(self, event):
774 self.cVert(self.sh)
775
776 def MovePageUp(self, event):
777 self.cVert(-self.sh)
778
779 def MoveHome(self, event):
780 self.cx = 0
781
782 def MoveEnd(self, event):
783 self.cx = self.CurrentLineLength()
784
785 def MoveStartOfFile(self, event):
786 self.cy = 0
787 self.cx = 0
788
789 def MoveEndOfFile(self, event):
790 self.cy = len(self.lines) - 1
791 self.cx = self.CurrentLineLength()
792
793 #-------------- Key handler mapping tables
794
795 def SetMoveSpecialFuncs(self, action):
796 action[WXK_DOWN] = self.MoveDown
797 action[WXK_UP] = self.MoveUp
798 action[WXK_LEFT] = self.MoveLeft
799 action[WXK_RIGHT] = self.MoveRight
800 action[WXK_NEXT] = self.MovePageDown
801 action[WXK_PRIOR] = self.MovePageUp
802 action[WXK_HOME] = self.MoveHome
803 action[WXK_END] = self.MoveEnd
804
805 def SetMoveSpecialControlFuncs(self, action):
806 action[WXK_HOME] = self.MoveStartOfFile
807 action[WXK_END] = self.MoveEndOfFile
808
809 def SetAltFuncs(self, action):
810 # subclass implements
811 pass
812
813 def SetControlFuncs(self, action):
814 action['c'] = self.OnCopySelection
815 action['d'] = self.OnDeleteSelection
816 action['v'] = self.OnPaste
817 action['x'] = self.OnCutSelection
818
819 def SetSpecialControlFuncs(self, action):
820 action[WXK_INSERT] = self.OnCopySelection
821
822 def SetShiftFuncs(self, action):
823 action[WXK_DELETE] = self.OnCutSelection
824 action[WXK_INSERT] = self.OnPaste
825
826 def SetSpecialFuncs(self, action):
827 action[WXK_BACK] = self.BackSpace
828 action[WXK_DELETE] = self.Delete
829 action[WXK_RETURN] = self.BreakLine
830 action[WXK_ESCAPE] = self.Escape
831 action[WXK_TAB] = self.TabKey
832
833 ##-------------- Logic for key handlers
834
835
836 def Move(self, keySettingFunction, key, event):
837 action = {}
838 keySettingFunction(action)
839
840 if not action.has_key(key):
841 return false
842
843 if event.ShiftDown():
844 if not self.Selecting:
845 self.Selecting = true
846 self.SelectBegin = (self.cy, self.cx)
847 action[key](event)
848 self.SelectEnd = (self.cy, self.cx)
849 else:
850 action[key](event)
851 if self.Selecting:
852 self.Selecting = false
853
854 self.SelectNotify(self.Selecting, self.SelectBegin, self.SelectEnd)
855 self.UpdateView()
856 return true
857
858 def MoveSpecialKey(self, event, key):
859 return self.Move(self.SetMoveSpecialFuncs, key, event)
860
861 def MoveSpecialControlKey(self, event, key):
862 if not event.ControlDown():
863 return false
864 return self.Move(self.SetMoveSpecialControlFuncs, key, event)
865
866 def Dispatch(self, keySettingFunction, key, event):
867 action = {}
868 keySettingFunction(action)
869 if action.has_key(key):
870 action[key](event)
871 self.UpdateView()
872 return true
873 return false
874
875 def ModifierKey(self, key, event, modifierKeyDown, MappingFunc):
876 if not modifierKeyDown:
877 return false
878
879 key = self.UnixKeyHack(key)
880 try:
881 key = chr(key)
882 except:
883 return false
884 if not self.Dispatch(MappingFunc, key, event):
885 wxBell()
886 return true
887
888 def ControlKey(self, event, key):
889 return self.ModifierKey(key, event, event.ControlDown(), self.SetControlFuncs)
890
891 def AltKey(self, event, key):
892 return self.ModifierKey(key, event, event.AltDown(), self.SetAltFuncs)
893
894 def SpecialControlKey(self, event, key):
895 if not event.ControlDown():
896 return false
897 if not self.Dispatch(self.SetSpecialControlFuncs, key, event):
898 wxBell()
899 return true
900
901 def ShiftKey(self, event, key):
902 if not event.ShiftDown():
903 return false
904 return self.Dispatch(self.SetShiftFuncs, key, event)
905
906 def NormalChar(self, event, key):
907 self.SelectOff()
908
909 # regular ascii
910 if not self.Dispatch(self.SetSpecialFuncs, key, event):
911 if (key>31) and (key<256):
912 self.InsertChar(chr(key))
913 else:
914 wxBell()
915 return
916 self.UpdateView()
917 self.AdjustScrollbars()
918
919 def OnChar(self, event):
920 key = event.KeyCode()
921 filters = [self.AltKey,
922 self.MoveSpecialControlKey,
923 self.ControlKey,
924 self.SpecialControlKey,
925 self.MoveSpecialKey,
926 self.ShiftKey,
927 self.NormalChar]
928 for filter in filters:
929 if filter(event,key):
930 break
931 return 0
932
933 #----------------------- Eliminate memory leaks
934
935 def OnDestroy(self, event):
936 self.mdc = None
937 self.odc = None
938 self.bgColor = None
939 self.fgColor = None
940 self.font = None
941 self.selectColor = None
942 self.scrollTimer = None
943 self.eofMarker = None
944
945 #-------------------- Abstract methods for subclasses
946
947 def OnClick(self):
948 pass
949
950 def SelectNotify(self, Selecting, SelectionBegin, SelectionEnd):
951 pass
952