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