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