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