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