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