diff -r 36d378bd852e -r dae55dd9ee14 TextViewer.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TextViewer.py Mon Jul 09 11:10:14 2007 +0200 @@ -0,0 +1,321 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C): Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#Lesser General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from wxPython.wx import * +from wxPython.stc import * +import wx + +import re + +#------------------------------------------------------------------------------- +# Textual programs Viewer class +#------------------------------------------------------------------------------- + + +NEWLINE = "\n" +NUMBERS = [str(i) for i in xrange(10)] +LETTERS = ['_'] +for i in xrange(26): + LETTERS.append(chr(ord('a') + i)) + LETTERS.append(chr(ord('A') + i)) + +[wxSTC_PLC_WORD, wxSTC_PLC_COMMENT, wxSTC_PLC_NUMBER, wxSTC_PLC_VARIABLE, + wxSTC_PLC_FUNCTION, wxSTC_PLC_JUMP] = range(6) +[SPACE, WORD, NUMBER, COMMENT] = range(4) + +[wxID_TEXTVIEWER, +] = [wx.NewId() for _init_ctrls in range(1)] + +if wx.Platform == '__WXMSW__': + faces = { 'times': 'Times New Roman', + 'mono' : 'Courier New', + 'helv' : 'Arial', + 'other': 'Comic Sans MS', + 'size' : 10, + } +else: + faces = { 'times': 'Times', + 'mono' : 'Courier', + 'helv' : 'Helvetica', + 'other': 'new century schoolbook', + 'size' : 12, + } +re_texts = {} +re_texts["letter"] = "[A-Za-z]" +re_texts["digit"] = "[0-9]" +re_texts["identifier"] = "((?:%(letter)s|(?:_(?:%(letter)s|%(digit)s)))(?:_?(?:%(letter)s|%(digit)s))*)"%re_texts +IDENTIFIER_MODEL = re.compile(re_texts["identifier"]) +LABEL_MODEL = re.compile("[ \t\n]%(identifier)s:[ \t\n]"%re_texts) + +class TextViewer(wxStyledTextCtrl): + + def __init__(self, parent, window, controler): + wxStyledTextCtrl.__init__(self, parent, wxID_TEXTVIEWER, style=0) + + self.CmdKeyAssign(ord('+'), wxSTC_SCMOD_CTRL, wxSTC_CMD_ZOOMIN) + self.CmdKeyAssign(ord('-'), wxSTC_SCMOD_CTRL, wxSTC_CMD_ZOOMOUT) + + self.SetViewWhiteSpace(False) + + self.SetLexer(wxSTC_LEX_CONTAINER) + + # Global default styles for all languages + self.StyleSetSpec(wxSTC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) + self.StyleClearAll() # Reset all to be like the default + + self.StyleSetSpec(wxSTC_STYLE_LINENUMBER, "back:#C0C0C0,size:%(size)d" % faces) + self.SetSelBackground(1, "#E0E0E0") + + # Highlighting styles + self.StyleSetSpec(wxSTC_PLC_WORD, "fore:#00007F,bold,size:%(size)d" % faces) + self.StyleSetSpec(wxSTC_PLC_VARIABLE, "fore:#7F0000,size:%(size)d" % faces) + self.StyleSetSpec(wxSTC_PLC_FUNCTION, "fore:#7F7F00,size:%(size)d" % faces) + self.StyleSetSpec(wxSTC_PLC_COMMENT, "fore:#7F7F7F,size:%(size)d" % faces) + self.StyleSetSpec(wxSTC_PLC_NUMBER, "fore:#007F7F,size:%(size)d" % faces) + self.StyleSetSpec(wxSTC_PLC_JUMP, "fore:#007F00,size:%(size)d" % faces) + + # Indicators styles + self.IndicatorSetStyle(0, wxSTC_INDIC_SQUIGGLE) + self.IndicatorSetForeground(0, wxRED) + + # Line numbers in the margin + self.SetMarginType(1, wxSTC_MARGIN_NUMBER) + self.SetMarginWidth(1, 50) + + # Indentation size + self.SetTabWidth(2) + self.SetUseTabs(0) + + self.Keywords = [] + self.Variables = [] + self.Functions = [] + self.Jumps = [] + self.TextChanged = False + self.TextSyntax = "ST" + + self.Controler = controler + + EVT_KEY_DOWN(self, self.OnKeyDown) + EVT_STC_STYLENEEDED(self, wxID_TEXTVIEWER, self.OnStyleNeeded) + EVT_KILL_FOCUS(self, self.OnKillFocus) + + def SetTextSyntax(self, syntax): + self.TextSyntax = syntax + + def SetKeywords(self, keywords): + self.Keywords = [keyword.upper() for keyword in keywords] + self.Colourise(0, -1) + + def SetVariables(self, variables): + self.Variables = [variable.upper() for variable in variables] + self.Colourise(0, -1) + + def SetFunctions(self, blocktypes): + self.Functions = [] + for category in blocktypes: + for blocktype in category["list"]: + if blocktype["name"] not in self.Keywords and blocktype["name"] not in self.Variables: + self.Functions.append(blocktype["name"].upper()) + self.Colourise(0, -1) + + def RefreshJumpList(self): + self.Jumps = [jump.upper() for jump in LABEL_MODEL.findall(self.GetText())] + self.Colourise(0, -1) + + def RefreshView(self): + self.SetText(self.Controler.GetCurrentElementEditingText()) + self.RefreshJumpList() + + def OnStyleNeeded(self, event): + self.TextChanged = True + line = self.LineFromPosition(self.GetEndStyled()) + if line == 0: + start_pos = 0 + else: + start_pos = self.GetLineEndPosition(line - 1) + 1 + end_pos = event.GetPosition() + self.StartStyling(start_pos, 0xff) + + i = start_pos + state = SPACE + line = "" + word = "" + while i < end_pos: + char = chr(self.GetCharAt(i)).upper() + line += char + if char == NEWLINE: + if state == COMMENT: + self.SetStyling(i - start_pos + 1, wxSTC_PLC_COMMENT) + elif state == NUMBER: + self.SetStyling(i - start_pos, wxSTC_PLC_NUMBER) + elif state == WORD: + if word in self.Keywords: + self.SetStyling(i - start_pos, wxSTC_PLC_WORD) + elif word in self.Variables: + self.SetStyling(i - start_pos, wxSTC_PLC_VARIABLE) + elif word in self.Functions: + self.SetStyling(i - start_pos, wxSTC_PLC_FUNCTION) + elif word in self.Jumps: + self.SetStyling(i - start_pos, wxSTC_PLC_JUMP) + else: + self.SetStyling(i - start_pos, 31) + if self.GetCurrentPos() < start_pos or self.GetCurrentPos() > i: + self.StartStyling(start_pos, wxSTC_INDICS_MASK) + self.SetStyling(i - start_pos, wxSTC_INDIC0_MASK) + self.StartStyling(i, 0xff) + else: + self.SetStyling(i - start_pos, 31) + start_pos = i + state = SPACE + line = "" + elif line.endswith("(*") and state != COMMENT: + self.SetStyling(i - start_pos - 1, 31) + start_pos = i + state = COMMENT + elif state == COMMENT: + if line.endswith("*)"): + self.SetStyling(i - start_pos + 2, wxSTC_PLC_COMMENT) + start_pos = i + 1 + state = SPACE + elif char in LETTERS: + if state == NUMBER: + word = "#" + state = WORD + elif state == SPACE: + self.SetStyling(i - start_pos, 31) + word = char + start_pos = i + state = WORD + else: + word += char + elif char in NUMBERS or char == '.' and state != WORD: + if state == SPACE: + self.SetStyling(i - start_pos, 31) + start_pos = i + state = NUMBER + if state == WORD and char != '.': + word += char + else: + if state == WORD: + if word in self.Keywords: + self.SetStyling(i - start_pos, wxSTC_PLC_WORD) + elif word in self.Variables: + self.SetStyling(i - start_pos, wxSTC_PLC_VARIABLE) + elif word in self.Functions: + self.SetStyling(i - start_pos, wxSTC_PLC_FUNCTION) + elif word in self.Jumps: + self.SetStyling(i - start_pos, wxSTC_PLC_JUMP) + else: + self.SetStyling(i - start_pos, 31) + if self.GetCurrentPos() < start_pos or self.GetCurrentPos() > i: + self.StartStyling(start_pos, wxSTC_INDICS_MASK) + self.SetStyling(i - start_pos, wxSTC_INDIC0_MASK) + self.StartStyling(i, 0xff) + word = "" + start_pos = i + state = SPACE + elif state == NUMBER: + self.SetStyling(i - start_pos, wxSTC_PLC_NUMBER) + start_pos = i + state = SPACE + i += 1 + if state == COMMENT: + self.SetStyling(i - start_pos + 2, wxSTC_PLC_COMMENT) + elif state == NUMBER: + self.SetStyling(i - start_pos, wxSTC_PLC_NUMBER) + elif state == WORD: + if word in self.Keywords: + self.SetStyling(i - start_pos, wxSTC_PLC_WORD) + elif word in self.Variables: + self.SetStyling(i - start_pos, wxSTC_PLC_VARIABLE) + elif word in self.Functions: + self.SetStyling(i - start_pos, wxSTC_PLC_FUNCTION) + elif word in self.Jumps: + self.SetStyling(i - start_pos, wxSTC_PLC_JUMP) + else: + self.SetStyling(i - start_pos, 31) + else: + self.SetStyling(i - start_pos, 31) + event.Skip() + + def Cut(self): + self.CmdKeyExecute(wxSTC_CMD_CUT) + + def Copy(self): + self.CmdKeyExecute(wxSTC_CMD_COPY) + + def Paste(self): + self.CmdKeyExecute(wxSTC_CMD_PASTE) + + def RefreshModel(self): + if self.TextChanged: + self.RefreshJumpList() + self.Controler.SetCurrentElementEditingText(self.GetText()) + + def OnKeyDown(self, event): + if self.CallTipActive(): + self.CallTipCancel() + key = event.KeyCode() + + # Code completion + if key == WXK_SPACE and event.ControlDown(): + + line = self.GetCurrentLine() + if line == 0: + start_pos = 0 + else: + start_pos = self.GetLineEndPosition(line - 1) + 1 + end_pos = self.GetCurrentPos() + + lineText = self.GetTextRange(start_pos, end_pos).replace("\t", " ") + words = lineText.split(" ") + words = [word for i, word in enumerate(words) if word != '' or i == len(words) - 1] + + kw = [] + + if self.TextSyntax == "IL": + if len(words) == 1: + kw = self.Keywords + elif len(words) == 2: + if words[0].upper() in ["CAL", "CALC", "CALNC"]: + kw = self.Functions + elif words[0].upper() in ["JMP", "JMPC", "JMPNC"]: + kw = self.Jumps + else: + kw = self.Variables + else: + kw = self.Keywords + self.Variables + self.Functions + if len(kw) > 0: + kw.sort() + self.AutoCompSetIgnoreCase(True) + self.AutoCompShow(len(words[-1]), " ".join(kw)) + else: + self.TextChanged = False + wxCallAfter(self.RefreshModel) + event.Skip() + + def OnKillFocus(self, event): + self.AutoCompCancel() + event.Skip() +