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