]> git.saurik.com Git - wxWidgets.git/blobdiff - utils/wxPython/lib/editor/editor.py
Added wxEditor from Dirc Holtwic
[wxWidgets.git] / utils / wxPython / lib / editor / editor.py
diff --git a/utils/wxPython/lib/editor/editor.py b/utils/wxPython/lib/editor/editor.py
new file mode 100644 (file)
index 0000000..3b0df9c
--- /dev/null
@@ -0,0 +1,619 @@
+#----------------------------------------------------------------------
+# Name:        wxPython.lib.editor.wxEditor
+# Purpose:     An intelligent text editor with colorization capabilities.
+#
+# Author:      Dirk Holtwic, Robin Dunn
+#
+# Created:     15-Dec-1999
+# RCS-ID:      $Id$
+# Copyright:   (c) 1999 by Dirk Holtwick, 1999
+# Licence:     wxWindows license
+#----------------------------------------------------------------------
+
+from wxPython.wx import *
+from string import *
+from keyword import *
+from regsub import *
+from tokenizer import *
+
+#---------------------------------------------------------------------------
+
+#EDITOR_STD_LINE = ("", [], (0,0,0))
+
+#---------
+
+class Line:
+    def __init__(self, text=""):
+        self.text       = text      # the string itself
+        self.syntax     = []        # the colors of the line
+        self.editable   = true      # edit?
+        self.visible    = 0         # will be incremented if not
+        self.indent     = 0         # not used yet
+
+#----------------------------------------------------------------------
+
+class wxEditor(wxScrolledWindow):
+
+    def __init__(self, parent, id=-1):
+    ###############################################################
+        """
+        Alles hat einen Anfang
+        """
+
+        wxScrolledWindow.__init__(self, parent, id,
+                                  wxDefaultPosition, wxSize(500,400),
+                                  wxSUNKEN_BORDER|wxWANTS_CHARS)
+
+        # the syntax informations, if they don't exist,
+        # all syntax stuff will be ignored
+
+        # cursor pos
+        self.cx = 0
+        self.cy = 0
+
+        # the lines that are visible
+        self.lines = []
+        self.line = 0
+        self.len = 0
+
+        self.ocy = 0
+
+        # border pos
+        #self.bx = 0
+        #self.by = 0
+
+        # screen
+        self.sx = 0
+        self.sy = 0
+        self.sw = 0
+        self.sh = 0
+
+        # font
+        dc = wxClientDC(self)
+
+        #if wxPlatform == "__WXMSW__":
+        self.font = wxFont(12, wxMODERN, wxNORMAL, wxNORMAL)
+        #else:
+        #    self.font = wxFont(12, wxMODERN, wxNORMAL, wxNORMAL, false)
+        dc.SetFont(self.font)
+
+        # font weight, height
+        self.fw = dc.GetCharWidth()
+        self.fh = dc.GetCharHeight()
+
+        #  back, for colour
+        self.bcol = wxNamedColour('white')
+        self.fcol = wxNamedColour('black')
+
+        self.cfcol = wxNamedColour('black')
+        self.cbcol = wxNamedColour('red')
+
+        # nicht edierbare zeile (hintergrund)
+        self.nedcol = wxNamedColour('grey')
+
+        self.SetBackgroundColour(self.bcol)
+        #dc.SetForegroundColour(self.fcol)
+
+        # events
+        EVT_LEFT_DOWN(self, self.OnMouseClick)
+        EVT_RIGHT_DOWN(self, self.OnMouseClick)
+        EVT_SCROLLWIN(self, self.OnScroll)
+
+        self.o_cx = self.cx
+        self.o_cy = self.cy
+        self.o_sx = self.sx
+        self.o_sy = self.sy
+        self.o_line = self.line
+        self.sco_x = 0
+        self.sco_y = 0
+
+        self.tabsize = 4
+
+        self.update = true
+        self.in_scroll =FALSE
+
+        bw,bh = self.GetSizeTuple()
+        # double buffering
+        self.mdc = wxMemoryDC()
+        self.mdc.SelectObject(wxEmptyBitmap(bw,bh))
+        # disable physical scrolling because invisible parts are not drawn
+        self.EnableScrolling(FALSE, FALSE)
+
+        # the ordinary text as it is
+        self.SetText()
+        self.SetFocus()
+
+
+#---------------------------------------------------------------------------
+
+    def CalcLines(self):
+    ###############################################################
+        self.lines = []
+        x =maxlen =0
+        for line in self.text:
+            if line.visible==0:
+                self.lines.append(x)
+            else:
+                if len(line.text) >maxlen:
+                    maxlen =len(line.text)
+            x = x + 1
+        self.len = len(self.lines)
+        self.max_linelength =maxlen
+
+    def SetFontTab(self, fonttab):
+    ###############################################################
+        """ Fonttabelle zum schnellen Zugriff """
+        self.ftab = fonttab
+
+    def SetText(self, text = [""]):
+    ###############################################################
+        """ Text mittels Liste setzen """
+        self.cx = 0
+        self.cy = 0
+        self.text = []
+
+        for t in text:
+            self.text.append(Line(t))
+
+        for l in range(0,len(text)-1):
+            #self.UpdateSyntax(l)
+            self.OnUpdateHighlight(l)
+
+        self.OnInit()
+
+        self.update = true
+        self.UpdateView(None, true)
+
+    # show new text
+    def GetText(self):
+    ###############################################################
+        """ Der gesamte Text als Liste """
+        text = []
+        for line in self.text:
+            text.append(line.text)
+        return text
+
+    def IsEmpty(self):
+    ###############################################################
+        """see if at least one text line is not empty"""
+        for line in self.text:
+            if line.text: return 0
+        return 1
+
+    def IsLine(self, line):
+    ###############################################################
+        """ Schauen, ob alles im grünen Bereich ist """
+        return (line>=0) and (line<self.len)
+
+    def IsEditable(self, line):
+    ###############################################################
+        return self.text[self.GetLine(line)].editable
+
+    def GetLine(self, line):
+    ###############################################################
+        return self.lines[line]
+
+    def GetTextLine(self, line):
+    ###############################################################
+        """ Text holen """
+        if self.IsLine(line):
+            return self.text[self.GetLine(line)].text
+        return ""
+
+    def SetTextLine(self, line, text):
+    ###############################################################
+        """ Nur den Text ändern """
+        if self.IsLine(line):
+            l = self.GetLine(line)
+            self.text[l].text = text
+            #self.UpdateSyntax(l)
+            self.OnUpdateHighlight(l)
+            self.update = true
+
+
+#---------------------------------------------------------------------------
+
+    def OnMouseClick(self, event):
+    ###############################################################
+        """
+        Wenn es Click gemacht hat => Cursor setzen
+        """
+        self.SetFocus()
+
+        self.cy = self.sy + (event.GetY() / self.fh)
+        if self.cy >= self.len: self.cy =max(self.len -1, 0)
+        linelen =len(self.text[self.GetLine(self.cy)].text)
+        self.cx = self.sx + (event.GetX() / self.fw)
+        # allow positioning right behind the last character
+        if self.cx > linelen: self.cx =linelen
+        if event.GetEventType() ==wxEVT_RIGHT_DOWN:
+            self.update = true
+            self.OnFold()
+        self.UpdateView()
+
+
+    def DrawCursor(self, dc = None):
+    ###############################################################
+        """
+        Auch der Cursor muß ja irgendwie gezeichnet werden
+        """
+        if not dc:
+            dc = wxClientDC(self)
+
+        if (self.len)<self.cy: #-1 ?
+            self.cy = self.len-1
+        s = self.text[self.GetLine(self.cy)].text
+
+        x = self.cx - self.sx
+        y = self.cy - self.sy
+        self.DrawSimpleCursor(x, y, dc)
+
+
+    def DrawSimpleCursor(self, xp, yp, dc = None, old=false):
+    ###############################################################
+        """
+        Auch der Cursor muß ja irgendwie gezeichnet werden
+        """
+        if not dc:
+            dc = wxClientDC(self)
+
+        if old:
+            xp = self.sco_x
+            yp = self.sco_y
+
+        szx = self.fw
+        szy = self.fh
+        x = xp * szx
+        y = yp * szy
+        dc.Blit(x,y,szx,szy,dc,x,y,wxSRC_INVERT)
+        self.sco_x = xp
+        self.sco_y = yp
+
+    def OnScroll(self, event):
+        dir =event.GetOrientation()
+        evt =event.GetEventType()
+        if dir ==wxHORIZONTAL:
+            if evt ==wxEVT_SCROLLWIN_LINEUP: self.sx =self.sx -1
+            elif evt ==wxEVT_SCROLLWIN_LINEDOWN: self.sx =self.sx +1
+            elif evt ==wxEVT_SCROLLWIN_PAGEUP: self.sx =self.sx -self.sw
+            elif evt ==wxEVT_SCROLLWIN_PAGEDOWN: self.sx =self.sx +self.sw
+            elif evt ==wxEVT_SCROLLWIN_TOP: self.sx =self.cx =0
+            elif evt ==wxEVT_SCROLLWIN_BOTTOM:
+                self.sx =self.max_linelength -self.sw
+                self.cx =self.max_linelength
+            else: self.sx =event.GetPosition()
+            if self.sx >(self.max_linelength -self.sw +1):
+                self.sx =self.max_linelength -self.sw +1
+            if self.sx <0: self.sx =0
+            if self.cx >(self.sx +self.sw -1): self.cx =self.sx +self.sw -1
+            if self.cx <self.sx: self.cx =self.sx
+        else:
+            if evt ==wxEVT_SCROLLWIN_LINEUP: self.sy =self.sy -1
+            elif evt ==wxEVT_SCROLLWIN_LINEDOWN: self.sy =self.sy +1
+            elif evt ==wxEVT_SCROLLWIN_PAGEUP: self.sy =self.sy -self.sh
+            elif evt ==wxEVT_SCROLLWIN_PAGEDOWN: self.sy =self.sy +self.sh
+            elif evt ==wxEVT_SCROLLWIN_TOP: self.sy =self.cy =0
+            elif evt ==wxEVT_SCROLLWIN_BOTTOM:
+                self.sy =self.len -self.sh
+                self.cy =self.len
+            else: self.sy =event.GetPosition()
+            if self.sy >(self.len -self.sh +1):
+                self.sy =self.len -self.sh +1
+            if self.sy <0: self.sy =0
+            if self.cy >(self.sy +self.sh -1): self.cy =self.sy +self.sh -1
+            if self.cy <self.sy: self.cy =self.sy
+        self.UpdateView()
+
+    def AdjustScrollbars(self):
+        # there appears to be endless recursion:
+        # SetScrollbars issue EvtPaint which calls UpdateView
+        # which calls AdjustScrollbars
+        if not self.in_scroll:
+            self.in_scroll =TRUE
+            self.SetScrollbars(self.fw, self.fh, self.max_linelength +1,
+                               # it seem to be a bug in scrollbars:
+                               # the scrollbar is hidden
+                               # even if current position >0
+                               max(self.len +1, self.sy +self.sh),
+                               self.sx, self.sy)
+            self.in_scroll =FALSE
+
+    # adapts the output to what it should be
+    def UpdateView(self, dc = None, doup=false):
+    ###############################################################
+        """
+        Diese Routine wird immer dann aufgerufen, wenn
+        sich etwas verändert hat
+        """
+
+        self.CalcLines()
+
+        if not dc:
+            dc = wxClientDC(self)
+
+        self.bw,self.bh = self.GetSizeTuple()
+        self.sw = self.bw / self.fw
+        self.sh = self.bh / self.fh
+
+        if self.cy<self.sy:
+            self.sy = self.cy
+        elif self.cy>(self.sy+self.sh-1):
+            self.sy = self.cy-self.sh+1
+
+        if self.cx<self.sx:
+            self.sx = self.cx
+        elif self.cx>(self.sx+self.sw-1):
+            self.sx = self.cx-self.sw+1
+
+        # left line? change syntax!
+        if self.ocy!=self.cy:
+            self.OnUpdateSyntax(self.ocy)
+        self.ocy = self.cy
+
+        # alles beim alten
+        self.AdjustScrollbars()
+        self.DrawSimpleCursor(0,0,dc, true)
+        # [als] i don't really understand how the following condition works
+        if self.update or doup:
+            self.Draw(dc)
+            self.update = false
+        else:
+            self.DrawCursor(dc)
+
+        self.o_cx = self.cx
+        self.o_cy = self.cy
+        self.o_sx = self.sx
+        self.o_sy = self.sy
+        self.o_line = self.line
+
+
+    def DrawEditText(self, t, x, y, dc = None):
+    ###############################################################
+        """ Einfache Hilfsroutine um Text zu schreiben
+        """
+        if not dc:
+            dc = wxClientDC(self)
+        dc.SetFont(self.font)
+        dc.DrawText(t, x * self.fw, y * self.fh)
+
+    def DrawLine(self, line, dc=None):
+    ###############################################################
+        """
+        Hier wird einfach die Ansicht der ganzen Seite
+        wiederhergestellt.
+        !!! Kann modifiziert werden !!!
+        """
+
+        if not dc:
+            dc = wxClientDC(self)
+
+        dc.SetBackgroundMode(wxSOLID)
+        dc.SetTextBackground(self.bcol)
+        dc.SetTextForeground(self.fcol)
+        #dc.Clear()
+
+        # delimiter
+        ll = self.sx
+        lr = self.sx + self.sw
+        y = line - self.sy
+
+        # text + syntax
+        if self.IsLine(line):
+            l   = self.GetLine(line)
+            t   = self.text[l].text
+            syn = self.text[l].syntax
+
+            if not self.text[l].editable:
+                dc.SetTextBackground(self.nedcol)
+            else:
+                dc.SetTextBackground(self.bcol)
+
+            dc.SetTextForeground(self.fcol)
+
+            pos = ll
+            for h in syn:
+                xp, col = h
+                if xp>=ll:
+                    self.DrawEditText(t[pos:xp], (pos-ll), y, dc)
+                    pos = xp
+                dc.SetTextForeground(self.ftab[col])
+            self.DrawEditText(t[pos:], (pos-ll), y, dc)
+
+    def Draw(self, odc=None):
+    ###############################################################
+        """
+        Hier wird einfach die Ansicht der ganzen Seite
+        wiederhergestellt.
+        !!! Kann modifiziert werden !!!
+        """
+
+        if not odc:
+            odc = wxClientDC(self)
+
+        dc = self.mdc
+        dc.SelectObject(wxEmptyBitmap(self.bw,self.bh))
+        dc.SetBackgroundMode(wxSOLID)
+        dc.SetTextBackground(self.bcol)
+        dc.SetTextForeground(self.fcol)
+        dc.Clear()
+        for line in range(self.sy, self.sy + self.sh): self.DrawLine(line, dc)
+        odc.Blit(0,0,self.bw,self.bh,dc,0,0,wxCOPY)
+        self.DrawCursor(odc)
+
+
+    def cVert(self, num):
+    ###############################################################
+        """ Vertikale Cursorverschiebung
+        """
+        cy = self.cy + num
+        if cy <0: cy =0
+        elif cy >(self.len -1): cy =self.len -1
+        # scroll when edge hit
+        if cy >(self.sy +self.sh -1): self.sy =cy -self.sh +1
+        elif cy <self.sy: self.sy =cy
+        self.cy =cy
+        # disallow positioning behind the end of the line
+        linelen =len(self.text[self.GetLine(cy)].text)
+        if self.cx >linelen: self.cx =linelen
+
+    def cHoriz(self, num):
+    ###############################################################
+        """ Horizontale Cursorverschiebung
+        """
+        cx = self.cx + num
+        linelen =len(self.text[self.GetLine(self.cy)].text)
+        if cx <0: cx =0
+        elif cx >linelen: cx =linelen
+        # scroll when edge hit
+        if cx >(self.sx +self.sw -2): self.sx =cx -self.sw +2
+        elif cx <self.sx: self.sx =cx
+        self.cx =cx
+
+    def InsertText(self, text):
+    ###############################################################
+        """
+        Simple Routine um Text - auch über mehrere
+        Zeilen - einzufügen
+        """
+
+        if self.IsEditable(self.cy):
+            tis = split(text, "\n")
+
+            t = self.GetTextLine(self.cy)
+
+            if len(tis)==1:
+                t = t[:self.cx] + text + t[self.cx:]
+                self.SetTextLine(self.cy, t)
+                self.cHoriz(len(text))
+            else:
+                rest = t[self.cx:]
+                t = t[:self.cx] + tis[0]
+                self.SetTextLine(self.cy, t)
+                for i in range(1,len(tis)):
+                    self.text.insert(self.GetLine(self.cy)+1, Line())
+                    self.lines.insert(self.cy+1,self.GetLine(self.cy)+1)
+                    self.cVert(+1)
+                    self.SetTextLine(self.cy, tis[i])
+                t = self.GetTextLine(self.cy)
+                self.cx = len(t)
+                t = t + rest
+                self.SetTextLine(self.cy, t)
+            self.update = true
+            #self.UpdateView()
+
+#-----------------------------------------------------------------------------------------
+
+    def RemoveLine(self, line):
+        pass
+
+
+    def OnChar(self, event):
+    ###############################################################
+        """
+        Wenn eine Taste gedrückt wird,
+        kann an dieser Stelle die Auswertung stattfinden
+        """
+
+        # get code
+        key = event.KeyCode()
+
+#        if event.ControlDown:
+#            if chr(key)=="k":
+#                print "weg"
+
+
+        # movements
+        if key==WXK_DOWN:
+            self.cVert(+1)
+        elif key==WXK_UP:
+            self.cVert(-1)
+        elif key==WXK_LEFT:
+            self.cHoriz(-1)
+        elif key==WXK_RIGHT:
+            self.cHoriz(+1)
+
+        elif key==WXK_NEXT:
+            self.cVert(self.sh)
+        elif key==WXK_PRIOR:
+            self.cVert(-self.sh)
+
+        elif key==WXK_HOME:
+            self.cx = 0
+        elif key==WXK_END:
+            self.cx = len(self.GetTextLine(self.cy))
+
+        elif key==WXK_BACK:
+            t = self.GetTextLine(self.cy)
+            if self.cx>0:
+                t = t[:self.cx-1] + t[self.cx:]
+                self.SetTextLine(self.cy, t)
+                self.cHoriz(-1)
+
+        elif key==WXK_DELETE:
+            t = self.GetTextLine(self.cy)
+            if self.cx<len(t):
+                t = t[:self.cx] + t[self.cx+1:]
+            self.SetTextLine(self.cy, t)
+
+        elif key==WXK_RETURN:
+            self.InsertText("\n")
+
+        elif key==WXK_TAB:
+            self.OnTabulator(event)
+
+        # clipboard (buggy)
+        elif key==WXK_F10:
+            if wxTheClipboard.Open():
+                data = wxTheClipboard.GetData()
+                wxTheClipboard.Close()
+                print data
+
+        # folding (buggy)
+        elif key==WXK_F12:
+            self.update = true
+            self.OnFold()
+
+        # regular ascii
+        elif (key>31) and (key<256):
+            self.InsertText(chr(key))
+
+        self.UpdateView()
+        return 0
+
+
+    def OnPaint(self, event):
+        dc = wxPaintDC(self)
+        self.bw,self.bh = self.GetSizeTuple()
+        self.UpdateView(dc, true)
+
+#-----------------------------------------------------------------------------------------
+
+    def GetIndent(self, line):
+        p = 0
+        for c in line:
+            if c==" ": p = p + 1
+            elif c=="\t": p =(p /self.tabsize +1) *self.tabsize
+            else: break
+        return p
+
+    def Goto(self, pos):
+        self.cVert(pos-self.cy-1)
+        self.UpdateView()
+
+# --------------------------------------------------------
+
+    # to be overloaded
+    def OnUpdateHighlight(self, line = -1):
+        pass
+
+    def OnUpdateSyntax(self, line = -1):
+        pass
+
+    def OnTabulator(self, event):
+        pass
+
+    def OnInit(self):
+        pass
+
+    def OnFold(self):
+        pass