py_ext/PythonEditor.py
changeset 1097 233681f2a00e
parent 1091 5f612651d227
child 1110 b6e252733c64
equal deleted inserted replaced
1096:c9ace6a881c9 1097:233681f2a00e
     1 import re
       
     2 import keyword
     1 import keyword
     3 
       
     4 import wx
       
     5 import wx.grid
       
     6 import wx.stc as stc
     2 import wx.stc as stc
     7 
     3 
     8 from plcopen.plcopen import TestTextElement
     4 from controls.CustomStyledTextCtrl import faces
     9 from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
     5 from editors.CodeFileEditor import CodeFileEditor, CodeEditor
    10 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
       
    11 from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos
       
    12 
     6 
    13 [STC_PYTHON_ERROR, STC_PYTHON_SEARCH_RESULT] = range(15, 17)
     7 class PythonCodeEditor(CodeEditor):
    14 
     8 
    15 HIGHLIGHT_TYPES = {
     9     KEYWORDS = keyword.kwlist
    16     ERROR_HIGHLIGHT: STC_PYTHON_ERROR,
    10     COMMENT_HEADER = "##"
    17     SEARCH_RESULT_HIGHLIGHT: STC_PYTHON_SEARCH_RESULT,
    11     
    18 }
    12     def SetCodeLexer(self):
       
    13         self.SetLexer(stc.STC_LEX_PYTHON)
       
    14         
       
    15         # Line numbers in margin
       
    16         self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2,size:%(size)d' % faces)    
       
    17         # Highlighted brace
       
    18         self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00,size:%(size)d' % faces)
       
    19         # Unmatched brace
       
    20         self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000,size:%(size)d' % faces)
       
    21         # Indentation guide
       
    22         self.StyleSetSpec(stc.STC_STYLE_INDENTGUIDE, 'fore:#CDCDCD,size:%(size)d' % faces)
    19 
    23 
    20 [ID_PYTHONEDITOR,
    24         # Python styles
    21 ] = [wx.NewId() for _init_ctrls in range(1)]
    25         self.StyleSetSpec(stc.STC_P_DEFAULT, 'fore:#000000,size:%(size)d' % faces)
       
    26         # Comments
       
    27         self.StyleSetSpec(stc.STC_P_COMMENTLINE,  'fore:#008000,back:#F0FFF0,size:%(size)d' % faces)
       
    28         self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0,size:%(size)d' % faces)
       
    29         # Numbers
       
    30         self.StyleSetSpec(stc.STC_P_NUMBER, 'fore:#008080,size:%(size)d' % faces)
       
    31         # Strings and characters
       
    32         self.StyleSetSpec(stc.STC_P_STRING, 'fore:#800080,size:%(size)d' % faces)
       
    33         self.StyleSetSpec(stc.STC_P_CHARACTER, 'fore:#800080,size:%(size)d' % faces)
       
    34         # Keywords
       
    35         self.StyleSetSpec(stc.STC_P_WORD, 'fore:#000080,bold,size:%(size)d' % faces)
       
    36         # Triple quotes
       
    37         self.StyleSetSpec(stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA,size:%(size)d' % faces)
       
    38         self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA,size:%(size)d' % faces)
       
    39         # Class names
       
    40         self.StyleSetSpec(stc.STC_P_CLASSNAME, 'fore:#0000FF,bold,size:%(size)d' % faces)
       
    41         # Function names
       
    42         self.StyleSetSpec(stc.STC_P_DEFNAME, 'fore:#008080,bold,size:%(size)d' % faces)
       
    43         # Operators
       
    44         self.StyleSetSpec(stc.STC_P_OPERATOR, 'fore:#800000,bold,size:%(size)d' % faces)
       
    45         # Identifiers. I leave this as not bold because everything seems
       
    46         # to be an identifier if it doesn't match the above criterae
       
    47         self.StyleSetSpec(stc.STC_P_IDENTIFIER, 'fore:#000000,size:%(size)d' % faces)
       
    48         
    22 
    49 
    23 class PythonEditor(ConfTreeNodeEditor):
    50 #-------------------------------------------------------------------------------
       
    51 #                          CFileEditor Main Frame Class
       
    52 #-------------------------------------------------------------------------------
    24 
    53 
    25     fold_symbols = 3
    54 class PythonEditor(CodeFileEditor):
    26     CONFNODEEDITOR_TABS = [
    55     
       
    56     CONFNODEEDITOR_TABS = CodeFileEditor.CONFNODEEDITOR_TABS + [
    27         (_("Python code"), "_create_PythonCodeEditor")]
    57         (_("Python code"), "_create_PythonCodeEditor")]
    28     
    58     
    29     def _create_PythonCodeEditor(self, prnt):
    59     def _create_PythonCodeEditor(self, prnt):
    30         self.PythonCodeEditor = CustomStyledTextCtrl(id=ID_PYTHONEDITOR, parent=prnt,
    60         self.PythonCodeEditor = PythonCodeEditor(prnt, self.ParentWindow, self.Controler)
    31                  name="TextViewer", pos=wx.DefaultPosition, 
       
    32                  size=wx.DefaultSize, style=0)
       
    33         self.PythonCodeEditor.ParentWindow = self
       
    34         
       
    35         self.PythonCodeEditor.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
       
    36         self.PythonCodeEditor.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
       
    37 
       
    38         self.PythonCodeEditor.SetLexer(stc.STC_LEX_PYTHON)
       
    39         self.PythonCodeEditor.SetKeyWords(0, " ".join(keyword.kwlist))
       
    40 
       
    41         self.PythonCodeEditor.SetProperty("fold", "1")
       
    42         self.PythonCodeEditor.SetProperty("tab.timmy.whinge.level", "1")
       
    43         self.PythonCodeEditor.SetMargins(0,0)
       
    44 
       
    45         self.PythonCodeEditor.SetViewWhiteSpace(False)
       
    46         
       
    47         self.PythonCodeEditor.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
       
    48         self.PythonCodeEditor.SetEdgeColumn(78)
       
    49 
       
    50         # Set up the numbers in the margin for margin #1
       
    51         self.PythonCodeEditor.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
       
    52         # Reasonable value for, say, 4-5 digits using a mono font (40 pix)
       
    53         self.PythonCodeEditor.SetMarginWidth(1, 40)
       
    54 
       
    55         # Setup a margin to hold fold markers
       
    56         self.PythonCodeEditor.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
       
    57         self.PythonCodeEditor.SetMarginMask(2, stc.STC_MASK_FOLDERS)
       
    58         self.PythonCodeEditor.SetMarginSensitive(2, True)
       
    59         self.PythonCodeEditor.SetMarginWidth(2, 12)
       
    60 
       
    61         if self.fold_symbols == 0:
       
    62             # Arrow pointing right for contracted folders, arrow pointing down for expanded
       
    63             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_ARROWDOWN, "black", "black")
       
    64             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_ARROW, "black", "black")
       
    65             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_EMPTY, "black", "black")
       
    66             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_EMPTY, "black", "black")
       
    67             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_EMPTY,     "white", "black")
       
    68             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY,     "white", "black")
       
    69             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY,     "white", "black")
       
    70             
       
    71         elif self.fold_symbols == 1:
       
    72             # Plus for contracted folders, minus for expanded
       
    73             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_MINUS, "white", "black")
       
    74             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_PLUS,  "white", "black")
       
    75             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_EMPTY, "white", "black")
       
    76             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_EMPTY, "white", "black")
       
    77             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_EMPTY, "white", "black")
       
    78             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
       
    79             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
       
    80 
       
    81         elif self.fold_symbols == 2:
       
    82             # Like a flattened tree control using circular headers and curved joins
       
    83             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_CIRCLEMINUS,          "white", "#404040")
       
    84             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_CIRCLEPLUS,           "white", "#404040")
       
    85             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,                "white", "#404040")
       
    86             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNERCURVE,         "white", "#404040")
       
    87             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_CIRCLEPLUSCONNECTED,  "white", "#404040")
       
    88             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
       
    89             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE,         "white", "#404040")
       
    90 
       
    91         elif self.fold_symbols == 3:
       
    92             # Like a flattened tree control using square headers
       
    93             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_BOXMINUS,          "white", "#808080")
       
    94             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_BOXPLUS,           "white", "#808080")
       
    95             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,             "white", "#808080")
       
    96             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNER,           "white", "#808080")
       
    97             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_BOXPLUSCONNECTED,  "white", "#808080")
       
    98             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
       
    99             self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER,           "white", "#808080")
       
   100 
       
   101 
       
   102         self.PythonCodeEditor.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
       
   103         self.PythonCodeEditor.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
       
   104         self.PythonCodeEditor.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
       
   105         
       
   106         # Global default style
       
   107         if wx.Platform == '__WXMSW__':
       
   108             self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Courier New')
       
   109         elif wx.Platform == '__WXMAC__':
       
   110             # TODO: if this looks fine on Linux too, remove the Mac-specific case 
       
   111             # and use this whenever OS != MSW.
       
   112             self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Monaco')
       
   113         else:
       
   114             defsize = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT).GetPointSize()
       
   115             self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Courier,size:%d'%defsize)
       
   116 
       
   117         # Clear styles and revert to default.
       
   118         self.PythonCodeEditor.StyleClearAll()
       
   119 
       
   120         # Following style specs only indicate differences from default.
       
   121         # The rest remains unchanged.
       
   122 
       
   123         # Line numbers in margin
       
   124         self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2,size:%(size)d' % faces)    
       
   125         # Highlighted brace
       
   126         self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00,size:%(size)d' % faces)
       
   127         # Unmatched brace
       
   128         self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000,size:%(size)d' % faces)
       
   129         # Indentation guide
       
   130         self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_INDENTGUIDE, 'fore:#CDCDCD,size:%(size)d' % faces)
       
   131 
       
   132         # Python styles
       
   133         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_DEFAULT, 'fore:#000000,size:%(size)d' % faces)
       
   134         # Comments
       
   135         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_COMMENTLINE,  'fore:#008000,back:#F0FFF0,size:%(size)d' % faces)
       
   136         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0,size:%(size)d' % faces)
       
   137         # Numbers
       
   138         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_NUMBER, 'fore:#008080,size:%(size)d' % faces)
       
   139         # Strings and characters
       
   140         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_STRING, 'fore:#800080,size:%(size)d' % faces)
       
   141         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_CHARACTER, 'fore:#800080,size:%(size)d' % faces)
       
   142         # Keywords
       
   143         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_WORD, 'fore:#000080,bold,size:%(size)d' % faces)
       
   144         # Triple quotes
       
   145         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA,size:%(size)d' % faces)
       
   146         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA,size:%(size)d' % faces)
       
   147         # Class names
       
   148         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_CLASSNAME, 'fore:#0000FF,bold,size:%(size)d' % faces)
       
   149         # Function names
       
   150         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_DEFNAME, 'fore:#008080,bold,size:%(size)d' % faces)
       
   151         # Operators
       
   152         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_OPERATOR, 'fore:#800000,bold,size:%(size)d' % faces)
       
   153         # Identifiers. I leave this as not bold because everything seems
       
   154         # to be an identifier if it doesn't match the above criterae
       
   155         self.PythonCodeEditor.StyleSetSpec(stc.STC_P_IDENTIFIER, 'fore:#000000,size:%(size)d' % faces)
       
   156         
       
   157         # Highlighting styles
       
   158         self.PythonCodeEditor.StyleSetSpec(STC_PYTHON_ERROR, 'fore:#FF0000,back:#FFFF00,size:%(size)d' % faces)
       
   159         self.PythonCodeEditor.StyleSetSpec(STC_PYTHON_SEARCH_RESULT, 'fore:#FFFFFF,back:#FFA500,size:%(size)d' % faces)
       
   160         
       
   161         # Caret color
       
   162         self.PythonCodeEditor.SetCaretForeground("BLUE")
       
   163         # Selection background
       
   164         self.PythonCodeEditor.SetSelBackground(1, '#66CCFF')
       
   165 
       
   166         self.PythonCodeEditor.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
       
   167         self.PythonCodeEditor.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
       
   168         
       
   169         # register some images for use in the AutoComplete box.
       
   170         #self.RegisterImage(1, images.getSmilesBitmap())
       
   171         self.PythonCodeEditor.RegisterImage(1, 
       
   172             wx.ArtProvider.GetBitmap(wx.ART_DELETE, size=(16,16)))
       
   173         self.PythonCodeEditor.RegisterImage(2, 
       
   174             wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
       
   175         self.PythonCodeEditor.RegisterImage(3, 
       
   176             wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
       
   177 
       
   178         # Indentation and tab stuff
       
   179         self.PythonCodeEditor.SetIndent(4)               # Proscribed indent size for wx
       
   180         self.PythonCodeEditor.SetIndentationGuides(True) # Show indent guides
       
   181         self.PythonCodeEditor.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
       
   182         self.PythonCodeEditor.SetTabIndents(True)        # Tab key indents
       
   183         self.PythonCodeEditor.SetTabWidth(4)             # Proscribed tab size for wx
       
   184         self.PythonCodeEditor.SetUseTabs(False)          # Use spaces rather than tabs, or
       
   185                                         # TabTimmy will complain!    
       
   186         # White space
       
   187         self.PythonCodeEditor.SetViewWhiteSpace(False)   # Don't view white space
       
   188 
       
   189         # EOL: Since we are loading/saving ourselves, and the
       
   190         # strings will always have \n's in them, set the STC to
       
   191         # edit them that way.            
       
   192         self.PythonCodeEditor.SetEOLMode(stc.STC_EOL_LF)
       
   193         self.PythonCodeEditor.SetViewEOL(False)
       
   194         
       
   195         # No right-edge mode indicator
       
   196         self.PythonCodeEditor.SetEdgeMode(stc.STC_EDGE_NONE)
       
   197         
       
   198         self.PythonCodeEditor.SetModEventMask(stc.STC_MOD_BEFOREINSERT|stc.STC_MOD_BEFOREDELETE)
       
   199 
       
   200         self.PythonCodeEditor.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_PYTHONEDITOR)
       
   201         self.PythonCodeEditor.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
       
   202         self.PythonCodeEditor.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_PYTHONEDITOR)
       
   203         
    61         
   204         return self.PythonCodeEditor
    62         return self.PythonCodeEditor
   205 
    63 
   206     def __init__(self, parent, controler, window):
    64     def RefreshView(self):
   207         ConfTreeNodeEditor.__init__(self, parent, controler, window)
    65         CodeFileEditor.RefreshView(self)
   208         
    66         
   209         self.DisableEvents = False
    67         self.PythonCodeEditor.RefreshView()
   210         self.CurrentAction = None
       
   211     
       
   212         self.Highlights = []
       
   213         self.SearchParams = None
       
   214         self.SearchResults = None
       
   215         self.CurrentFindHighlight = None
       
   216         
       
   217         self.RefreshHighlightsTimer = wx.Timer(self, -1)
       
   218         self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
       
   219     
       
   220     def GetBufferState(self):
       
   221         return self.Controler.GetBufferState()
       
   222         
       
   223     def Undo(self):
       
   224         self.Controler.LoadPrevious()
       
   225         self.RefreshView()
       
   226             
       
   227     def Redo(self):
       
   228         self.Controler.LoadNext()
       
   229         self.RefreshView()
       
   230     
       
   231     def OnModification(self, event):
       
   232         if not self.DisableEvents:
       
   233             mod_type = event.GetModificationType()
       
   234             if not (mod_type&wx.stc.STC_PERFORMED_UNDO or mod_type&wx.stc.STC_PERFORMED_REDO):
       
   235                 if mod_type&wx.stc.STC_MOD_BEFOREINSERT:
       
   236                     if self.CurrentAction is None:
       
   237                         self.StartBuffering()
       
   238                     elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1:
       
   239                         self.Controler.EndBuffering()
       
   240                         self.StartBuffering()
       
   241                     self.CurrentAction = ("Add", event.GetPosition())
       
   242                     wx.CallAfter(self.RefreshModel)
       
   243                 elif mod_type&wx.stc.STC_MOD_BEFOREDELETE:
       
   244                     if self.CurrentAction == None:
       
   245                         self.StartBuffering()
       
   246                     elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1:
       
   247                         self.Controler.EndBuffering()
       
   248                         self.StartBuffering()
       
   249                     self.CurrentAction = ("Delete", event.GetPosition())
       
   250                     wx.CallAfter(self.RefreshModel)
       
   251         event.Skip()
       
   252     
       
   253     def OnDoDrop(self, event):
       
   254         self.ResetBuffer()
       
   255         wx.CallAfter(self.RefreshModel)
       
   256         event.Skip()
       
   257 
       
   258     # Buffer the last model state
       
   259     def RefreshBuffer(self):
       
   260         self.Controler.BufferPython()
       
   261         if self.ParentWindow is not None:
       
   262             self.ParentWindow.RefreshTitle()
       
   263             self.ParentWindow.RefreshFileMenu()
       
   264             self.ParentWindow.RefreshEditMenu()
       
   265             self.ParentWindow.RefreshPageTitles()
       
   266     
       
   267     def StartBuffering(self):
       
   268         self.Controler.StartBuffering()
       
   269         if self.ParentWindow is not None:
       
   270             self.ParentWindow.RefreshTitle()
       
   271             self.ParentWindow.RefreshFileMenu()
       
   272             self.ParentWindow.RefreshEditMenu()
       
   273             self.ParentWindow.RefreshPageTitles()
       
   274     
       
   275     def ResetBuffer(self):
       
   276         if self.CurrentAction != None:
       
   277             self.Controler.EndBuffering()
       
   278             self.CurrentAction = None
       
   279 
       
   280     def RefreshView(self):
       
   281         ConfTreeNodeEditor.RefreshView(self)
       
   282         
       
   283         self.ResetBuffer()
       
   284         self.DisableEvents = True
       
   285         old_cursor_pos = self.PythonCodeEditor.GetCurrentPos()
       
   286         line = self.PythonCodeEditor.GetFirstVisibleLine()
       
   287         column = self.PythonCodeEditor.GetXOffset()
       
   288         old_text = self.PythonCodeEditor.GetText()
       
   289         new_text = self.Controler.GetPythonCode()
       
   290         if old_text != new_text:
       
   291             self.PythonCodeEditor.SetText(new_text)
       
   292             new_cursor_pos = GetCursorPos(old_text, new_text)
       
   293             self.PythonCodeEditor.LineScroll(column, line)
       
   294             if new_cursor_pos != None:
       
   295                 self.PythonCodeEditor.GotoPos(new_cursor_pos)
       
   296             else:
       
   297                 self.PythonCodeEditor.GotoPos(old_cursor_pos)
       
   298             self.PythonCodeEditor.EmptyUndoBuffer()
       
   299         self.DisableEvents = False
       
   300         
       
   301         self.PythonCodeEditor.Colourise(0, -1)
       
   302         
       
   303         self.ShowHighlights()
       
   304 
       
   305     def RefreshModel(self):
       
   306         self.Controler.SetPythonCode(self.PythonCodeEditor.GetText())
       
   307 
       
   308     def OnKeyPressed(self, event):
       
   309         if self.PythonCodeEditor.CallTipActive():
       
   310             self.PythonCodeEditor.CallTipCancel()
       
   311         key = event.GetKeyCode()
       
   312 
       
   313         if key == 32 and event.ControlDown():
       
   314             pos = self.PythonCodeEditor.GetCurrentPos()
       
   315 
       
   316             # Code completion
       
   317             if not event.ShiftDown():
       
   318                 self.PythonCodeEditor.AutoCompSetIgnoreCase(False)  # so this needs to match
       
   319 
       
   320                 # Images are specified with a appended "?type"
       
   321                 self.PythonCodeEditor.AutoCompShow(0, " ".join([word + "?1" for word in keyword.kwlist]))
       
   322         else:
       
   323             event.Skip()
       
   324     
       
   325     def OnKillFocus(self, event):
       
   326         self.PythonCodeEditor.AutoCompCancel()
       
   327         event.Skip()
       
   328 
       
   329     def OnUpdateUI(self, evt):
       
   330         # check for matching braces
       
   331         braceAtCaret = -1
       
   332         braceOpposite = -1
       
   333         charBefore = None
       
   334         caretPos = self.PythonCodeEditor.GetCurrentPos()
       
   335 
       
   336         if caretPos > 0:
       
   337             charBefore = self.PythonCodeEditor.GetCharAt(caretPos - 1)
       
   338             styleBefore = self.PythonCodeEditor.GetStyleAt(caretPos - 1)
       
   339 
       
   340         # check before
       
   341         if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
       
   342             braceAtCaret = caretPos - 1
       
   343 
       
   344         # check after
       
   345         if braceAtCaret < 0:
       
   346             charAfter = self.PythonCodeEditor.GetCharAt(caretPos)
       
   347             styleAfter = self.PythonCodeEditor.GetStyleAt(caretPos)
       
   348 
       
   349             if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
       
   350                 braceAtCaret = caretPos
       
   351 
       
   352         if braceAtCaret >= 0:
       
   353             braceOpposite = self.PythonCodeEditor.BraceMatch(braceAtCaret)
       
   354 
       
   355         if braceAtCaret != -1  and braceOpposite == -1:
       
   356             self.PythonCodeEditor.BraceBadLight(braceAtCaret)
       
   357         else:
       
   358             self.PythonCodeEditor.BraceHighlight(braceAtCaret, braceOpposite)
       
   359 
       
   360     def OnMarginClick(self, evt):
       
   361         # fold and unfold as needed
       
   362         if evt.GetMargin() == 2:
       
   363             if evt.GetShift() and evt.GetControl():
       
   364                 self.FoldAll()
       
   365             else:
       
   366                 lineClicked = self.PythonCodeEditor.LineFromPosition(evt.GetPosition())
       
   367 
       
   368                 if self.PythonCodeEditor.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
       
   369                     if evt.GetShift():
       
   370                         self.PythonCodeEditor.SetFoldExpanded(lineClicked, True)
       
   371                         self.Expand(lineClicked, True, True, 1)
       
   372                     elif evt.GetControl():
       
   373                         if self.PythonCodeEditor.GetFoldExpanded(lineClicked):
       
   374                             self.PythonCodeEditor.SetFoldExpanded(lineClicked, False)
       
   375                             self.Expand(lineClicked, False, True, 0)
       
   376                         else:
       
   377                             self.PythonCodeEditor.SetFoldExpanded(lineClicked, True)
       
   378                             self.Expand(lineClicked, True, True, 100)
       
   379                     else:
       
   380                         self.PythonCodeEditor.ToggleFold(lineClicked)
       
   381 
       
   382 
       
   383     def FoldAll(self):
       
   384         lineCount = self.PythonCodeEditor.GetLineCount()
       
   385         expanding = True
       
   386 
       
   387         # find out if we are folding or unfolding
       
   388         for lineNum in range(lineCount):
       
   389             if self.PythonCodeEditor.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
       
   390                 expanding = not self.PythonCodeEditor.GetFoldExpanded(lineNum)
       
   391                 break
       
   392 
       
   393         lineNum = 0
       
   394 
       
   395         while lineNum < lineCount:
       
   396             level = self.PythonCodeEditor.GetFoldLevel(lineNum)
       
   397             if level & stc.STC_FOLDLEVELHEADERFLAG and \
       
   398                (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
       
   399 
       
   400                 if expanding:
       
   401                     self.PythonCodeEditor.SetFoldExpanded(lineNum, True)
       
   402                     lineNum = self.Expand(lineNum, True)
       
   403                     lineNum = lineNum - 1
       
   404                 else:
       
   405                     lastChild = self.PythonCodeEditor.GetLastChild(lineNum, -1)
       
   406                     self.PythonCodeEditor.SetFoldExpanded(lineNum, False)
       
   407 
       
   408                     if lastChild > lineNum:
       
   409                         self.PythonCodeEditor.HideLines(lineNum+1, lastChild)
       
   410 
       
   411             lineNum = lineNum + 1
       
   412 
       
   413 
       
   414 
       
   415     def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
       
   416         lastChild = self.PythonCodeEditor.GetLastChild(line, level)
       
   417         line = line + 1
       
   418 
       
   419         while line <= lastChild:
       
   420             if force:
       
   421                 if visLevels > 0:
       
   422                     self.PythonCodeEditor.ShowLines(line, line)
       
   423                 else:
       
   424                     self.PythonCodeEditor.HideLines(line, line)
       
   425             else:
       
   426                 if doExpand:
       
   427                     self.PythonCodeEditor.ShowLines(line, line)
       
   428 
       
   429             if level == -1:
       
   430                 level = self.PythonCodeEditor.GetFoldLevel(line)
       
   431 
       
   432             if level & stc.STC_FOLDLEVELHEADERFLAG:
       
   433                 if force:
       
   434                     if visLevels > 1:
       
   435                         self.PythonCodeEditor.SetFoldExpanded(line, True)
       
   436                     else:
       
   437                         self.PythonCodeEditor.SetFoldExpanded(line, False)
       
   438 
       
   439                     line = self.Expand(line, doExpand, force, visLevels-1)
       
   440 
       
   441                 else:
       
   442                     if doExpand and self.PythonCodeEditor.GetFoldExpanded(line):
       
   443                         line = self.Expand(line, True, force, visLevels-1)
       
   444                     else:
       
   445                         line = self.Expand(line, False, force, visLevels-1)
       
   446             else:
       
   447                 line = line + 1
       
   448 
       
   449         return line
       
   450 
       
   451     def Cut(self):
       
   452         self.ResetBuffer()
       
   453         self.DisableEvents = True
       
   454         self.PythonCodeEditor.CmdKeyExecute(wx.stc.STC_CMD_CUT)
       
   455         self.DisableEvents = False
       
   456         self.RefreshModel()
       
   457         self.RefreshBuffer()
       
   458     
       
   459     def Copy(self):
       
   460         self.PythonCodeEditor.CmdKeyExecute(wx.stc.STC_CMD_COPY)
       
   461     
       
   462     def Paste(self):
       
   463         self.ResetBuffer()
       
   464         self.DisableEvents = True
       
   465         self.PythonCodeEditor.CmdKeyExecute(wx.stc.STC_CMD_PASTE)
       
   466         self.DisableEvents = False
       
   467         self.RefreshModel()
       
   468         self.RefreshBuffer()
       
   469 
    68 
   470     def Find(self, direction, search_params):
    69     def Find(self, direction, search_params):
   471         if self.SearchParams != search_params:
    70         self.PythonCodeEditor.Find(direction, search_params)
   472             self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)
       
   473             
       
   474             self.SearchParams = search_params
       
   475             criteria = {
       
   476                 "raw_pattern": search_params["find_pattern"], 
       
   477                 "pattern": re.compile(search_params["find_pattern"]),
       
   478                 "case_sensitive": search_params["case_sensitive"],
       
   479                 "regular_expression": search_params["regular_expression"],
       
   480                 "filter": "all"}
       
   481             
       
   482             self.SearchResults = [
       
   483                 (start, end, SEARCH_RESULT_HIGHLIGHT)
       
   484                 for start, end, text in 
       
   485                 TestTextElement(self.PythonCodeEditor.GetText(), criteria)]
       
   486             self.CurrentFindHighlight = None
       
   487         
       
   488         if len(self.SearchResults) > 0:
       
   489             if self.CurrentFindHighlight is not None:
       
   490                 old_idx = self.SearchResults.index(self.CurrentFindHighlight)
       
   491                 if self.SearchParams["wrap"]:
       
   492                     idx = (old_idx + direction) % len(self.SearchResults)
       
   493                 else:
       
   494                     idx = max(0, min(old_idx + direction, len(self.SearchResults) - 1))
       
   495                 if idx != old_idx:
       
   496                     self.RemoveHighlight(*self.CurrentFindHighlight)
       
   497                     self.CurrentFindHighlight = self.SearchResults[idx]
       
   498                     self.AddHighlight(*self.CurrentFindHighlight)
       
   499             else:
       
   500                 self.CurrentFindHighlight = self.SearchResults[0]
       
   501                 self.AddHighlight(*self.CurrentFindHighlight)
       
   502             
       
   503         else:
       
   504             if self.CurrentFindHighlight is not None:
       
   505                 self.RemoveHighlight(*self.CurrentFindHighlight)
       
   506             self.CurrentFindHighlight = None
       
   507 
       
   508 #-------------------------------------------------------------------------------
       
   509 #                        Highlights showing functions
       
   510 #-------------------------------------------------------------------------------
       
   511 
       
   512     def OnRefreshHighlightsTimer(self, event):
       
   513         self.RefreshView()
       
   514         event.Skip()
       
   515 
       
   516     def ClearHighlights(self, highlight_type=None):
       
   517         if highlight_type is None:
       
   518             self.Highlights = []
       
   519         else:
       
   520             highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
       
   521             if highlight_type is not None:
       
   522                 self.Highlights = [(start, end, highlight) for (start, end, highlight) in self.Highlights if highlight != highlight_type]
       
   523         self.RefreshView()
       
   524 
       
   525     def AddHighlight(self, start, end, highlight_type):
       
   526         highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
       
   527         if highlight_type is not None:
       
   528             self.Highlights.append((start, end, highlight_type))
       
   529             self.PythonCodeEditor.GotoPos(self.PythonCodeEditor.PositionFromLine(start[0]) + start[1])
       
   530             self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
       
   531             self.RefreshView()
       
   532 
       
   533     def RemoveHighlight(self, start, end, highlight_type):
       
   534         highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
       
   535         if (highlight_type is not None and 
       
   536             (start, end, highlight_type) in self.Highlights):
       
   537             self.Highlights.remove((start, end, highlight_type))
       
   538             self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
       
   539     
       
   540     def ShowHighlights(self):
       
   541         for start, end, highlight_type in self.Highlights:
       
   542             if start[0] == 0:
       
   543                 highlight_start_pos = start[1]
       
   544             else:
       
   545                 highlight_start_pos = self.PythonCodeEditor.GetLineEndPosition(start[0] - 1) + start[1] + 1
       
   546             if end[0] == 0:
       
   547                 highlight_end_pos = end[1] - indent + 1
       
   548             else:
       
   549                 highlight_end_pos = self.PythonCodeEditor.GetLineEndPosition(end[0] - 1) + end[1] + 2
       
   550             self.PythonCodeEditor.StartStyling(highlight_start_pos, 0xff)
       
   551             self.PythonCodeEditor.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type)
       
   552             self.PythonCodeEditor.StartStyling(highlight_start_pos, 0x00)
       
   553             self.PythonCodeEditor.SetStyling(len(self.PythonCodeEditor.GetText()) - highlight_end_pos, stc.STC_STYLE_DEFAULT)
       
   554