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