]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/editor/editor.py
e02d521f46082f9fd656c8cc5d5f3dcba2ed35f6
[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():
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 self.SelectEnd is None:
466 self.OnClick()
467 else:
468 self.Selecting = false
469 self.SelectNotify(false, self.SelectBegin, self.SelectEnd)
470
471 self.ReleaseMouse()
472 self.scrollTimer.Stop()
473
474
475 #------------------------- Scrolling
476
477 def HorizScroll(self, event, eventType):
478 maxLineLen = self.CalcMaxLineLen()
479
480 if eventType == wxEVT_SCROLLWIN_LINEUP:
481 self.sx -= 1
482 elif eventType == wxEVT_SCROLLWIN_LINEDOWN:
483 self.sx += 1
484 elif eventType == wxEVT_SCROLLWIN_PAGEUP:
485 self.sx -= self.sw
486 elif eventType == wxEVT_SCROLLWIN_PAGEDOWN:
487 self.sx += self.sw
488 elif eventType == wxEVT_SCROLLWIN_TOP:
489 self.sx = self.cx = 0
490 elif eventType == wxEVT_SCROLLWIN_BOTTOM:
491 self.sx = maxLineLen - self.sw
492 self.cx = maxLineLen
493 else:
494 self.sx = event.GetPosition()
495
496 self.HorizBoundaries()
497
498 def VertScroll(self, event, eventType):
499 if eventType == wxEVT_SCROLLWIN_LINEUP:
500 self.sy -= 1
501 elif eventType == wxEVT_SCROLLWIN_LINEDOWN:
502 self.sy += 1
503 elif eventType == wxEVT_SCROLLWIN_PAGEUP:
504 self.sy -= self.sh
505 elif eventType == wxEVT_SCROLLWIN_PAGEDOWN:
506 self.sy += self.sh
507 elif eventType == wxEVT_SCROLLWIN_TOP:
508 self.sy = self.cy = 0
509 elif eventType == wxEVT_SCROLLWIN_BOTTOM:
510 self.sy = self.LinesInFile() - self.sh
511 self.cy = self.LinesInFile()
512 else:
513 self.sy = event.GetPosition()
514
515 self.VertBoundaries()
516
517 def OnScroll(self, event):
518 dir = event.GetOrientation()
519 eventType = event.GetEventType()
520 if dir == wxHORIZONTAL:
521 self.HorizScroll(event, eventType)
522 else:
523 self.VertScroll(event, eventType)
524 self.UpdateView()
525
526
527 def AdjustScrollbars(self):
528 for i in range(2):
529 self.SetCharDimensions()
530 self.scroller.SetScrollbars(
531 self.fw, self.fh,
532 self.CalcMaxLineLen()+3, max(self.LinesInFile()+1, self.sh),
533 self.sx, self.sy)
534
535 #------------ backspace, delete, return
536
537 def BreakLine(self, event):
538 if self.IsLine(self.cy):
539 t = self.lines[self.cy]
540 self.lines = self.lines[:self.cy] + [t[:self.cx],t[self.cx:]] + self.lines[self.cy+1:]
541 self.cVert(1)
542 self.cx = 0
543 self.TouchBuffer()
544
545 def InsertChar(self,char):
546 if self.IsLine(self.cy):
547 t = self.lines[self.cy]
548 t = t[:self.cx] + char + t[self.cx:]
549 self.SetTextLine(self.cy, t)
550 self.cHoriz(1)
551 self.TouchBuffer()
552
553 def JoinLines(self):
554 t1 = self.lines[self.cy]
555 t2 = self.lines[self.cy+1]
556 self.cx = len(t1)
557 self.lines = self.lines[:self.cy] + [t1 + t2] + self.lines[self.cy+2:]
558 self.TouchBuffer()
559
560
561 def DeleteChar(self,x,y,oldtext):
562 newtext = oldtext[:x] + oldtext[x+1:]
563 self.SetTextLine(y, newtext)
564 self.TouchBuffer()
565
566
567 def BackSpace(self, event):
568 t = self.GetTextLine(self.cy)
569 if self.cx>0:
570 self.DeleteChar(self.cx-1,self.cy,t)
571 self.cHoriz(-1)
572 self.TouchBuffer()
573 elif self.cx == 0:
574 if self.cy > 0:
575 self.cy -= 1
576 self.JoinLines()
577 self.TouchBuffer()
578 else:
579 wxBell()
580
581 def Delete(self, event):
582 t = self.GetTextLine(self.cy)
583 if self.cx<len(t):
584 self.DeleteChar(self.cx,self.cy,t)
585 self.TouchBuffer()
586 else:
587 if self.cy < len(self.lines) - 1:
588 self.JoinLines()
589 self.TouchBuffer()
590
591 def Escape(self, event):
592 self.SelectOff()
593
594 def TabKey(self, event):
595 numSpaces = self.SpacesPerTab - (self.cx % self.SpacesPerTab)
596 self.SingleLineInsert(' ' * numSpaces)
597
598 ##----------- selection routines
599
600 def SelectUpdate(self):
601 self.SelectEnd = (self.cy, self.cx)
602 self.SelectNotify(self.Selecting, self.SelectBegin, self.SelectEnd)
603 self.UpdateView()
604
605 def NormalizedSelect(self):
606 (begin, end) = (self.SelectBegin, self.SelectEnd)
607 (bRow, bCol) = begin
608 (eRow, eCol) = end
609 if (bRow < eRow):
610 return (begin, end)
611 elif (eRow < bRow):
612 return (end, begin)
613 else:
614 if (bCol < eCol):
615 return (begin, end)
616 else:
617 return (end, begin)
618
619 def FindSelection(self):
620 if self.SelectEnd is None or self.SelectBegin is None:
621 wxBell()
622 return None
623 (begin, end) = self.NormalizedSelect()
624 (bRow, bCol) = begin
625 (eRow, eCol) = end
626 return (bRow, bCol, eRow, eCol)
627
628 def SelectOff(self):
629 self.SelectBegin = None
630 self.SelectEnd = None
631 self.Selecting = false
632 self.SelectNotify(false,None,None)
633
634 def CopySelection(self, event):
635 selection = self.FindSelection()
636 if selection is None:
637 return
638 (bRow, bCol, eRow, eCol) = selection
639
640 if bRow == eRow:
641 self.SingleLineCopy(bRow, bCol, eCol)
642 else:
643 self.MultipleLineCopy(bRow, bCol, eRow, eCol)
644
645 def OnCopySelection(self, event):
646 self.CopySelection(event)
647 self.SelectOff()
648
649 def CopyToClipboard(self, linesOfText):
650 do = wxTextDataObject()
651 do.SetText(string.join(linesOfText, os.linesep))
652 wxTheClipboard.Open()
653 wxTheClipboard.SetData(do)
654 wxTheClipboard.Close()
655
656 def SingleLineCopy(self, Row, bCol, eCol):
657 Line = self.GetTextLine(Row)
658 self.CopyToClipboard([Line[bCol:eCol]])
659
660 def MultipleLineCopy(self, bRow, bCol, eRow, eCol):
661 bLine = self.GetTextLine(bRow)[bCol:]
662 eLine = self.GetTextLine(eRow)[:eCol]
663 self.CopyToClipboard([bLine] + [l for l in self.lines[bRow + 1:eRow]] + [eLine])
664
665 def OnDeleteSelection(self, event):
666 selection = self.FindSelection()
667 if selection is None:
668 return
669 (bRow, bCol, eRow, eCol) = selection
670
671 if bRow == eRow:
672 self.SingleLineDelete(bRow, bCol, eCol)
673 else:
674 self.MultipleLineDelete(bRow, bCol, eRow, eCol)
675
676 self.TouchBuffer()
677
678 self.cy = bRow
679 self.cx = bCol
680 self.SelectOff()
681 self.UpdateView()
682
683
684 def SingleLineDelete(self, Row, bCol, eCol):
685 ModLine = self.GetTextLine(Row)
686 ModLine = ModLine[:bCol] + ModLine[eCol:]
687 self.SetTextLine(Row,ModLine)
688
689 def MultipleLineDelete(self, bRow, bCol, eRow, eCol):
690 bLine = self.GetTextLine(bRow)
691 eLine = self.GetTextLine(eRow)
692 ModLine = bLine[:bCol] + eLine[eCol:]
693 self.lines[bRow:eRow + 1] = [ModLine]
694
695 def OnPaste(self, event):
696 do = wxTextDataObject()
697 wxTheClipboard.Open()
698 success = wxTheClipboard.GetData(do)
699 wxTheClipboard.Close()
700 if success:
701 pastedLines = LineSplitter(do.GetText())
702 else:
703 wxBell()
704 return
705 if len(pastedLines) == 0:
706 wxBell()
707 return
708 elif len(pastedLines) == 1:
709 self.SingleLineInsert(pastedLines[0])
710 else:
711 self.MultipleLinePaste(pastedLines)
712
713 def SingleLineInsert(self, newText):
714 ModLine = self.GetTextLine(self.cy)
715 ModLine = ModLine[:self.cx] + newText + ModLine[self.cx:]
716 self.SetTextLine(self.cy, ModLine)
717 self.cHoriz(len(newText))
718 self.TouchBuffer()
719 self.UpdateView()
720
721 def MultipleLinePaste(self, pastedLines):
722 FirstLine = LastLine = self.GetTextLine(self.cy)
723 FirstLine = FirstLine[:self.cx] + pastedLines[0]
724 LastLine = pastedLines[-1] + LastLine[self.cx:]
725
726 NewSlice = [FirstLine]
727 NewSlice += [l for l in pastedLines[1:-1]]
728 NewSlice += [LastLine]
729 self.lines[self.cy:self.cy + 1] = NewSlice
730
731 self.cy = self.cy + len(pastedLines)-1
732 self.cx = len(pastedLines[-1])
733 self.TouchBuffer()
734 self.UpdateView()
735
736 def OnCutSelection(self,event):
737 self.CopySelection(event)
738 self.OnDeleteSelection(event)
739
740 #-------------- Keyboard movement implementations
741
742 def MoveDown(self, event):
743 self.cVert(+1)
744
745 def MoveUp(self, event):
746 self.cVert(-1)
747
748 def MoveLeft(self, event):
749 if self.cx == 0:
750 if self.cy == 0:
751 wxBell()
752 else:
753 self.cVert(-1)
754 self.cx = self.CurrentLineLength()
755 else:
756 self.cx -= 1
757
758 def MoveRight(self, event):
759 linelen = self.CurrentLineLength()
760 if self.cx == linelen:
761 if self.cy == len(self.lines) - 1:
762 wxBell()
763 else:
764 self.cx = 0
765 self.cVert(1)
766 else:
767 self.cx += 1
768
769
770 def MovePageDown(self, event):
771 self.cVert(self.sh)
772
773 def MovePageUp(self, event):
774 self.cVert(-self.sh)
775
776 def MoveHome(self, event):
777 self.cx = 0
778
779 def MoveEnd(self, event):
780 self.cx = self.CurrentLineLength()
781
782 def MoveStartOfFile(self, event):
783 self.cy = 0
784 self.cx = 0
785
786 def MoveEndOfFile(self, event):
787 self.cy = len(self.lines) - 1
788 self.cx = self.CurrentLineLength()
789
790 #-------------- Key handler mapping tables
791
792 def SetMoveSpecialFuncs(self, action):
793 action[WXK_DOWN] = self.MoveDown
794 action[WXK_UP] = self.MoveUp
795 action[WXK_LEFT] = self.MoveLeft
796 action[WXK_RIGHT] = self.MoveRight
797 action[WXK_NEXT] = self.MovePageDown
798 action[WXK_PRIOR] = self.MovePageUp
799 action[WXK_HOME] = self.MoveHome
800 action[WXK_END] = self.MoveEnd
801
802 def SetMoveSpecialControlFuncs(self, action):
803 action[WXK_HOME] = self.MoveStartOfFile
804 action[WXK_END] = self.MoveEndOfFile
805
806 def SetAltFuncs(self, action):
807 # subclass implements
808 pass
809
810 def SetControlFuncs(self, action):
811 action['c'] = self.OnCopySelection
812 action['d'] = self.OnDeleteSelection
813 action['v'] = self.OnPaste
814 action['x'] = self.OnCutSelection
815
816 def SetSpecialControlFuncs(self, action):
817 action[WXK_INSERT] = self.OnCopySelection
818
819 def SetShiftFuncs(self, action):
820 action[WXK_DELETE] = self.OnCutSelection
821 action[WXK_INSERT] = self.OnPaste
822
823 def SetSpecialFuncs(self, action):
824 action[WXK_BACK] = self.BackSpace
825 action[WXK_DELETE] = self.Delete
826 action[WXK_RETURN] = self.BreakLine
827 action[WXK_ESCAPE] = self.Escape
828 action[WXK_TAB] = self.TabKey
829
830 ##-------------- Logic for key handlers
831
832
833 def Move(self, keySettingFunction, key, event):
834 action = {}
835 keySettingFunction(action)
836
837 if not action.has_key(key):
838 return false
839
840 if event.ShiftDown():
841 if not self.Selecting:
842 self.Selecting = true
843 self.SelectBegin = (self.cy, self.cx)
844 action[key](event)
845 self.SelectEnd = (self.cy, self.cx)
846 else:
847 action[key](event)
848 if self.Selecting:
849 self.Selecting = false
850
851 self.SelectNotify(self.Selecting, self.SelectBegin, self.SelectEnd)
852 self.UpdateView()
853 return true
854
855 def MoveSpecialKey(self, event, key):
856 return self.Move(self.SetMoveSpecialFuncs, key, event)
857
858 def MoveSpecialControlKey(self, event, key):
859 if not event.ControlDown():
860 return false
861 return self.Move(self.SetMoveSpecialControlFuncs, key, event)
862
863 def Dispatch(self, keySettingFunction, key, event):
864 action = {}
865 keySettingFunction(action)
866 if action.has_key(key):
867 action[key](event)
868 self.UpdateView()
869 return true
870 return false
871
872 def ModifierKey(self, key, event, modifierKeyDown, MappingFunc):
873 if not modifierKeyDown:
874 return false
875
876 key = self.UnixKeyHack(key)
877 try:
878 key = chr(key)
879 except:
880 return false
881 if not self.Dispatch(MappingFunc, key, event):
882 wxBell()
883 return true
884
885 def ControlKey(self, event, key):
886 return self.ModifierKey(key, event, event.ControlDown(), self.SetControlFuncs)
887
888 def AltKey(self, event, key):
889 return self.ModifierKey(key, event, event.AltDown(), self.SetAltFuncs)
890
891 def SpecialControlKey(self, event, key):
892 if not event.ControlDown():
893 return false
894 if not self.Dispatch(self.SetSpecialControlFuncs, key, event):
895 wxBell()
896 return true
897
898 def ShiftKey(self, event, key):
899 if not event.ShiftDown():
900 return false
901 return self.Dispatch(self.SetShiftFuncs, key, event)
902
903 def NormalChar(self, event, key):
904 self.SelectOff()
905
906 # regular ascii
907 if not self.Dispatch(self.SetSpecialFuncs, key, event):
908 if (key>31) and (key<256):
909 self.InsertChar(chr(key))
910 else:
911 wxBell()
912 return
913 self.UpdateView()
914 self.AdjustScrollbars()
915
916 def OnChar(self, event):
917 key = event.KeyCode()
918 filters = [self.AltKey,
919 self.MoveSpecialControlKey,
920 self.ControlKey,
921 self.SpecialControlKey,
922 self.MoveSpecialKey,
923 self.ShiftKey,
924 self.NormalChar]
925 for filter in filters:
926 if filter(event,key):
927 break
928 return 0
929
930 #----------------------- Eliminate memory leaks
931
932 def OnDestroy(self, event):
933 self.mdc = None
934 self.odc = None
935 self.bgColor = None
936 self.fgColor = None
937 self.font = None
938 self.selectColor = None
939 self.scrollTimer = None
940 self.eofMarker = None
941
942 #-------------------- Abstract methods for subclasses
943
944 def OnClick(self):
945 pass
946
947 def SelectNotify(self, Selecting, SelectionBegin, SelectionEnd):
948 pass
949