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