1 #----------------------------------------------------------------------
2 # Name: wxPython.lib.editor.wxEditor
3 # Purpose: An intelligent text editor with colorization capabilities.
5 # Author: Dirk Holtwic, Robin Dunn
9 # Copyright: (c) 1999 by Dirk Holtwick, 1999
10 # Licence: wxWindows license
11 #----------------------------------------------------------------------
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...
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
26 from wxPython
.wx
import *
30 from tokenizer
import *
32 #---------------------------------------------------------------------------
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
43 #----------------------------------------------------------------------
45 class wxEditor(wxScrolledWindow
):
47 def __init__(self
, parent
, id=-1):
48 ###############################################################
50 Alles hat einen Anfang
53 wxScrolledWindow
.__init
__(self
, parent
, id,
54 wxDefaultPosition
, wxSize(500,400),
55 wxSUNKEN_BORDER|wxWANTS_CHARS
)
57 # the syntax informations, if they don't exist,
58 # all syntax stuff will be ignored
64 # the lines that are visible
86 if wxPlatform
== "__WXMSW__":
87 self
.font
= wxFont(10, wxMODERN
, wxNORMAL
, wxNORMAL
)
89 self
.font
= wxFont(12, wxMODERN
, wxNORMAL
, wxNORMAL
, false
)
93 self
.fw
= dc
.GetCharWidth()
94 self
.fh
= dc
.GetCharHeight()
97 self
.bcol
= wxNamedColour('white')
98 self
.fcol
= wxNamedColour('black')
100 self
.cfcol
= wxNamedColour('black')
101 self
.cbcol
= wxNamedColour('red')
103 # nicht edierbare zeile (hintergrund)
104 self
.nedcol
= wxNamedColour('grey')
106 self
.SetBackgroundColour(self
.bcol
)
107 #dc.SetForegroundColour(self.fcol)
110 EVT_LEFT_DOWN(self
, self
.OnMouseClick
)
111 EVT_RIGHT_DOWN(self
, self
.OnMouseClick
)
112 EVT_SCROLLWIN(self
, self
.OnScroll
)
118 self
.o_line
= self
.line
125 self
.in_scroll
=FALSE
126 self
.inUpdate
= FALSE
129 bw
,bh
= self
.GetSizeTuple()
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
)
136 # the ordinary text as it is
141 #---------------------------------------------------------------------------
144 ###############################################################
147 for line
in self
.text
:
151 if len(line
.text
) >maxlen
:
152 maxlen
=len(line
.text
)
154 self
.len = len(self
.lines
)
155 self
.max_linelength
=maxlen
158 def SetFontTab(self
, fonttab
):
159 ###############################################################
160 """ Fonttabelle zum schnellen Zugriff """
164 def SetText(self
, text
= [""]):
165 ###############################################################
166 """ Text mittels Liste setzen """
172 self
.text
.append(Line(t
))
174 for l
in range(0,len(text
)-1):
175 #self.UpdateSyntax(l)
176 self
.OnUpdateHighlight(l
)
181 self
.UpdateView(None, true
)
186 ###############################################################
187 """ Der gesamte Text als Liste """
189 for line
in self
.text
:
190 text
.append(line
.text
)
195 ###############################################################
196 """see if at least one text line is not empty"""
197 for line
in self
.text
:
198 if line
.text
: return 0
202 def IsLine(self
, line
):
203 ###############################################################
204 """ Schauen, ob alles im grünen Bereich ist """
205 return (line
>=0) and (line
<self
.len)
208 def IsEditable(self
, line
):
209 ###############################################################
210 return self
.text
[self
.GetLine(line
)].editable
213 def GetLine(self
, line
):
214 ###############################################################
215 return self
.lines
[line
]
218 def GetTextLine(self
, line
):
219 ###############################################################
221 if self
.IsLine(line
):
222 return self
.text
[self
.GetLine(line
)].text
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
)
237 #---------------------------------------------------------------------------
239 def OnMouseClick(self
, event
):
240 ###############################################################
242 Wenn es Click gemacht hat => Cursor setzen
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
:
258 def DrawCursor(self
, dc
= None):
259 ###############################################################
261 Auch der Cursor muß ja irgendwie gezeichnet werden
264 dc
= wxClientDC(self
)
266 if (self
.len)<self
.cy
: #-1 ?
268 s
= self
.text
[self
.GetLine(self
.cy
)].text
270 x
= self
.cx
- self
.sx
271 y
= self
.cy
- self
.sy
272 self
.DrawSimpleCursor(x
, y
, dc
)
275 def DrawSimpleCursor(self
, xp
, yp
, dc
= None, old
=false
):
276 ###############################################################
278 Auch der Cursor muß ja irgendwie gezeichnet werden
281 dc
= wxClientDC(self
)
291 dc
.Blit(x
,y
,szx
,szy
,dc
,x
,y
,wxSRC_INVERT
)
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
309 self
.sx
=event
.GetPosition()
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
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
327 self
.sy
=event
.GetPosition()
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
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
:
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
),
350 self
.osx
, self
.osy
= self
.sx
, self
.sy
351 self
.in_scroll
=FALSE
354 # adapts the output to what it should be
355 def UpdateView(self
, dc
= None, doup
=false
):
356 ###############################################################
358 Diese Routine wird immer dann aufgerufen, wenn
359 sich etwas verändert hat
368 dc
= wxClientDC(self
)
370 self
.bw
,self
.bh
= self
.GetSizeTuple()
371 self
.sw
= self
.bw
/ self
.fw
372 self
.sh
= self
.bh
/ self
.fh
376 elif self
.cy
>(self
.sy
+self
.sh
-1):
377 self
.sy
= self
.cy
-self
.sh
+1
381 elif self
.cx
>(self
.sx
+self
.sw
-1):
382 self
.sx
= self
.cx
-self
.sw
+1
384 # left line? change syntax!
385 if self
.ocy
!=self
.cy
:
386 self
.OnUpdateSyntax(self
.ocy
)
390 if self
.osx
!= self
.sx
or self
.osy
!= self
.sy
:
391 self
.AdjustScrollbars()
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:
397 # self.update = false
399 # self.DrawCursor(dc)
405 self
.o_line
= self
.line
406 self
.inUpdate
= false
411 def DrawEditText(self
, t
, x
, y
, dc
= None):
412 ###############################################################
413 """ Einfache Hilfsroutine um Text zu schreiben
416 dc
= wxClientDC(self
)
417 dc
.SetFont(self
.font
)
418 dc
.DrawText(t
, x
* self
.fw
, y
* self
.fh
)
421 def DrawLine(self
, line
, dc
=None):
422 ###############################################################
424 Hier wird einfach die Ansicht der ganzen Seite
426 !!! Kann modifiziert werden !!!
430 dc
= wxClientDC(self
)
432 dc
.SetBackgroundMode(wxSOLID
)
433 dc
.SetTextBackground(self
.bcol
)
434 dc
.SetTextForeground(self
.fcol
)
439 lr
= self
.sx
+ self
.sw
443 if self
.IsLine(line
):
444 l
= self
.GetLine(line
)
445 t
= self
.text
[l
].text
446 syn
= self
.text
[l
].syntax
448 if not self
.text
[l
].editable
:
449 dc
.SetTextBackground(self
.nedcol
)
451 dc
.SetTextBackground(self
.bcol
)
453 dc
.SetTextForeground(self
.fcol
)
459 self
.DrawEditText(t
[pos
:xp
], (pos
-ll
), y
, dc
)
461 dc
.SetTextForeground(self
.ftab
[col
])
462 self
.DrawEditText(t
[pos
:], (pos
-ll
), y
, dc
)
465 def Draw(self
, odc
=None):
466 ###############################################################
468 Hier wird einfach die Ansicht der ganzen Seite
470 !!! Kann modifiziert werden !!!
474 odc
= wxClientDC(self
)
477 dc
.SelectObject(wxEmptyBitmap(self
.bw
,self
.bh
))
478 dc
.SetBackgroundMode(wxSOLID
)
479 dc
.SetTextBackground(self
.bcol
)
480 dc
.SetTextForeground(self
.fcol
)
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
)
487 def cVert(self
, num
):
488 ###############################################################
489 """ Vertikale Cursorverschiebung
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
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
503 def cHoriz(self
, num
):
504 ###############################################################
505 """ Horizontale Cursorverschiebung
508 linelen
=len(self
.text
[self
.GetLine(self
.cy
)].text
)
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
517 def InsertText(self
, text
):
518 ###############################################################
520 Simple Routine um Text - auch über mehrere
524 if self
.IsEditable(self
.cy
):
525 tis
= split(text
, "\n")
527 t
= self
.GetTextLine(self
.cy
)
530 t
= t
[:self
.cx
] + text
+ t
[self
.cx
:]
531 self
.SetTextLine(self
.cy
, t
)
532 self
.cHoriz(len(text
))
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)
541 self
.SetTextLine(self
.cy
, tis
[i
])
542 t
= self
.GetTextLine(self
.cy
)
545 self
.SetTextLine(self
.cy
, t
)
549 #-----------------------------------------------------------------------------------------
551 def RemoveLine(self
, line
):
555 def OnChar(self
, event
):
556 ###############################################################
558 Wenn eine Taste gedrückt wird,
559 kann an dieser Stelle die Auswertung stattfinden
563 key
= event
.KeyCode()
565 # if event.ControlDown:
588 self
.cx
= len(self
.GetTextLine(self
.cy
))
591 t
= self
.GetTextLine(self
.cy
)
593 t
= t
[:self
.cx
-1] + t
[self
.cx
:]
594 self
.SetTextLine(self
.cy
, t
)
597 elif key
==WXK_DELETE
:
598 t
= self
.GetTextLine(self
.cy
)
600 t
= t
[:self
.cx
] + t
[self
.cx
+1:]
601 self
.SetTextLine(self
.cy
, t
)
603 elif key
==WXK_RETURN
:
604 self
.InsertText("\n")
607 self
.OnTabulator(event
)
611 if wxTheClipboard
.Open():
612 data
= wxTheClipboard
.GetData()
613 wxTheClipboard
.Close()
622 elif (key
>31) and (key
<256):
623 self
.InsertText(chr(key
))
629 def OnPaint(self
, event
):
631 self
.bw
,self
.bh
= self
.GetSizeTuple()
632 self
.UpdateView(dc
, true
)
635 #-----------------------------------------------------------------------------------------
637 def GetIndent(self
, line
):
641 elif c
=="\t": p
=(p
/self
.tabsize
+1) *self
.tabsize
647 self
.cVert(pos
-self
.cy
-1)
650 # --------------------------------------------------------
653 def OnUpdateHighlight(self
, line
= -1):
656 def OnUpdateSyntax(self
, line
= -1):
659 def OnTabulator(self
, event
):