TextViewer.py
changeset 582 aa41547baa2a
parent 566 6014ef82a98a
child 586 9aa96a36cf33
equal deleted inserted replaced
581:c2d96ea9c14a 582:aa41547baa2a
    27 from types import *
    27 from types import *
    28 
    28 
    29 import re
    29 import re
    30 
    30 
    31 from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
    31 from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
       
    32 from plcopen.structures import ST_BLOCK_START_KEYWORDS, ST_BLOCK_END_KEYWORDS
    32 
    33 
    33 #-------------------------------------------------------------------------------
    34 #-------------------------------------------------------------------------------
    34 #                         Textual programs Viewer class
    35 #                         Textual programs Viewer class
    35 #-------------------------------------------------------------------------------
    36 #-------------------------------------------------------------------------------
    36 
    37 
    96         else:
    97         else:
    97             return i + 1
    98             return i + 1
    98     else:
    99     else:
    99         return None
   100         return None
   100 
   101 
       
   102 def LineStartswith(line, symbols):
       
   103     return reduce(lambda x, y: x or y, map(lambda x: line.startswith(x), symbols), False)
   101 
   104 
   102 class TextViewer(wx.stc.StyledTextCtrl):
   105 class TextViewer(wx.stc.StyledTextCtrl):
   103     
   106     
   104     if wx.VERSION < (2, 6, 0):
   107     if wx.VERSION < (2, 6, 0):
   105         def Bind(self, event, function, id = None):
   108         def Bind(self, event, function, id = None):
   145             self.IndicatorSetForeground(0, wx.WHITE)
   148             self.IndicatorSetForeground(0, wx.WHITE)
   146         
   149         
   147         # Line numbers in the margin
   150         # Line numbers in the margin
   148         self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
   151         self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
   149         self.SetMarginWidth(1, 50)
   152         self.SetMarginWidth(1, 50)
       
   153         
       
   154         # Folding
       
   155         self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPEN,    wx.stc.STC_MARK_BOXMINUS,          "white", "#808080")
       
   156         self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDER,        wx.stc.STC_MARK_BOXPLUS,           "white", "#808080")
       
   157         self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERSUB,     wx.stc.STC_MARK_VLINE,             "white", "#808080")
       
   158         self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERTAIL,    wx.stc.STC_MARK_LCORNER,           "white", "#808080")
       
   159         self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEREND,     wx.stc.STC_MARK_BOXPLUSCONNECTED,  "white", "#808080")
       
   160         self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPENMID, wx.stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
       
   161         self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERMIDTAIL, wx.stc.STC_MARK_TCORNER,           "white", "#808080")
   150         
   162         
   151         # Indentation size
   163         # Indentation size
   152         self.SetTabWidth(2)
   164         self.SetTabWidth(2)
   153         self.SetUseTabs(0)
   165         self.SetUseTabs(0)
   154         
   166         
   177         self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|
   189         self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|
   178                              wx.stc.STC_MOD_BEFOREDELETE|
   190                              wx.stc.STC_MOD_BEFOREDELETE|
   179                              wx.stc.STC_PERFORMED_USER)
   191                              wx.stc.STC_PERFORMED_USER)
   180 
   192 
   181         self.Bind(wx.stc.EVT_STC_STYLENEEDED, self.OnStyleNeeded, id=ID_TEXTVIEWER)
   193         self.Bind(wx.stc.EVT_STC_STYLENEEDED, self.OnStyleNeeded, id=ID_TEXTVIEWER)
       
   194         self.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
   182         if controler:
   195         if controler:
   183             self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
   196             self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
   184             self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_TEXTVIEWER)
   197             self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_TEXTVIEWER)
   185             self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
   198             self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
   186             self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_TEXTVIEWER)
   199             self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_TEXTVIEWER)
   298                 event.SetDragText("")
   311                 event.SetDragText("")
   299         event.Skip()
   312         event.Skip()
   300     
   313     
   301     def SetTextSyntax(self, syntax):
   314     def SetTextSyntax(self, syntax):
   302         self.TextSyntax = syntax
   315         self.TextSyntax = syntax
       
   316         if syntax == "ST":
       
   317             self.SetMarginType(2, wx.stc.STC_MARGIN_SYMBOL)
       
   318             self.SetMarginMask(2, wx.stc.STC_MASK_FOLDERS)
       
   319             self.SetMarginSensitive(2, 1)
       
   320             self.SetMarginWidth(2, 12)
   303     
   321     
   304     def SetKeywords(self, keywords):
   322     def SetKeywords(self, keywords):
   305         self.Keywords = [keyword.upper() for keyword in keywords]
   323         self.Keywords = [keyword.upper() for keyword in keywords]
   306         self.Colourise(0, -1)
   324         self.Colourise(0, -1)
   307     
   325     
   366                         self.Functions[blockname]["extensible"] |= blocktype["extensible"]
   384                         self.Functions[blockname]["extensible"] |= blocktype["extensible"]
   367                     else:
   385                     else:
   368                         self.Functions[blockname] = {"interface": interface,
   386                         self.Functions[blockname] = {"interface": interface,
   369                                                      "extensible": blocktype["extensible"]}
   387                                                      "extensible": blocktype["extensible"]}
   370         self.Colourise(0, -1)
   388         self.Colourise(0, -1)
   371     
   389             
   372     def RefreshVariableTree(self):
   390     def RefreshVariableTree(self):
   373         words = self.TagName.split("::")
   391         words = self.TagName.split("::")
   374         self.Variables = self.GenerateVariableTree([(variable["Name"], variable["Type"], variable["Tree"]) for variable in self.Controler.GetEditedElementInterfaceVars(self.TagName, self.Debug)])
   392         self.Variables = self.GenerateVariableTree([(variable["Name"], variable["Type"], variable["Tree"]) for variable in self.Controler.GetEditedElementInterfaceVars(self.TagName, self.Debug)])
   375         if self.Controler.GetEditedElementType(self.TagName, self.Debug)[1] == "function" or words[0] == "T" and self.TextSyntax == "IL":
   393         if self.Controler.GetEditedElementType(self.TagName, self.Debug)[1] == "function" or words[0] == "T" and self.TextSyntax == "IL":
   376             return_type = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, self.Debug)
   394             return_type = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, self.Debug)
   395     def IsCallParameter(self, name, call):
   413     def IsCallParameter(self, name, call):
   396         if call is not None:
   414         if call is not None:
   397             return (call["interface"].get(name.upper(), None) is not None or 
   415             return (call["interface"].get(name.upper(), None) is not None or 
   398                     call["extensible"] and EXTENSIBLE_PARAMETER.match(name.upper()) is not None)
   416                     call["extensible"] and EXTENSIBLE_PARAMETER.match(name.upper()) is not None)
   399         return False
   417         return False
   400         
   418     
       
   419     def RefreshLineFolding(self, line_number):
       
   420         if self.TextSyntax == "ST":
       
   421             level = wx.stc.STC_FOLDLEVELBASE + self.GetLineIndentation(line_number)
       
   422             line = self.GetLine(line_number).strip()
       
   423             if line == "":
       
   424                 if line_number > 0:
       
   425                     if LineStartswith(self.GetLine(line_number - 1).strip(), ST_BLOCK_END_KEYWORDS):
       
   426                         level = self.GetFoldLevel(self.GetFoldParent(line_number - 1)) & wx.stc.STC_FOLDLEVELNUMBERMASK
       
   427                     else:
       
   428                         level = self.GetFoldLevel(line_number - 1) & wx.stc.STC_FOLDLEVELNUMBERMASK
       
   429                 if level != wx.stc.STC_FOLDLEVELBASE:
       
   430                     level |=  wx.stc.STC_FOLDLEVELWHITEFLAG 
       
   431             elif LineStartswith(line, ST_BLOCK_START_KEYWORDS):
       
   432                 level |= wx.stc.STC_FOLDLEVELHEADERFLAG
       
   433             elif LineStartswith(line, ST_BLOCK_END_KEYWORDS):
       
   434                 if LineStartswith(self.GetLine(line_number - 1).strip(), ST_BLOCK_END_KEYWORDS):
       
   435                     level = self.GetFoldLevel(self.GetFoldParent(line_number - 1)) & wx.stc.STC_FOLDLEVELNUMBERMASK
       
   436                 else:
       
   437                     level = self.GetFoldLevel(line_number - 1) & wx.stc.STC_FOLDLEVELNUMBERMASK
       
   438             self.SetFoldLevel(line_number, level)
       
   439     
   401     def OnStyleNeeded(self, event):
   440     def OnStyleNeeded(self, event):
   402         self.TextChanged = True
   441         self.TextChanged = True
   403         line = self.LineFromPosition(self.GetEndStyled())
   442         line_number = self.LineFromPosition(self.GetEndStyled())
   404         if line == 0:
   443         if line_number == 0:
   405             start_pos = last_styled_pos = 0
   444             start_pos = last_styled_pos = 0
   406         else:
   445         else:
   407             start_pos = last_styled_pos = self.GetLineEndPosition(line - 1) + 1
   446             start_pos = last_styled_pos = self.GetLineEndPosition(line_number - 1) + 1
       
   447         self.RefreshLineFolding(line_number)
   408         end_pos = event.GetPosition()
   448         end_pos = event.GetPosition()
   409         self.StartStyling(start_pos, 0xff)
   449         self.StartStyling(start_pos, 0xff)
   410         
   450         
   411         current_context = self.Variables
   451         current_context = self.Variables
   412         current_call = None
   452         current_call = None
   447                 else:
   487                 else:
   448                     self.SetStyling(current_pos - last_styled_pos, 31)
   488                     self.SetStyling(current_pos - last_styled_pos, 31)
   449                 last_styled_pos = current_pos
   489                 last_styled_pos = current_pos
   450                 state = SPACE
   490                 state = SPACE
   451                 line = ""
   491                 line = ""
       
   492                 line_number += 1
       
   493                 self.RefreshLineFolding(line_number)
   452             elif line.endswith("(*") and state != COMMENT:
   494             elif line.endswith("(*") and state != COMMENT:
   453                 self.SetStyling(current_pos - last_styled_pos - 1, 31)
   495                 self.SetStyling(current_pos - last_styled_pos - 1, 31)
   454                 last_styled_pos = current_pos
   496                 last_styled_pos = current_pos
   455                 if state == WORD:
   497                 if state == WORD:
   456                     current_context = self.Variables
   498                     current_context = self.Variables
   583         else:
   625         else:
   584             self.SetStyling(current_pos - start_pos, 31)
   626             self.SetStyling(current_pos - start_pos, 31)
   585         self.ShowHighlights(start_pos, end_pos)
   627         self.ShowHighlights(start_pos, end_pos)
   586         event.Skip()
   628         event.Skip()
   587     
   629     
       
   630     def OnMarginClick(self, event):
       
   631         if event.GetMargin() == 2:
       
   632             self.ToggleFold(self.LineFromPosition(event.GetPosition()))
       
   633         event.Skip()
       
   634         
   588     def Cut(self):
   635     def Cut(self):
   589         self.ResetBuffer()
   636         self.ResetBuffer()
   590         self.CmdKeyExecute(wx.stc.STC_CMD_CUT)
   637         self.CmdKeyExecute(wx.stc.STC_CMD_CUT)
   591         self.RefreshModel()
   638         self.RefreshModel()
   592         self.RefreshBuffer()
   639         self.RefreshBuffer()
   606     
   653     
   607     def OnKeyDown(self, event):
   654     def OnKeyDown(self, event):
   608         if self.CallTipActive():
   655         if self.CallTipActive():
   609             self.CallTipCancel()
   656             self.CallTipCancel()
   610         key = event.GetKeyCode()
   657         key = event.GetKeyCode()
   611 
   658         key_handled = False
       
   659         
   612         # Code completion
   660         # Code completion
   613         if key == wx.WXK_SPACE and event.ControlDown():
   661         if key == wx.WXK_SPACE and event.ControlDown():
   614             
   662             
   615             line = self.GetCurrentLine()
   663             line = self.GetCurrentLine()
   616             if line == 0:
   664             if line == 0:
   641                 if len(words[-1]) > 0:
   689                 if len(words[-1]) > 0:
   642                     kw = [keyword for keyword in kw if keyword.startswith(words[-1])]
   690                     kw = [keyword for keyword in kw if keyword.startswith(words[-1])]
   643                 kw.sort()
   691                 kw.sort()
   644                 self.AutoCompSetIgnoreCase(True)
   692                 self.AutoCompSetIgnoreCase(True)
   645                 self.AutoCompShow(len(words[-1]), " ".join(kw))
   693                 self.AutoCompShow(len(words[-1]), " ".join(kw))
   646         else:
   694             key_handled = True
       
   695         elif key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER:
       
   696             if self.TextSyntax == "ST":
       
   697                 line = self.GetCurrentLine()
       
   698                 indent = self.GetLineIndentation(line)
       
   699                 if LineStartswith(self.GetLine(line).strip(), ST_BLOCK_START_KEYWORDS):
       
   700                     indent += 2
       
   701                 self.AddText("\n" + " " * indent)
       
   702                 key_handled = True
       
   703         elif key == wx.WXK_BACK:
       
   704             if self.TextSyntax == "ST":
       
   705                 line = self.GetCurrentLine()
       
   706                 indent = self.GetLineIndentation(line)
       
   707                 if self.GetLine(line).strip() == "" and indent > 0:
       
   708                     self.DelLineLeft()
       
   709                     self.AddText(" " * max(0, indent - 2))
       
   710                     key_handled = True
       
   711         if not key_handled:
   647             event.Skip()
   712             event.Skip()
   648 
   713 
   649     def OnKillFocus(self, event):
   714     def OnKillFocus(self, event):
   650         self.AutoCompCancel()
   715         self.AutoCompCancel()
   651         event.Skip()
   716         event.Skip()