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