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