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