Laurent@1057: import re Laurent@1057: import keyword Laurent@1057: laurent@738: import wx laurent@738: import wx.grid Laurent@1057: import wx.stc as stc Laurent@1057: Laurent@1057: from plcopen.plcopen import TestTextElement Laurent@1057: from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD Laurent@814: from editors.ConfTreeNodeEditor import ConfTreeNodeEditor Laurent@1060: from editors.TextViewer import GetCursorPos, faces laurent@657: Laurent@1057: [STC_PYTHON_ERROR, STC_PYTHON_SEARCH_RESULT] = range(15, 17) Laurent@1057: Laurent@1057: HIGHLIGHT_TYPES = { Laurent@1057: ERROR_HIGHLIGHT: STC_PYTHON_ERROR, Laurent@1057: SEARCH_RESULT_HIGHLIGHT: STC_PYTHON_SEARCH_RESULT, Laurent@1057: } Laurent@1057: laurent@366: [ID_PYTHONEDITOR, laurent@366: ] = [wx.NewId() for _init_ctrls in range(1)] laurent@366: laurent@738: class PythonEditor(ConfTreeNodeEditor): laurent@366: laurent@366: fold_symbols = 3 Laurent@920: CONFNODEEDITOR_TABS = [ Laurent@920: (_("Python code"), "_create_PythonCodeEditor")] Laurent@920: Laurent@920: def _create_PythonCodeEditor(self, prnt): Laurent@920: self.PythonCodeEditor = stc.StyledTextCtrl(id=ID_PYTHONEDITOR, parent=prnt, laurent@657: name="TextViewer", pos=wx.DefaultPosition, laurent@657: size=wx.DefaultSize, style=0) Laurent@920: self.PythonCodeEditor.ParentWindow = self Laurent@920: Laurent@920: self.PythonCodeEditor.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) Laurent@920: self.PythonCodeEditor.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) Laurent@920: Laurent@920: self.PythonCodeEditor.SetLexer(stc.STC_LEX_PYTHON) Laurent@920: self.PythonCodeEditor.SetKeyWords(0, " ".join(keyword.kwlist)) Laurent@920: Laurent@920: self.PythonCodeEditor.SetProperty("fold", "1") Laurent@920: self.PythonCodeEditor.SetProperty("tab.timmy.whinge.level", "1") Laurent@920: self.PythonCodeEditor.SetMargins(0,0) Laurent@920: Laurent@920: self.PythonCodeEditor.SetViewWhiteSpace(False) Laurent@920: Laurent@920: self.PythonCodeEditor.SetEdgeMode(stc.STC_EDGE_BACKGROUND) Laurent@920: self.PythonCodeEditor.SetEdgeColumn(78) laurent@366: laurent@366: # Set up the numbers in the margin for margin #1 Laurent@920: self.PythonCodeEditor.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER) laurent@366: # Reasonable value for, say, 4-5 digits using a mono font (40 pix) Laurent@920: self.PythonCodeEditor.SetMarginWidth(1, 40) laurent@366: laurent@366: # Setup a margin to hold fold markers Laurent@920: self.PythonCodeEditor.SetMarginType(2, stc.STC_MARGIN_SYMBOL) Laurent@920: self.PythonCodeEditor.SetMarginMask(2, stc.STC_MASK_FOLDERS) Laurent@920: self.PythonCodeEditor.SetMarginSensitive(2, True) Laurent@920: self.PythonCodeEditor.SetMarginWidth(2, 12) laurent@366: laurent@366: if self.fold_symbols == 0: laurent@366: # Arrow pointing right for contracted folders, arrow pointing down for expanded Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black") laurent@366: laurent@366: elif self.fold_symbols == 1: laurent@366: # Plus for contracted folders, minus for expanded Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black") laurent@366: laurent@366: elif self.fold_symbols == 2: laurent@366: # Like a flattened tree control using circular headers and curved joins Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040") laurent@366: laurent@366: elif self.fold_symbols == 3: laurent@366: # Like a flattened tree control using square headers Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080") Laurent@920: self.PythonCodeEditor.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080") Laurent@920: Laurent@920: Laurent@920: self.PythonCodeEditor.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI) Laurent@920: self.PythonCodeEditor.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick) Laurent@920: self.PythonCodeEditor.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed) Laurent@1060: laurent@366: # Global default style laurent@366: if wx.Platform == '__WXMSW__': Laurent@920: self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Courier New') laurent@366: elif wx.Platform == '__WXMAC__': laurent@366: # TODO: if this looks fine on Linux too, remove the Mac-specific case laurent@366: # and use this whenever OS != MSW. Laurent@920: self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Monaco') laurent@366: else: laurent@366: defsize = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT).GetPointSize() Laurent@920: self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Courier,size:%d'%defsize) laurent@366: laurent@366: # Clear styles and revert to default. Laurent@920: self.PythonCodeEditor.StyleClearAll() laurent@366: laurent@366: # Following style specs only indicate differences from default. laurent@366: # The rest remains unchanged. laurent@366: laurent@366: # Line numbers in margin Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2,size:%(size)d' % faces) laurent@366: # Highlighted brace Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00,size:%(size)d' % faces) laurent@366: # Unmatched brace Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000,size:%(size)d' % faces) laurent@366: # Indentation guide Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_STYLE_INDENTGUIDE, 'fore:#CDCDCD,size:%(size)d' % faces) laurent@366: laurent@366: # Python styles Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_DEFAULT, 'fore:#000000,size:%(size)d' % faces) laurent@366: # Comments Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0,size:%(size)d' % faces) Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0,size:%(size)d' % faces) laurent@366: # Numbers Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_NUMBER, 'fore:#008080,size:%(size)d' % faces) laurent@366: # Strings and characters Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_STRING, 'fore:#800080,size:%(size)d' % faces) Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_CHARACTER, 'fore:#800080,size:%(size)d' % faces) laurent@366: # Keywords Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_WORD, 'fore:#000080,bold,size:%(size)d' % faces) laurent@366: # Triple quotes Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA,size:%(size)d' % faces) Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA,size:%(size)d' % faces) laurent@366: # Class names Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_CLASSNAME, 'fore:#0000FF,bold,size:%(size)d' % faces) laurent@366: # Function names Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_DEFNAME, 'fore:#008080,bold,size:%(size)d' % faces) laurent@366: # Operators Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_OPERATOR, 'fore:#800000,bold,size:%(size)d' % faces) laurent@366: # Identifiers. I leave this as not bold because everything seems laurent@366: # to be an identifier if it doesn't match the above criterae Laurent@1058: self.PythonCodeEditor.StyleSetSpec(stc.STC_P_IDENTIFIER, 'fore:#000000,size:%(size)d' % faces) Laurent@1057: Laurent@1057: # Highlighting styles Laurent@1058: self.PythonCodeEditor.StyleSetSpec(STC_PYTHON_ERROR, 'fore:#FF0000,back:#FFFF00,size:%(size)d' % faces) Laurent@1058: self.PythonCodeEditor.StyleSetSpec(STC_PYTHON_SEARCH_RESULT, 'fore:#FFFFFF,back:#FFA500,size:%(size)d' % faces) Laurent@1057: laurent@366: # Caret color Laurent@920: self.PythonCodeEditor.SetCaretForeground("BLUE") laurent@366: # Selection background Laurent@920: self.PythonCodeEditor.SetSelBackground(1, '#66CCFF') Laurent@920: Laurent@920: self.PythonCodeEditor.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)) Laurent@920: self.PythonCodeEditor.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)) laurent@366: laurent@366: # register some images for use in the AutoComplete box. laurent@366: #self.RegisterImage(1, images.getSmilesBitmap()) Laurent@920: self.PythonCodeEditor.RegisterImage(1, laurent@366: wx.ArtProvider.GetBitmap(wx.ART_DELETE, size=(16,16))) Laurent@920: self.PythonCodeEditor.RegisterImage(2, laurent@366: wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16))) Laurent@920: self.PythonCodeEditor.RegisterImage(3, laurent@366: wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16))) laurent@366: laurent@366: # Indentation and tab stuff Laurent@920: self.PythonCodeEditor.SetIndent(4) # Proscribed indent size for wx Laurent@920: self.PythonCodeEditor.SetIndentationGuides(True) # Show indent guides Laurent@920: self.PythonCodeEditor.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space Laurent@920: self.PythonCodeEditor.SetTabIndents(True) # Tab key indents Laurent@920: self.PythonCodeEditor.SetTabWidth(4) # Proscribed tab size for wx Laurent@920: self.PythonCodeEditor.SetUseTabs(False) # Use spaces rather than tabs, or laurent@366: # TabTimmy will complain! laurent@366: # White space Laurent@920: self.PythonCodeEditor.SetViewWhiteSpace(False) # Don't view white space laurent@366: laurent@366: # EOL: Since we are loading/saving ourselves, and the laurent@366: # strings will always have \n's in them, set the STC to laurent@366: # edit them that way. Laurent@1057: self.PythonCodeEditor.SetEOLMode(stc.STC_EOL_LF) Laurent@920: self.PythonCodeEditor.SetViewEOL(False) laurent@366: laurent@366: # No right-edge mode indicator Laurent@920: self.PythonCodeEditor.SetEdgeMode(stc.STC_EDGE_NONE) Laurent@920: Laurent@1057: self.PythonCodeEditor.SetModEventMask(stc.STC_MOD_BEFOREINSERT|stc.STC_MOD_BEFOREDELETE) Laurent@920: Laurent@920: self.PythonCodeEditor.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_PYTHONEDITOR) Laurent@920: self.PythonCodeEditor.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) Laurent@920: self.PythonCodeEditor.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_PYTHONEDITOR) Laurent@920: Laurent@920: return self.PythonCodeEditor laurent@657: laurent@657: def __init__(self, parent, controler, window): laurent@743: ConfTreeNodeEditor.__init__(self, parent, controler, window) laurent@657: laurent@657: self.DisableEvents = False laurent@366: self.CurrentAction = None laurent@657: Laurent@1057: self.Highlights = [] Laurent@1057: self.SearchParams = None Laurent@1057: self.SearchResults = None Laurent@1057: self.CurrentFindHighlight = None Laurent@1057: Laurent@1057: self.RefreshHighlightsTimer = wx.Timer(self, -1) Laurent@1057: self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) Laurent@1057: laurent@657: def GetBufferState(self): laurent@657: return self.Controler.GetBufferState() laurent@657: laurent@657: def Undo(self): laurent@657: self.Controler.LoadPrevious() laurent@657: self.RefreshView() laurent@657: laurent@657: def Redo(self): laurent@657: self.Controler.LoadNext() laurent@657: self.RefreshView() laurent@657: laurent@366: def OnModification(self, event): laurent@366: if not self.DisableEvents: laurent@366: mod_type = event.GetModificationType() laurent@366: if not (mod_type&wx.stc.STC_PERFORMED_UNDO or mod_type&wx.stc.STC_PERFORMED_REDO): laurent@366: if mod_type&wx.stc.STC_MOD_BEFOREINSERT: laurent@657: if self.CurrentAction is None: laurent@366: self.StartBuffering() laurent@366: elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1: laurent@366: self.Controler.EndBuffering() laurent@366: self.StartBuffering() laurent@366: self.CurrentAction = ("Add", event.GetPosition()) laurent@657: wx.CallAfter(self.RefreshModel) laurent@366: elif mod_type&wx.stc.STC_MOD_BEFOREDELETE: laurent@366: if self.CurrentAction == None: laurent@366: self.StartBuffering() laurent@366: elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1: laurent@366: self.Controler.EndBuffering() laurent@366: self.StartBuffering() laurent@366: self.CurrentAction = ("Delete", event.GetPosition()) laurent@657: wx.CallAfter(self.RefreshModel) laurent@366: event.Skip() laurent@366: laurent@366: def OnDoDrop(self, event): laurent@366: self.ResetBuffer() laurent@366: wx.CallAfter(self.RefreshModel) laurent@366: event.Skip() laurent@366: laurent@366: # Buffer the last model state laurent@366: def RefreshBuffer(self): laurent@366: self.Controler.BufferPython() laurent@657: if self.ParentWindow is not None: laurent@366: self.ParentWindow.RefreshTitle() laurent@534: self.ParentWindow.RefreshFileMenu() laurent@366: self.ParentWindow.RefreshEditMenu() laurent@657: self.ParentWindow.RefreshPageTitles() laurent@366: laurent@366: def StartBuffering(self): laurent@366: self.Controler.StartBuffering() laurent@657: if self.ParentWindow is not None: laurent@366: self.ParentWindow.RefreshTitle() laurent@534: self.ParentWindow.RefreshFileMenu() laurent@366: self.ParentWindow.RefreshEditMenu() laurent@657: self.ParentWindow.RefreshPageTitles() laurent@366: laurent@366: def ResetBuffer(self): laurent@366: if self.CurrentAction != None: laurent@366: self.Controler.EndBuffering() laurent@366: self.CurrentAction = None laurent@366: laurent@366: def RefreshView(self): laurent@751: ConfTreeNodeEditor.RefreshView(self) laurent@751: laurent@366: self.ResetBuffer() laurent@366: self.DisableEvents = True Laurent@920: old_cursor_pos = self.PythonCodeEditor.GetCurrentPos() Laurent@1060: line = self.PythonCodeEditor.GetFirstVisibleLine() Laurent@1060: column = self.PythonCodeEditor.GetXOffset() Laurent@920: old_text = self.PythonCodeEditor.GetText() laurent@366: new_text = self.Controler.GetPythonCode() Laurent@1060: if old_text != new_text: Laurent@1060: self.PythonCodeEditor.SetText(new_text) Laurent@1060: new_cursor_pos = GetCursorPos(old_text, new_text) Laurent@1060: self.PythonCodeEditor.LineScroll(column, line) Laurent@1060: if new_cursor_pos != None: Laurent@1060: self.PythonCodeEditor.GotoPos(new_cursor_pos) Laurent@1060: else: Laurent@1060: self.PythonCodeEditor.GotoPos(old_cursor_pos) Laurent@1060: self.PythonCodeEditor.EmptyUndoBuffer() laurent@366: self.DisableEvents = False laurent@366: Laurent@920: self.PythonCodeEditor.Colourise(0, -1) Laurent@1057: Laurent@1057: self.ShowHighlights() laurent@366: laurent@366: def RefreshModel(self): Laurent@920: self.Controler.SetPythonCode(self.PythonCodeEditor.GetText()) laurent@366: laurent@366: def OnKeyPressed(self, event): Laurent@920: if self.PythonCodeEditor.CallTipActive(): Laurent@920: self.PythonCodeEditor.CallTipCancel() laurent@366: key = event.GetKeyCode() laurent@366: laurent@366: if key == 32 and event.ControlDown(): Laurent@920: pos = self.PythonCodeEditor.GetCurrentPos() laurent@738: laurent@366: # Code completion laurent@738: if not event.ShiftDown(): Laurent@920: self.PythonCodeEditor.AutoCompSetIgnoreCase(False) # so this needs to match laurent@366: laurent@366: # Images are specified with a appended "?type" Laurent@920: self.PythonCodeEditor.AutoCompShow(0, " ".join([word + "?1" for word in keyword.kwlist])) laurent@366: else: laurent@366: event.Skip() Laurent@1060: laurent@366: def OnKillFocus(self, event): Laurent@920: self.PythonCodeEditor.AutoCompCancel() laurent@366: event.Skip() laurent@366: laurent@366: def OnUpdateUI(self, evt): laurent@366: # check for matching braces laurent@366: braceAtCaret = -1 laurent@366: braceOpposite = -1 laurent@366: charBefore = None Laurent@920: caretPos = self.PythonCodeEditor.GetCurrentPos() laurent@366: laurent@366: if caretPos > 0: Laurent@920: charBefore = self.PythonCodeEditor.GetCharAt(caretPos - 1) Laurent@920: styleBefore = self.PythonCodeEditor.GetStyleAt(caretPos - 1) laurent@366: laurent@366: # check before laurent@366: if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR: laurent@366: braceAtCaret = caretPos - 1 laurent@366: laurent@366: # check after laurent@366: if braceAtCaret < 0: Laurent@920: charAfter = self.PythonCodeEditor.GetCharAt(caretPos) Laurent@920: styleAfter = self.PythonCodeEditor.GetStyleAt(caretPos) laurent@366: laurent@366: if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR: laurent@366: braceAtCaret = caretPos laurent@366: laurent@366: if braceAtCaret >= 0: Laurent@920: braceOpposite = self.PythonCodeEditor.BraceMatch(braceAtCaret) laurent@366: laurent@366: if braceAtCaret != -1 and braceOpposite == -1: Laurent@920: self.PythonCodeEditor.BraceBadLight(braceAtCaret) Laurent@920: else: Laurent@920: self.PythonCodeEditor.BraceHighlight(braceAtCaret, braceOpposite) laurent@366: laurent@366: def OnMarginClick(self, evt): laurent@366: # fold and unfold as needed laurent@366: if evt.GetMargin() == 2: laurent@366: if evt.GetShift() and evt.GetControl(): laurent@366: self.FoldAll() laurent@366: else: Laurent@920: lineClicked = self.PythonCodeEditor.LineFromPosition(evt.GetPosition()) Laurent@920: Laurent@920: if self.PythonCodeEditor.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG: laurent@366: if evt.GetShift(): Laurent@920: self.PythonCodeEditor.SetFoldExpanded(lineClicked, True) laurent@366: self.Expand(lineClicked, True, True, 1) laurent@366: elif evt.GetControl(): Laurent@920: if self.PythonCodeEditor.GetFoldExpanded(lineClicked): Laurent@920: self.PythonCodeEditor.SetFoldExpanded(lineClicked, False) laurent@366: self.Expand(lineClicked, False, True, 0) laurent@366: else: Laurent@920: self.PythonCodeEditor.SetFoldExpanded(lineClicked, True) laurent@366: self.Expand(lineClicked, True, True, 100) laurent@366: else: Laurent@920: self.PythonCodeEditor.ToggleFold(lineClicked) laurent@366: laurent@366: laurent@366: def FoldAll(self): Laurent@920: lineCount = self.PythonCodeEditor.GetLineCount() laurent@366: expanding = True laurent@366: laurent@366: # find out if we are folding or unfolding laurent@366: for lineNum in range(lineCount): Laurent@920: if self.PythonCodeEditor.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG: Laurent@920: expanding = not self.PythonCodeEditor.GetFoldExpanded(lineNum) laurent@366: break laurent@366: laurent@366: lineNum = 0 laurent@366: laurent@366: while lineNum < lineCount: Laurent@920: level = self.PythonCodeEditor.GetFoldLevel(lineNum) laurent@366: if level & stc.STC_FOLDLEVELHEADERFLAG and \ laurent@366: (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE: laurent@366: laurent@366: if expanding: Laurent@920: self.PythonCodeEditor.SetFoldExpanded(lineNum, True) laurent@366: lineNum = self.Expand(lineNum, True) laurent@366: lineNum = lineNum - 1 laurent@366: else: Laurent@920: lastChild = self.PythonCodeEditor.GetLastChild(lineNum, -1) Laurent@920: self.PythonCodeEditor.SetFoldExpanded(lineNum, False) laurent@366: laurent@366: if lastChild > lineNum: Laurent@920: self.PythonCodeEditor.HideLines(lineNum+1, lastChild) laurent@366: laurent@366: lineNum = lineNum + 1 laurent@366: laurent@366: laurent@366: laurent@366: def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): Laurent@920: lastChild = self.PythonCodeEditor.GetLastChild(line, level) laurent@366: line = line + 1 laurent@366: laurent@366: while line <= lastChild: laurent@366: if force: laurent@366: if visLevels > 0: Laurent@920: self.PythonCodeEditor.ShowLines(line, line) laurent@366: else: Laurent@920: self.PythonCodeEditor.HideLines(line, line) laurent@366: else: laurent@366: if doExpand: Laurent@920: self.PythonCodeEditor.ShowLines(line, line) laurent@366: laurent@366: if level == -1: Laurent@920: level = self.PythonCodeEditor.GetFoldLevel(line) laurent@366: laurent@366: if level & stc.STC_FOLDLEVELHEADERFLAG: laurent@366: if force: laurent@366: if visLevels > 1: Laurent@920: self.PythonCodeEditor.SetFoldExpanded(line, True) laurent@366: else: Laurent@920: self.PythonCodeEditor.SetFoldExpanded(line, False) laurent@366: laurent@366: line = self.Expand(line, doExpand, force, visLevels-1) laurent@366: laurent@366: else: Laurent@920: if doExpand and self.PythonCodeEditor.GetFoldExpanded(line): laurent@366: line = self.Expand(line, True, force, visLevels-1) laurent@366: else: laurent@366: line = self.Expand(line, False, force, visLevels-1) laurent@366: else: laurent@366: line = line + 1 laurent@366: laurent@366: return line laurent@657: laurent@657: def Cut(self): laurent@657: self.ResetBuffer() laurent@657: self.DisableEvents = True Laurent@920: self.PythonCodeEditor.CmdKeyExecute(wx.stc.STC_CMD_CUT) laurent@657: self.DisableEvents = False laurent@657: self.RefreshModel() laurent@657: self.RefreshBuffer() laurent@657: laurent@657: def Copy(self): Laurent@920: self.PythonCodeEditor.CmdKeyExecute(wx.stc.STC_CMD_COPY) laurent@657: laurent@657: def Paste(self): laurent@657: self.ResetBuffer() laurent@657: self.DisableEvents = True Laurent@920: self.PythonCodeEditor.CmdKeyExecute(wx.stc.STC_CMD_PASTE) laurent@657: self.DisableEvents = False laurent@657: self.RefreshModel() laurent@657: self.RefreshBuffer() Laurent@1057: Laurent@1057: def Find(self, direction, search_params): Laurent@1057: if self.SearchParams != search_params: Laurent@1057: self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT) Laurent@1057: Laurent@1057: self.SearchParams = search_params Laurent@1057: criteria = { Laurent@1057: "raw_pattern": search_params["find_pattern"], Laurent@1057: "pattern": re.compile(search_params["find_pattern"]), Laurent@1057: "case_sensitive": search_params["case_sensitive"], Laurent@1057: "regular_expression": search_params["regular_expression"], Laurent@1057: "filter": "all"} Laurent@1057: Laurent@1057: self.SearchResults = [ Laurent@1057: (start, end, SEARCH_RESULT_HIGHLIGHT) Laurent@1057: for start, end, text in Laurent@1057: TestTextElement(self.PythonCodeEditor.GetText(), criteria)] Laurent@1057: self.CurrentFindHighlight = None Laurent@1057: Laurent@1057: if len(self.SearchResults) > 0: Laurent@1057: if self.CurrentFindHighlight is not None: Laurent@1057: old_idx = self.SearchResults.index(self.CurrentFindHighlight) Laurent@1057: if self.SearchParams["wrap"]: Laurent@1057: idx = (old_idx + direction) % len(self.SearchResults) Laurent@1057: else: Laurent@1057: idx = max(0, min(old_idx + direction, len(self.SearchResults) - 1)) Laurent@1057: if idx != old_idx: Laurent@1057: self.RemoveHighlight(*self.CurrentFindHighlight) Laurent@1057: self.CurrentFindHighlight = self.SearchResults[idx] Laurent@1057: self.AddHighlight(*self.CurrentFindHighlight) Laurent@1057: else: Laurent@1057: self.CurrentFindHighlight = self.SearchResults[0] Laurent@1057: self.AddHighlight(*self.CurrentFindHighlight) Laurent@1057: Laurent@1057: else: Laurent@1057: if self.CurrentFindHighlight is not None: Laurent@1057: self.RemoveHighlight(*self.CurrentFindHighlight) Laurent@1057: self.CurrentFindHighlight = None Laurent@1057: Laurent@1057: #------------------------------------------------------------------------------- Laurent@1057: # Highlights showing functions Laurent@1057: #------------------------------------------------------------------------------- Laurent@1057: Laurent@1057: def OnRefreshHighlightsTimer(self, event): Laurent@1057: self.RefreshView() Laurent@1057: event.Skip() Laurent@1057: Laurent@1057: def ClearHighlights(self, highlight_type=None): Laurent@1057: if highlight_type is None: Laurent@1057: self.Highlights = [] Laurent@1057: else: Laurent@1057: highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None) Laurent@1057: if highlight_type is not None: Laurent@1057: self.Highlights = [(start, end, highlight) for (start, end, highlight) in self.Highlights if highlight != highlight_type] Laurent@1057: self.RefreshView() Laurent@1057: Laurent@1057: def AddHighlight(self, start, end, highlight_type): Laurent@1057: highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None) Laurent@1057: if highlight_type is not None: Laurent@1057: self.Highlights.append((start, end, highlight_type)) Laurent@1057: self.PythonCodeEditor.GotoPos(self.PythonCodeEditor.PositionFromLine(start[0]) + start[1]) Laurent@1057: self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) Laurent@1057: self.RefreshView() Laurent@1057: Laurent@1057: def RemoveHighlight(self, start, end, highlight_type): Laurent@1057: highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None) Laurent@1057: if (highlight_type is not None and Laurent@1057: (start, end, highlight_type) in self.Highlights): Laurent@1057: self.Highlights.remove((start, end, highlight_type)) Laurent@1057: self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) Laurent@1057: Laurent@1057: def ShowHighlights(self): Laurent@1057: for start, end, highlight_type in self.Highlights: Laurent@1057: if start[0] == 0: Laurent@1057: highlight_start_pos = start[1] Laurent@1057: else: Laurent@1057: highlight_start_pos = self.PythonCodeEditor.GetLineEndPosition(start[0] - 1) + start[1] + 1 Laurent@1057: if end[0] == 0: Laurent@1057: highlight_end_pos = end[1] - indent + 1 Laurent@1057: else: Laurent@1057: highlight_end_pos = self.PythonCodeEditor.GetLineEndPosition(end[0] - 1) + end[1] + 2 Laurent@1057: self.PythonCodeEditor.StartStyling(highlight_start_pos, 0xff) Laurent@1057: self.PythonCodeEditor.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type) Laurent@1057: self.PythonCodeEditor.StartStyling(highlight_start_pos, 0x00) Laurent@1057: self.PythonCodeEditor.SetStyling(len(self.PythonCodeEditor.GetText()) - highlight_end_pos, stc.STC_STYLE_DEFAULT) Laurent@1057: