]> git.saurik.com Git - wxWidgets.git/blob - utils/wxPython/lib/editor/editor.py
Fixes to avoid an endless event looping for wxGTK
[wxWidgets.git] / utils / wxPython / lib / editor / editor.py
1 #----------------------------------------------------------------------
2 # Name: wxPython.lib.editor.wxEditor
3 # Purpose: An intelligent text editor with colorization capabilities.
4 #
5 # Author: Dirk Holtwic, Robin Dunn
6 #
7 # Created: 15-Dec-1999
8 # RCS-ID: $Id$
9 # Copyright: (c) 1999 by Dirk Holtwick, 1999
10 # Licence: wxWindows license
11 #----------------------------------------------------------------------
12
13
14 # PLEASE NOTE: This is experimental code. It needs an overhall in the
15 # drawing and update code, and there is occasionally a
16 # mysteriously disappearing line...
17 #
18 # I am working on a StyledTextEditor that will likely
19 # render this editor obsolete... But this one is at
20 # least somewhat functional now while the other is still
21 # vapor.
22 #
23 # - Robin
24
25
26 from wxPython.wx import *
27 from string import *
28 from keyword import *
29 from regsub import *
30 from tokenizer import *
31
32 #---------------------------------------------------------------------------
33
34
35 class Line:
36 def __init__(self, text=""):
37 self.text = text # the string itself
38 self.syntax = [] # the colors of the line
39 self.editable = true # edit?
40 self.visible = 0 # will be incremented if not
41 self.indent = 0 # not used yet
42
43 #----------------------------------------------------------------------
44
45 class wxEditor(wxScrolledWindow):
46
47 def __init__(self, parent, id=-1):
48 ###############################################################
49 """
50 Alles hat einen Anfang
51 """
52
53 wxScrolledWindow.__init__(self, parent, id,
54 wxDefaultPosition, wxSize(500,400),
55 wxSUNKEN_BORDER|wxWANTS_CHARS)
56
57 # the syntax informations, if they don't exist,
58 # all syntax stuff will be ignored
59
60 # cursor pos
61 self.cx = 0
62 self.cy = 0
63
64 # the lines that are visible
65 self.lines = []
66 self.line = 0
67 self.len = 0
68
69 self.ocy = 0
70
71 # border pos
72 #self.bx = 0
73 #self.by = 0
74
75 # screen
76 self.sx = 0
77 self.sy = 0
78 self.sw = 0
79 self.sh = 0
80 self.osx= 0
81 self.osy= 0
82
83 # font
84 dc = wxClientDC(self)
85
86 if wxPlatform == "__WXMSW__":
87 self.font = wxFont(10, wxMODERN, wxNORMAL, wxNORMAL)
88 else:
89 self.font = wxFont(12, wxMODERN, wxNORMAL, wxNORMAL, false)
90 dc.SetFont(self.font)
91
92 # font weight, height
93 self.fw = dc.GetCharWidth()
94 self.fh = dc.GetCharHeight()
95
96 # back, for colour
97 self.bcol = wxNamedColour('white')
98 self.fcol = wxNamedColour('black')
99
100 self.cfcol = wxNamedColour('black')
101 self.cbcol = wxNamedColour('red')
102
103 # nicht edierbare zeile (hintergrund)
104 self.nedcol = wxNamedColour('grey')
105
106 self.SetBackgroundColour(self.bcol)
107 #dc.SetForegroundColour(self.fcol)
108
109 # events
110 EVT_LEFT_DOWN(self, self.OnMouseClick)
111 EVT_RIGHT_DOWN(self, self.OnMouseClick)
112 EVT_SCROLLWIN(self, self.OnScroll)
113
114 self.o_cx = self.cx
115 self.o_cy = self.cy
116 self.o_sx = self.sx
117 self.o_sy = self.sy
118 self.o_line = self.line
119 self.sco_x = 0
120 self.sco_y = 0
121
122 self.tabsize = 4
123
124 self.update = true
125 self.in_scroll =FALSE
126 self.inUpdate = FALSE
127
128
129 bw,bh = self.GetSizeTuple()
130 # double buffering
131 self.mdc = wxMemoryDC()
132 self.mdc.SelectObject(wxEmptyBitmap(bw,bh))
133 # disable physical scrolling because invisible parts are not drawn
134 self.EnableScrolling(FALSE, FALSE)
135
136 # the ordinary text as it is
137 self.SetText()
138 self.SetFocus()
139
140
141 #---------------------------------------------------------------------------
142
143 def CalcLines(self):
144 ###############################################################
145 self.lines = []
146 x =maxlen =0
147 for line in self.text:
148 if line.visible==0:
149 self.lines.append(x)
150 else:
151 if len(line.text) >maxlen:
152 maxlen =len(line.text)
153 x = x + 1
154 self.len = len(self.lines)
155 self.max_linelength =maxlen
156
157
158 def SetFontTab(self, fonttab):
159 ###############################################################
160 """ Fonttabelle zum schnellen Zugriff """
161 self.ftab = fonttab
162
163
164 def SetText(self, text = [""]):
165 ###############################################################
166 """ Text mittels Liste setzen """
167 self.cx = 0
168 self.cy = 0
169 self.text = []
170
171 for t in text:
172 self.text.append(Line(t))
173
174 for l in range(0,len(text)-1):
175 #self.UpdateSyntax(l)
176 self.OnUpdateHighlight(l)
177
178 self.OnInit()
179
180 self.update = true
181 self.UpdateView(None, true)
182
183
184 # show new text
185 def GetText(self):
186 ###############################################################
187 """ Der gesamte Text als Liste """
188 text = []
189 for line in self.text:
190 text.append(line.text)
191 return text
192
193
194 def IsEmpty(self):
195 ###############################################################
196 """see if at least one text line is not empty"""
197 for line in self.text:
198 if line.text: return 0
199 return 1
200
201
202 def IsLine(self, line):
203 ###############################################################
204 """ Schauen, ob alles im grünen Bereich ist """
205 return (line>=0) and (line<self.len)
206
207
208 def IsEditable(self, line):
209 ###############################################################
210 return self.text[self.GetLine(line)].editable
211
212
213 def GetLine(self, line):
214 ###############################################################
215 return self.lines[line]
216
217
218 def GetTextLine(self, line):
219 ###############################################################
220 """ Text holen """
221 if self.IsLine(line):
222 return self.text[self.GetLine(line)].text
223 return ""
224
225
226 def SetTextLine(self, line, text):
227 ###############################################################
228 """ Nur den Text ändern """
229 if self.IsLine(line):
230 l = self.GetLine(line)
231 self.text[l].text = text
232 #self.UpdateSyntax(l)
233 self.OnUpdateHighlight(l)
234 self.update = true
235
236
237 #---------------------------------------------------------------------------
238
239 def OnMouseClick(self, event):
240 ###############################################################
241 """
242 Wenn es Click gemacht hat => Cursor setzen
243 """
244 self.SetFocus()
245
246 self.cy = self.sy + (event.GetY() / self.fh)
247 if self.cy >= self.len: self.cy =max(self.len -1, 0)
248 linelen =len(self.text[self.GetLine(self.cy)].text)
249 self.cx = self.sx + (event.GetX() / self.fw)
250 # allow positioning right behind the last character
251 if self.cx > linelen: self.cx =linelen
252 if event.GetEventType() ==wxEVT_RIGHT_DOWN:
253 self.update = true
254 self.OnFold()
255 self.UpdateView()
256
257
258 def DrawCursor(self, dc = None):
259 ###############################################################
260 """
261 Auch der Cursor muß ja irgendwie gezeichnet werden
262 """
263 if not dc:
264 dc = wxClientDC(self)
265
266 if (self.len)<self.cy: #-1 ?
267 self.cy = self.len-1
268 s = self.text[self.GetLine(self.cy)].text
269
270 x = self.cx - self.sx
271 y = self.cy - self.sy
272 self.DrawSimpleCursor(x, y, dc)
273
274
275 def DrawSimpleCursor(self, xp, yp, dc = None, old=false):
276 ###############################################################
277 """
278 Auch der Cursor muß ja irgendwie gezeichnet werden
279 """
280 if not dc:
281 dc = wxClientDC(self)
282
283 if old:
284 xp = self.sco_x
285 yp = self.sco_y
286
287 szx = self.fw
288 szy = self.fh
289 x = xp * szx
290 y = yp * szy
291 dc.Blit(x,y,szx,szy,dc,x,y,wxSRC_INVERT)
292 self.sco_x = xp
293 self.sco_y = yp
294
295
296 def OnScroll(self, event):
297 dir =event.GetOrientation()
298 evt =event.GetEventType()
299 if dir ==wxHORIZONTAL:
300 if evt ==wxEVT_SCROLLWIN_LINEUP: self.sx =self.sx -1
301 elif evt ==wxEVT_SCROLLWIN_LINEDOWN: self.sx =self.sx +1
302 elif evt ==wxEVT_SCROLLWIN_PAGEUP: self.sx =self.sx -self.sw
303 elif evt ==wxEVT_SCROLLWIN_PAGEDOWN: self.sx =self.sx +self.sw
304 elif evt ==wxEVT_SCROLLWIN_TOP: self.sx =self.cx =0
305 elif evt ==wxEVT_SCROLLWIN_BOTTOM:
306 self.sx =self.max_linelength -self.sw
307 self.cx =self.max_linelength
308 else:
309 self.sx =event.GetPosition()
310
311 if self.sx >(self.max_linelength -self.sw +1):
312 self.sx =self.max_linelength -self.sw +1
313 if self.sx <0: self.sx =0
314 if self.cx >(self.sx +self.sw -1): self.cx =self.sx +self.sw -1
315 if self.cx <self.sx: self.cx =self.sx
316
317 else:
318 if evt ==wxEVT_SCROLLWIN_LINEUP: self.sy =self.sy -1
319 elif evt ==wxEVT_SCROLLWIN_LINEDOWN: self.sy =self.sy +1
320 elif evt ==wxEVT_SCROLLWIN_PAGEUP: self.sy =self.sy -self.sh
321 elif evt ==wxEVT_SCROLLWIN_PAGEDOWN: self.sy =self.sy +self.sh
322 elif evt ==wxEVT_SCROLLWIN_TOP: self.sy =self.cy =0
323 elif evt ==wxEVT_SCROLLWIN_BOTTOM:
324 self.sy =self.len -self.sh
325 self.cy =self.len
326 else:
327 self.sy =event.GetPosition()
328
329 if self.sy >(self.len -self.sh +1):
330 self.sy =self.len -self.sh +1
331 if self.sy <0: self.sy =0
332 if self.cy >(self.sy +self.sh -1): self.cy =self.sy +self.sh -1
333 if self.cy <self.sy: self.cy =self.sy
334
335 self.UpdateView()
336
337
338 def AdjustScrollbars(self):
339 # there appears to be endless recursion:
340 # SetScrollbars issue EvtPaint which calls UpdateView
341 # which calls AdjustScrollbars
342 if not self.in_scroll:
343 self.in_scroll =TRUE
344 self.SetScrollbars(self.fw, self.fh, self.max_linelength +1,
345 # it seem to be a bug in scrollbars:
346 # the scrollbar is hidden
347 # even if current position >0
348 max(self.len +1, self.sy +self.sh),
349 self.sx, self.sy)
350 self.osx, self.osy = self.sx, self.sy
351 self.in_scroll =FALSE
352
353
354 # adapts the output to what it should be
355 def UpdateView(self, dc = None, doup=false):
356 ###############################################################
357 """
358 Diese Routine wird immer dann aufgerufen, wenn
359 sich etwas verändert hat
360 """
361 if self.inUpdate:
362 return
363 self.inUpdate = true
364
365 self.CalcLines()
366
367 if not dc:
368 dc = wxClientDC(self)
369
370 self.bw,self.bh = self.GetSizeTuple()
371 self.sw = self.bw / self.fw
372 self.sh = self.bh / self.fh
373
374 if self.cy<self.sy:
375 self.sy = self.cy
376 elif self.cy>(self.sy+self.sh-1):
377 self.sy = self.cy-self.sh+1
378
379 if self.cx<self.sx:
380 self.sx = self.cx
381 elif self.cx>(self.sx+self.sw-1):
382 self.sx = self.cx-self.sw+1
383
384 # left line? change syntax!
385 if self.ocy!=self.cy:
386 self.OnUpdateSyntax(self.ocy)
387 self.ocy = self.cy
388
389 # alles beim alten
390 if self.osx != self.sx or self.osy != self.sy:
391 self.AdjustScrollbars()
392
393 self.DrawSimpleCursor(0,0,dc, true)
394 # [als] i don't really understand how the following condition works
395 #if self.update or doup:
396 self.Draw(dc)
397 # self.update = false
398 #else:
399 # self.DrawCursor(dc)
400
401 self.o_cx = self.cx
402 self.o_cy = self.cy
403 self.o_sx = self.sx
404 self.o_sy = self.sy
405 self.o_line = self.line
406 self.inUpdate = false
407
408
409
410
411 def DrawEditText(self, t, x, y, dc = None):
412 ###############################################################
413 """ Einfache Hilfsroutine um Text zu schreiben
414 """
415 if not dc:
416 dc = wxClientDC(self)
417 dc.SetFont(self.font)
418 dc.DrawText(t, x * self.fw, y * self.fh)
419
420
421 def DrawLine(self, line, dc=None):
422 ###############################################################
423 """
424 Hier wird einfach die Ansicht der ganzen Seite
425 wiederhergestellt.
426 !!! Kann modifiziert werden !!!
427 """
428
429 if not dc:
430 dc = wxClientDC(self)
431
432 dc.SetBackgroundMode(wxSOLID)
433 dc.SetTextBackground(self.bcol)
434 dc.SetTextForeground(self.fcol)
435 #dc.Clear()
436
437 # delimiter
438 ll = self.sx
439 lr = self.sx + self.sw
440 y = line - self.sy
441
442 # text + syntax
443 if self.IsLine(line):
444 l = self.GetLine(line)
445 t = self.text[l].text
446 syn = self.text[l].syntax
447
448 if not self.text[l].editable:
449 dc.SetTextBackground(self.nedcol)
450 else:
451 dc.SetTextBackground(self.bcol)
452
453 dc.SetTextForeground(self.fcol)
454
455 pos = ll
456 for h in syn:
457 xp, col = h
458 if xp>=ll:
459 self.DrawEditText(t[pos:xp], (pos-ll), y, dc)
460 pos = xp
461 dc.SetTextForeground(self.ftab[col])
462 self.DrawEditText(t[pos:], (pos-ll), y, dc)
463
464
465 def Draw(self, odc=None):
466 ###############################################################
467 """
468 Hier wird einfach die Ansicht der ganzen Seite
469 wiederhergestellt.
470 !!! Kann modifiziert werden !!!
471 """
472
473 if not odc:
474 odc = wxClientDC(self)
475
476 dc = self.mdc
477 dc.SelectObject(wxEmptyBitmap(self.bw,self.bh))
478 dc.SetBackgroundMode(wxSOLID)
479 dc.SetTextBackground(self.bcol)
480 dc.SetTextForeground(self.fcol)
481 dc.Clear()
482 for line in range(self.sy, self.sy + self.sh): self.DrawLine(line, dc)
483 odc.Blit(0,0,self.bw,self.bh,dc,0,0,wxCOPY)
484 self.DrawCursor(odc)
485
486
487 def cVert(self, num):
488 ###############################################################
489 """ Vertikale Cursorverschiebung
490 """
491 cy = self.cy + num
492 if cy <0: cy =0
493 elif cy >(self.len -1): cy =self.len -1
494 # scroll when edge hit
495 if cy >(self.sy +self.sh -1): self.sy =cy -self.sh +1
496 elif cy <self.sy: self.sy =cy
497 self.cy =cy
498 # disallow positioning behind the end of the line
499 linelen =len(self.text[self.GetLine(cy)].text)
500 if self.cx >linelen: self.cx =linelen
501
502
503 def cHoriz(self, num):
504 ###############################################################
505 """ Horizontale Cursorverschiebung
506 """
507 cx = self.cx + num
508 linelen =len(self.text[self.GetLine(self.cy)].text)
509 if cx <0: cx =0
510 elif cx >linelen: cx =linelen
511 # scroll when edge hit
512 if cx >(self.sx +self.sw -2): self.sx =cx -self.sw +2
513 elif cx <self.sx: self.sx =cx
514 self.cx =cx
515
516
517 def InsertText(self, text):
518 ###############################################################
519 """
520 Simple Routine um Text - auch über mehrere
521 Zeilen - einzufügen
522 """
523
524 if self.IsEditable(self.cy):
525 tis = split(text, "\n")
526
527 t = self.GetTextLine(self.cy)
528
529 if len(tis)==1:
530 t = t[:self.cx] + text + t[self.cx:]
531 self.SetTextLine(self.cy, t)
532 self.cHoriz(len(text))
533 else:
534 rest = t[self.cx:]
535 t = t[:self.cx] + tis[0]
536 self.SetTextLine(self.cy, t)
537 for i in range(1,len(tis)):
538 self.text.insert(self.GetLine(self.cy)+1, Line())
539 self.lines.insert(self.cy+1,self.GetLine(self.cy)+1)
540 self.cVert(+1)
541 self.SetTextLine(self.cy, tis[i])
542 t = self.GetTextLine(self.cy)
543 self.cx = len(t)
544 t = t + rest
545 self.SetTextLine(self.cy, t)
546 self.update = true
547 #self.UpdateView()
548
549 #-----------------------------------------------------------------------------------------
550
551 def RemoveLine(self, line):
552 pass
553
554
555 def OnChar(self, event):
556 ###############################################################
557 """
558 Wenn eine Taste gedrückt wird,
559 kann an dieser Stelle die Auswertung stattfinden
560 """
561
562 # get code
563 key = event.KeyCode()
564
565 # if event.ControlDown:
566 # if chr(key)=="k":
567 # print "weg"
568
569
570 # movements
571 if key==WXK_DOWN:
572 self.cVert(+1)
573 elif key==WXK_UP:
574 self.cVert(-1)
575 elif key==WXK_LEFT:
576 self.cHoriz(-1)
577 elif key==WXK_RIGHT:
578 self.cHoriz(+1)
579
580 elif key==WXK_NEXT:
581 self.cVert(self.sh)
582 elif key==WXK_PRIOR:
583 self.cVert(-self.sh)
584
585 elif key==WXK_HOME:
586 self.cx = 0
587 elif key==WXK_END:
588 self.cx = len(self.GetTextLine(self.cy))
589
590 elif key==WXK_BACK:
591 t = self.GetTextLine(self.cy)
592 if self.cx>0:
593 t = t[:self.cx-1] + t[self.cx:]
594 self.SetTextLine(self.cy, t)
595 self.cHoriz(-1)
596
597 elif key==WXK_DELETE:
598 t = self.GetTextLine(self.cy)
599 if self.cx<len(t):
600 t = t[:self.cx] + t[self.cx+1:]
601 self.SetTextLine(self.cy, t)
602
603 elif key==WXK_RETURN:
604 self.InsertText("\n")
605
606 elif key==WXK_TAB:
607 self.OnTabulator(event)
608
609 # clipboard (buggy)
610 elif key==WXK_F10:
611 if wxTheClipboard.Open():
612 data = wxTheClipboard.GetData()
613 wxTheClipboard.Close()
614 print data
615
616 # folding (buggy)
617 elif key==WXK_F12:
618 self.update = true
619 self.OnFold()
620
621 # regular ascii
622 elif (key>31) and (key<256):
623 self.InsertText(chr(key))
624
625 self.UpdateView()
626 return 0
627
628
629 def OnPaint(self, event):
630 dc = wxPaintDC(self)
631 self.bw,self.bh = self.GetSizeTuple()
632 self.UpdateView(dc, true)
633
634
635 #-----------------------------------------------------------------------------------------
636
637 def GetIndent(self, line):
638 p = 0
639 for c in line:
640 if c==" ": p = p + 1
641 elif c=="\t": p =(p /self.tabsize +1) *self.tabsize
642 else: break
643 return p
644
645
646 def Goto(self, pos):
647 self.cVert(pos-self.cy-1)
648 self.UpdateView()
649
650 # --------------------------------------------------------
651
652 # to be overloaded
653 def OnUpdateHighlight(self, line = -1):
654 pass
655
656 def OnUpdateSyntax(self, line = -1):
657 pass
658
659 def OnTabulator(self, event):
660 pass
661
662 def OnInit(self):
663 pass
664
665 def OnFold(self):
666 pass
667