Esempio n. 1
0
class TextViewer(EditorPanel):

    ID = ID_TEXTVIEWER

    if wx.VERSION < (2, 6, 0):
        def Bind(self, event, function, id=None):
            if id is not None:
                event(self, id, function)
            else:
                event(self, function)

    def _init_Editor(self, prnt):
        self.Editor = CustomStyledTextCtrl(id=ID_TEXTVIEWERTEXTCTRL,
                                           parent=prnt, name="TextViewer", size=wx.Size(0, 0), style=0)
        self.Editor.ParentWindow = self

        self.Editor.CmdKeyAssign(ord('+'), wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMIN)
        self.Editor.CmdKeyAssign(ord('-'), wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMOUT)

        self.Editor.SetViewWhiteSpace(False)

        self.Editor.SetLexer(wx.stc.STC_LEX_CONTAINER)

        # Global default styles for all languages
        self.Editor.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
        self.Editor.StyleClearAll()  # Reset all to be like the default

        self.Editor.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,  "back:#C0C0C0,size:%(size)d" % faces)
        self.Editor.SetSelBackground(1, "#E0E0E0")

        # Highlighting styles
        self.Editor.StyleSetSpec(STC_PLC_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_VARIABLE, "fore:#7F0000,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_PARAMETER, "fore:#7F007F,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_FUNCTION, "fore:#7F7F00,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_COMMENT, "fore:#7F7F7F,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_STRING, "fore:#007F00,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_JUMP, "fore:#FF7FFF,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_ERROR, "fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)
        self.Editor.StyleSetSpec(STC_PLC_SEARCH_RESULT, "fore:#FFFFFF,back:#FFA500,size:%(size)d" % faces)

        # Indicators styles
        self.Editor.IndicatorSetStyle(0, wx.stc.STC_INDIC_SQUIGGLE)
        if self.ParentWindow is not None and self.Controler is not None:
            self.Editor.IndicatorSetForeground(0, wx.RED)
        else:
            self.Editor.IndicatorSetForeground(0, wx.WHITE)

        # Line numbers in the margin
        self.Editor.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
        self.Editor.SetMarginWidth(1, 50)

        # Folding
        self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPEN,    wx.stc.STC_MARK_BOXMINUS,          "white", "#808080")
        self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDER,        wx.stc.STC_MARK_BOXPLUS,           "white", "#808080")
        self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERSUB,     wx.stc.STC_MARK_VLINE,             "white", "#808080")
        self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERTAIL,    wx.stc.STC_MARK_LCORNER,           "white", "#808080")
        self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEREND,     wx.stc.STC_MARK_BOXPLUSCONNECTED,  "white", "#808080")
        self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPENMID, wx.stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
        self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERMIDTAIL, wx.stc.STC_MARK_TCORNER,           "white", "#808080")

        # Indentation size
        self.Editor.SetTabWidth(2)
        self.Editor.SetUseTabs(0)

        self.Editor.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT |
                                    wx.stc.STC_MOD_BEFOREDELETE |
                                    wx.stc.STC_PERFORMED_USER)

        self.Bind(wx.stc.EVT_STC_STYLENEEDED, self.OnStyleNeeded, id=ID_TEXTVIEWERTEXTCTRL)
        self.Editor.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
        self.Editor.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
        self.Editor.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        if self.Controler is not None:
            self.Editor.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
            self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_TEXTVIEWERTEXTCTRL)
            self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_TEXTVIEWERTEXTCTRL)

    def __init__(self, parent, tagname, window, controler, debug=False, instancepath=""):
        if tagname != "" and controler is not None:
            self.VARIABLE_PANEL_TYPE = controler.GetPouType(tagname.split("::")[1])

        EditorPanel.__init__(self, parent, tagname, window, controler, debug)

        self.Keywords = []
        self.Variables = {}
        self.Functions = {}
        self.TypeNames = []
        self.Jumps = []
        self.EnumeratedValues = []
        self.DisableEvents = True
        self.TextSyntax = None
        self.CurrentAction = None

        self.InstancePath = instancepath
        self.ContextStack = []
        self.CallStack = []

        self.ResetSearchResults()

        self.RefreshHighlightsTimer = wx.Timer(self, -1)
        self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)

    def __del__(self):
        self.RefreshHighlightsTimer.Stop()

    def GetTitle(self):
        if self.Debug or self.TagName == "":
            if len(self.InstancePath) > 15:
                return "..." + self.InstancePath[-12:]
            return self.InstancePath
        return EditorPanel.GetTitle(self)

    def GetInstancePath(self):
        return self.InstancePath

    def IsViewing(self, tagname):
        if self.Debug or self.TagName == "":
            return self.InstancePath == tagname
        else:
            return self.TagName == tagname

    def GetText(self):
        return self.Editor.GetText()

    def SetText(self, text):
        self.Editor.SetText(text)

    def SelectAll(self):
        self.Editor.SelectAll()

    def Colourise(self, start, end):
        self.Editor.Colourise(start, end)

    def StartStyling(self, pos, mask):
        self.Editor.StartStyling(pos, mask)

    def SetStyling(self, length, style):
        self.Editor.SetStyling(length, style)

    def GetCurrentPos(self):
        return self.Editor.GetCurrentPos()

    def ResetSearchResults(self):
        self.Highlights = []
        self.SearchParams = None
        self.SearchResults = None
        self.CurrentFindHighlight = None

    def OnModification(self, event):
        if not self.DisableEvents:
            mod_type = event.GetModificationType()
            if mod_type & wx.stc.STC_MOD_BEFOREINSERT:
                if self.CurrentAction is None:
                    self.StartBuffering()
                elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1:
                    self.Controler.EndBuffering()
                    self.StartBuffering()
                self.CurrentAction = ("Add", event.GetPosition())
                wx.CallAfter(self.RefreshModel)
            elif mod_type & wx.stc.STC_MOD_BEFOREDELETE:
                if self.CurrentAction is None:
                    self.StartBuffering()
                elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1:
                    self.Controler.EndBuffering()
                    self.StartBuffering()
                self.CurrentAction = ("Delete", event.GetPosition())
                wx.CallAfter(self.RefreshModel)
        event.Skip()

    def OnDoDrop(self, event):
        try:
            values = eval(event.GetDragText())
        except Exception:
            values = event.GetDragText()
        if isinstance(values, tuple):
            message = None
            if values[1] in ["program", "debug"]:
                event.SetDragText("")
            elif values[1] in ["functionBlock", "function"]:
                blocktype = values[0]
                blockname = values[2]
                if len(values) > 3:
                    blockinputs = values[3]
                else:
                    blockinputs = None
                if values[1] != "function":
                    if blockname == "":
                        dialog = wx.TextEntryDialog(self.ParentWindow, _("Block name"), _("Please enter a block name"), "", wx.OK | wx.CANCEL | wx.CENTRE)
                        if dialog.ShowModal() == wx.ID_OK:
                            blockname = dialog.GetValue()
                        else:
                            event.SetDragText("")
                            return
                        dialog.Destroy()
                    if blockname.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]:
                        message = _("\"%s\" pou already exists!") % blockname
                    elif blockname.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]:
                        message = _("\"%s\" element for this pou already exists!") % blockname
                    else:
                        self.Controler.AddEditedElementPouVar(self.TagName, values[0], blockname)
                        self.RefreshVariablePanel()
                        self.RefreshVariableTree()
                blockinfo = self.Controler.GetBlockType(blocktype, blockinputs, self.Debug)
                hint = ',\n    '.join(
                            [" " + fctdecl[0]+" := (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["inputs"]] +
                            [" " + fctdecl[0]+" => (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["outputs"]])
                if values[1] == "function":
                    event.SetDragText(blocktype+"(\n    "+hint+")")
                else:
                    event.SetDragText(blockname+"(\n    "+hint+")")
            elif values[1] == "location":
                pou_name, pou_type = self.Controler.GetEditedElementType(self.TagName, self.Debug)
                if len(values) > 2 and pou_type == "program":
                    var_name = values[3]
                    dlg = wx.TextEntryDialog(
                        self.ParentWindow,
                        _("Confirm or change variable name"),
                        _('Variable Drop'), var_name)
                    dlg.SetValue(var_name)
                    var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None
                    dlg.Destroy()
                    if var_name is None:
                        return
                    elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]:
                        message = _("\"%s\" pou already exists!") % var_name
                    elif var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]:
                        message = _("\"%s\" element for this pou already exists!") % var_name
                    else:
                        location = values[0]
                        if not location.startswith("%"):
                            dialog = wx.SingleChoiceDialog(
                                self.ParentWindow,
                                _("Select a variable class:"),
                                _("Variable class"),
                                [_("Input"), _("Output"), _("Memory")],
                                wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL)
                            if dialog.ShowModal() == wx.ID_OK:
                                selected = dialog.GetSelection()
                            else:
                                selected = None
                            dialog.Destroy()
                            if selected is None:
                                event.SetDragText("")
                                return
                            if selected == 0:
                                location = "%I" + location
                            elif selected == 1:
                                location = "%Q" + location
                            else:
                                location = "%M" + location
                        if values[2] is not None:
                            var_type = values[2]
                        else:
                            var_type = LOCATIONDATATYPES.get(location[2], ["BOOL"])[0]
                        self.Controler.AddEditedElementPouVar(
                            self.TagName,
                            var_type, var_name,
                            location=location, description=values[4])
                        self.RefreshVariablePanel()
                        self.RefreshVariableTree()
                        event.SetDragText(var_name)
                else:
                    event.SetDragText("")
            elif values[1] == "NamedConstant":
                pou_name, pou_type = self.Controler.GetEditedElementType(self.TagName, self.Debug)
                if pou_type == "program":
                    initval = values[0]
                    var_name = values[3]
                    dlg = wx.TextEntryDialog(
                        self.ParentWindow,
                        _("Confirm or change variable name"),
                        _('Variable Drop'), var_name)
                    dlg.SetValue(var_name)
                    var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None
                    dlg.Destroy()
                    if var_name is None:
                        return
                    elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]:
                        message = _("\"%s\" pou already exists!") % var_name
                    else:
                        var_type = values[2]
                        if not var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]:
                            self.Controler.AddEditedElementPouVar(self.TagName,
                                                                  var_type,
                                                                  var_name,
                                                                  description=values[4], initval=initval)
                            self.RefreshVariablePanel()
                            self.RefreshVariableTree()
                        event.SetDragText(var_name)
            elif values[1] == "Global":
                var_name = values[0]
                dlg = wx.TextEntryDialog(
                    self.ParentWindow,
                    _("Confirm or change variable name"),
                    _('Variable Drop'), var_name)
                dlg.SetValue(var_name)
                var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None
                dlg.Destroy()
                if var_name is None:
                    return
                elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]:
                    message = _("\"%s\" pou already exists!") % var_name
                else:
                    if not var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]:
                        self.Controler.AddEditedElementPouExternalVar(self.TagName, values[2], var_name)
                        self.RefreshVariablePanel()
                        self.RefreshVariableTree()
                    event.SetDragText(var_name)
            elif values[1] == "Constant":
                event.SetDragText(values[0])
            elif values[3] == self.TagName:
                self.ResetBuffer()
                event.SetDragText(values[0])
                wx.CallAfter(self.RefreshModel)
            else:
                message = _("Variable don't belong to this POU!")
            if message is not None:
                dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR)
                dialog.ShowModal()
                dialog.Destroy()
                event.SetDragText("")
        event.Skip()

    def SetTextSyntax(self, syntax):
        self.TextSyntax = syntax
        if syntax in ["ST", "ALL"]:
            self.Editor.SetMarginType(2, wx.stc.STC_MARGIN_SYMBOL)
            self.Editor.SetMarginMask(2, wx.stc.STC_MASK_FOLDERS)
            self.Editor.SetMarginSensitive(2, 1)
            self.Editor.SetMarginWidth(2, 12)
            if syntax == "ST":
                self.BlockStartKeywords = ST_BLOCK_START_KEYWORDS
                self.BlockEndKeywords = ST_BLOCK_START_KEYWORDS
            else:
                self.BlockStartKeywords = IEC_BLOCK_START_KEYWORDS
                self.BlockEndKeywords = IEC_BLOCK_START_KEYWORDS
        else:
            self.BlockStartKeywords = []
            self.BlockEndKeywords = []

    def SetKeywords(self, keywords):
        self.Keywords = [keyword.upper() for keyword in keywords]
        self.Colourise(0, -1)

    def RefreshJumpList(self):
        if self.TextSyntax == "IL":
            self.Jumps = [jump.upper() for jump in LABEL_MODEL.findall(self.GetText())]
            self.Colourise(0, -1)

    # Buffer the last model state
    def RefreshBuffer(self):
        self.Controler.BufferProject()
        if self.ParentWindow:
            self.ParentWindow.RefreshTitle()
            self.ParentWindow.RefreshFileMenu()
            self.ParentWindow.RefreshEditMenu()

    def StartBuffering(self):
        self.Controler.StartBuffering()
        if self.ParentWindow:
            self.ParentWindow.RefreshTitle()
            self.ParentWindow.RefreshFileMenu()
            self.ParentWindow.RefreshEditMenu()

    def ResetBuffer(self):
        if self.CurrentAction is not None:
            self.Controler.EndBuffering()
            self.CurrentAction = None

    def GetBufferState(self):
        if not self.Debug and self.TextSyntax != "ALL":
            return self.Controler.GetBufferState()
        return False, False

    def Undo(self):
        if not self.Debug and self.TextSyntax != "ALL":
            self.Controler.LoadPrevious()
            self.ParentWindow.CloseTabsWithoutModel()

    def Redo(self):
        if not self.Debug and self.TextSyntax != "ALL":
            self.Controler.LoadNext()
            self.ParentWindow.CloseTabsWithoutModel()

    def HasNoModel(self):
        if not self.Debug and self.TextSyntax != "ALL":
            return self.Controler.GetEditedElement(self.TagName) is None
        return False

    def RefreshView(self, variablepanel=True):
        EditorPanel.RefreshView(self, variablepanel)

        if self.Controler is not None:
            self.ResetBuffer()
            self.DisableEvents = True
            old_cursor_pos = self.GetCurrentPos()
            line = self.Editor.GetFirstVisibleLine()
            column = self.Editor.GetXOffset()
            old_text = self.GetText()
            new_text = self.Controler.GetEditedElementText(self.TagName, self.Debug)
            if old_text != new_text:
                self.SetText(new_text)
                new_cursor_pos = GetCursorPos(old_text, new_text)
                self.Editor.LineScroll(column, line)
                if new_cursor_pos is not None:
                    self.Editor.GotoPos(new_cursor_pos)
                else:
                    self.Editor.GotoPos(old_cursor_pos)
                self.RefreshJumpList()
                self.Editor.EmptyUndoBuffer()
            self.DisableEvents = False

            self.RefreshVariableTree()

            self.TypeNames = [typename.upper() for typename in self.Controler.GetDataTypes(self.TagName, True, self.Debug)]
            self.EnumeratedValues = [value.upper() for value in self.Controler.GetEnumeratedDataValues()]

            self.Functions = {}
            for category in self.Controler.GetBlockTypes(self.TagName, self.Debug):
                for blocktype in category["list"]:
                    blockname = blocktype["name"].upper()
                    if blocktype["type"] == "function" and blockname not in self.Keywords and blockname not in self.Variables.keys():
                        interface = dict([(name, {}) for name, type, modifier in blocktype["inputs"] + blocktype["outputs"] if name != ''])
                        for param in ["EN", "ENO"]:
                            if param not in interface:
                                interface[param] = {}
                        if blockname in self.Functions:
                            self.Functions[blockname]["interface"].update(interface)
                            self.Functions[blockname]["extensible"] |= blocktype["extensible"]
                        else:
                            self.Functions[blockname] = {"interface": interface,
                                                         "extensible": blocktype["extensible"]}

        self.Colourise(0, -1)

    def RefreshVariableTree(self):
        words = self.TagName.split("::")
        self.Variables = self.GenerateVariableTree(
            [(variable.Name, variable.Type, variable.Tree)
             for variable in self.Controler.GetEditedElementInterfaceVars(
                self.TagName, True, self.Debug)])
        if self.Controler.GetEditedElementType(self.TagName, self.Debug)[1] == "function" or words[0] == "T" and self.TextSyntax == "IL":
            return_type, (var_tree, var_dimension) = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, True, self.Debug)
            if return_type is not None:
                self.Variables[words[-1].upper()] = self.GenerateVariableTree(var_tree)
            else:
                self.Variables[words[-1].upper()] = {}

    def GenerateVariableTree(self, list):
        tree = {}
        for var_name, var_type, (var_tree, var_dimension) in list:
            tree[var_name.upper()] = self.GenerateVariableTree(var_tree)
        return tree

    def IsValidVariable(self, name, context):
        return context is not None and context.get(name, None) is not None

    def IsCallParameter(self, name, call):
        if call is not None:
            return (call["interface"].get(name.upper(), None) is not None or
                    call["extensible"] and EXTENSIBLE_PARAMETER.match(name.upper()) is not None)
        return False

    def RefreshLineFolding(self, line_number):
        if self.TextSyntax in ["ST", "ALL"]:
            level = wx.stc.STC_FOLDLEVELBASE + self.Editor.GetLineIndentation(line_number)
            line = self.Editor.GetLine(line_number).strip()
            if line == "":
                if line_number > 0:
                    if LineStartswith(self.Editor.GetLine(line_number - 1).strip(), self.BlockEndKeywords):
                        level = self.Editor.GetFoldLevel(self.Editor.GetFoldParent(line_number - 1)) & wx.stc.STC_FOLDLEVELNUMBERMASK
                    else:
                        level = self.Editor.GetFoldLevel(line_number - 1) & wx.stc.STC_FOLDLEVELNUMBERMASK
                if level != wx.stc.STC_FOLDLEVELBASE:
                    level |= wx.stc.STC_FOLDLEVELWHITEFLAG
            elif LineStartswith(line, self.BlockStartKeywords):
                level |= wx.stc.STC_FOLDLEVELHEADERFLAG
            elif LineStartswith(line, self.BlockEndKeywords):
                if LineStartswith(self.Editor.GetLine(line_number - 1).strip(), self.BlockEndKeywords):
                    level = self.Editor.GetFoldLevel(self.Editor.GetFoldParent(line_number - 1)) & wx.stc.STC_FOLDLEVELNUMBERMASK
                else:
                    level = self.Editor.GetFoldLevel(line_number - 1) & wx.stc.STC_FOLDLEVELNUMBERMASK
            self.Editor.SetFoldLevel(line_number, level)

    def OnStyleNeeded(self, event):
        self.TextChanged = True
        line_number = self.Editor.LineFromPosition(self.Editor.GetEndStyled())
        if line_number == 0:
            start_pos = last_styled_pos = 0
        else:
            start_pos = last_styled_pos = self.Editor.GetLineEndPosition(line_number - 1) + 1
        self.RefreshLineFolding(line_number)
        end_pos = event.GetPosition()
        self.StartStyling(start_pos, 0xff)

        current_context = self.Variables
        current_call = None

        current_pos = last_styled_pos
        state = SPACE
        line = ""
        word = ""
        while current_pos < end_pos:
            char = chr(self.Editor.GetCharAt(current_pos)).upper()
            line += char
            if char == NEWLINE:
                self.ContextStack = []
                current_context = self.Variables
                if state == COMMENT:
                    self.SetStyling(current_pos - last_styled_pos, STC_PLC_COMMENT)
                elif state == NUMBER:
                    self.SetStyling(current_pos - last_styled_pos, STC_PLC_NUMBER)
                elif state == WORD:
                    if word in self.Keywords or word in self.TypeNames:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_WORD)
                    elif self.IsValidVariable(word, current_context):
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_VARIABLE)
                    elif self.IsCallParameter(word, current_call):
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_PARAMETER)
                    elif word in self.Functions:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_FUNCTION)
                    elif self.TextSyntax == "IL" and word in self.Jumps:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_JUMP)
                    elif word in self.EnumeratedValues:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_NUMBER)
                    else:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                        if word not in ["]", ")"] and (self.GetCurrentPos() < last_styled_pos or self.GetCurrentPos() > current_pos):
                            self.StartStyling(last_styled_pos, wx.stc.STC_INDICS_MASK)
                            self.SetStyling(current_pos - last_styled_pos, wx.stc.STC_INDIC0_MASK)
                            self.StartStyling(current_pos, 0xff)
                else:
                    self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                last_styled_pos = current_pos
                if (state != DPRAGMA) and (state != COMMENT):
                    state = SPACE
                line = ""
                line_number += 1
                self.RefreshLineFolding(line_number)
            elif line.endswith("(*") and state != COMMENT:
                self.SetStyling(current_pos - last_styled_pos - 1, STC_PLC_EMPTY)
                last_styled_pos = current_pos
                if state == WORD:
                    current_context = self.Variables
                state = COMMENT
            elif line.endswith("{") and state not in [PRAGMA, DPRAGMA]:
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                last_styled_pos = current_pos
                if state == WORD:
                    current_context = self.Variables
                state = PRAGMA
            elif line.endswith("{{") and state == PRAGMA:
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                last_styled_pos = current_pos
                state = DPRAGMA
            elif state == COMMENT:
                if line.endswith("*)"):
                    self.SetStyling(current_pos - last_styled_pos + 2, STC_PLC_COMMENT)
                    last_styled_pos = current_pos + 1
                    state = SPACE
                    if len(self.CallStack) > 0:
                        current_call = self.CallStack.pop()
                    else:
                        current_call = None
            elif state == PRAGMA:
                if line.endswith("}"):
                    self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                    last_styled_pos = current_pos
                    state = SPACE
            elif state == DPRAGMA:
                if line.endswith("}}"):
                    self.SetStyling(current_pos - last_styled_pos + 1, STC_PLC_EMPTY)
                    last_styled_pos = current_pos + 1
                    state = SPACE
            elif (line.endswith("'") or line.endswith('"')) and state not in [STRING, WSTRING]:
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                last_styled_pos = current_pos
                if state == WORD:
                    current_context = self.Variables
                if line.endswith("'"):
                    state = STRING
                else:
                    state = WSTRING
            elif state == STRING:
                if line.endswith("'") and not line.endswith("$'"):
                    self.SetStyling(current_pos - last_styled_pos + 1, STC_PLC_STRING)
                    last_styled_pos = current_pos + 1
                    state = SPACE
            elif state == WSTRING:
                if line.endswith('"') and not line.endswith('$"'):
                    self.SetStyling(current_pos - last_styled_pos + 1, STC_PLC_STRING)
                    last_styled_pos = current_pos + 1
                    state = SPACE
            elif char in LETTERS:
                if state == NUMBER:
                    word = "#"
                    state = WORD
                elif state == SPACE:
                    self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                    word = char
                    last_styled_pos = current_pos
                    state = WORD
                else:
                    word += char
            elif char in NUMBERS or char == '.' and state != WORD:
                if state == SPACE:
                    self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                    last_styled_pos = current_pos
                    state = NUMBER
                elif state == WORD and char != '.':
                    word += char
            elif char == '(' and state == SPACE:
                self.CallStack.append(current_call)
                current_call = None
            else:
                if state == WORD:
                    if word in self.Keywords or word in self.TypeNames:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_WORD)
                    elif self.IsValidVariable(word, current_context):
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_VARIABLE)
                    elif self.IsCallParameter(word, current_call):
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_PARAMETER)
                    elif word in self.Functions:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_FUNCTION)
                    elif self.TextSyntax == "IL" and word in self.Jumps:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_JUMP)
                    elif word in self.EnumeratedValues:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_NUMBER)
                    else:
                        self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
                        if word not in ["]", ")"] and (self.GetCurrentPos() < last_styled_pos or self.GetCurrentPos() > current_pos):
                            self.StartStyling(last_styled_pos, wx.stc.STC_INDICS_MASK)
                            self.SetStyling(current_pos - last_styled_pos, wx.stc.STC_INDIC0_MASK)
                            self.StartStyling(current_pos, 0xff)
                    if char == '.':
                        if word != "]":
                            if current_context is not None:
                                current_context = current_context.get(word, None)
                            else:
                                current_context = None
                    elif char == '(':
                        self.CallStack.append(current_call)
                        current_call = self.Functions.get(word, None)
                        if current_call is None and self.IsValidVariable(word, current_context):
                            current_call = {"interface": current_context.get(word, {}),
                                            "extensible": False}
                        current_context = self.Variables
                    else:
                        if char == '[' and current_context is not None:
                            self.ContextStack.append(current_context.get(word, None))
                        current_context = self.Variables

                    word = ""
                    last_styled_pos = current_pos
                    state = SPACE
                elif state == NUMBER:
                    self.SetStyling(current_pos - last_styled_pos, STC_PLC_NUMBER)
                    last_styled_pos = current_pos
                    state = SPACE
                if char == ']':
                    if len(self.ContextStack) > 0:
                        current_context = self.ContextStack.pop()
                    else:
                        current_context = self.Variables
                    word = char
                    state = WORD
                elif char == ')':
                    current_context = self.Variables
                    if len(self.CallStack) > 0:
                        current_call = self.CallStack.pop()
                    else:
                        current_call = None
                    word = char
                    state = WORD
            current_pos += 1
        if state == COMMENT:
            self.SetStyling(current_pos - last_styled_pos + 2, STC_PLC_COMMENT)
        elif state == NUMBER:
            self.SetStyling(current_pos - last_styled_pos, STC_PLC_NUMBER)
        elif state == WORD:
            if word in self.Keywords or word in self.TypeNames:
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_WORD)
            elif self.IsValidVariable(word, current_context):
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_VARIABLE)
            elif self.IsCallParameter(word, current_call):
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_PARAMETER)
            elif self.TextSyntax == "IL" and word in self.Functions:
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_FUNCTION)
            elif word in self.Jumps:
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_JUMP)
            elif word in self.EnumeratedValues:
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_NUMBER)
            else:
                self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
        else:
            self.SetStyling(current_pos - start_pos, STC_PLC_EMPTY)
        self.ShowHighlights(start_pos, end_pos)
        event.Skip()

    def OnMarginClick(self, event):
        if event.GetMargin() == 2:
            line = self.Editor.LineFromPosition(event.GetPosition())
            if self.Editor.GetFoldLevel(line) & wx.stc.STC_FOLDLEVELHEADERFLAG:
                self.Editor.ToggleFold(line)
        event.Skip()

    def OnUpdateUI(self, event):
        selected = self.Editor.GetSelectedText()
        if self.ParentWindow and selected != "":
            self.ParentWindow.SetCopyBuffer(selected, True)
        event.Skip()

    def Cut(self):
        self.ResetBuffer()
        self.DisableEvents = True
        self.Editor.CmdKeyExecute(wx.stc.STC_CMD_CUT)
        self.DisableEvents = False
        self.RefreshModel()
        self.RefreshBuffer()

    def Copy(self):
        self.Editor.CmdKeyExecute(wx.stc.STC_CMD_COPY)
        if self.ParentWindow:
            self.ParentWindow.RefreshEditMenu()

    def Paste(self):
        self.ResetBuffer()
        self.DisableEvents = True
        self.Editor.CmdKeyExecute(wx.stc.STC_CMD_PASTE)
        self.DisableEvents = False
        self.RefreshModel()
        self.RefreshBuffer()

    def Search(self, criteria):
        return self.Controler.SearchInPou(self.TagName, criteria, self.Debug)

    def Find(self, direction, search_params):
        if self.SearchParams != search_params:
            self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)

            self.SearchParams = search_params
            self.SearchResults = [
                (infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT)
                for infos, start, end, text in
                self.Search(search_params)]
            self.CurrentFindHighlight = None

        if len(self.SearchResults) > 0:
            if self.CurrentFindHighlight is not None:
                old_idx = self.SearchResults.index(self.CurrentFindHighlight)
                if self.SearchParams["wrap"]:
                    idx = (old_idx + direction) % len(self.SearchResults)
                else:
                    idx = max(0, min(old_idx + direction, len(self.SearchResults) - 1))
                if idx != old_idx:
                    self.RemoveHighlight(*self.CurrentFindHighlight)
                    self.CurrentFindHighlight = self.SearchResults[idx]
                    self.AddHighlight(*self.CurrentFindHighlight)
            else:
                self.CurrentFindHighlight = self.SearchResults[0]
                self.AddHighlight(*self.CurrentFindHighlight)

        else:
            if self.CurrentFindHighlight is not None:
                self.RemoveHighlight(*self.CurrentFindHighlight)
            self.CurrentFindHighlight = None

    def RefreshModel(self):
        self.RefreshJumpList()
        self.Controler.SetEditedElementText(self.TagName, self.GetText())
        self.ResetSearchResults()

    def OnKeyDown(self, event):
        key = event.GetKeyCode()
        if self.Controler is not None:

            if self.Editor.CallTipActive():
                self.Editor.CallTipCancel()

            key_handled = False

            line = self.Editor.GetCurrentLine()
            if line == 0:
                start_pos = 0
            else:
                start_pos = self.Editor.GetLineEndPosition(line - 1) + 1
            end_pos = self.GetCurrentPos()
            lineText = self.Editor.GetTextRange(start_pos, end_pos).replace("\t", " ")

            # Code completion
            if key == wx.WXK_SPACE and event.ControlDown():

                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.keys()
                else:
                    kw = self.Keywords + self.Variables.keys() + self.Functions.keys()
                if len(kw) > 0:
                    if len(words[-1]) > 0:
                        kw = [keyword for keyword in kw if keyword.startswith(words[-1])]
                    kw.sort()
                    self.Editor.AutoCompSetIgnoreCase(True)
                    self.Editor.AutoCompShow(len(words[-1]), " ".join(kw))
                key_handled = True
            elif key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER:
                if self.TextSyntax in ["ST", "ALL"]:
                    indent = self.Editor.GetLineIndentation(line)
                    if LineStartswith(lineText.strip(), self.BlockStartKeywords):
                        indent = (indent / 2 + 1) * 2
                    self.Editor.AddText("\n" + " " * indent)
                    key_handled = True
            elif key == wx.WXK_BACK:
                if self.TextSyntax in ["ST", "ALL"]:
                    if not self.Editor.GetSelectedText():
                        indent = self.Editor.GetColumn(self.Editor.GetCurrentPos())
                        if lineText.strip() == "" and len(lineText) > 0 and indent > 0:
                            self.Editor.DelLineLeft()
                            self.Editor.AddText(" " * ((max(0, indent - 1) / 2) * 2))
                            key_handled = True
            if not key_handled:
                event.Skip()
        else:
            event.Skip()

    def OnKillFocus(self, event):
        self.Editor.AutoCompCancel()
        event.Skip()

    # -------------------------------------------------------------------------------
    #                        Highlights showing functions
    # -------------------------------------------------------------------------------

    def OnRefreshHighlightsTimer(self, event):
        self.RefreshView()
        event.Skip()

    def ClearHighlights(self, highlight_type=None):
        EditorPanel.ClearHighlights(self, highlight_type)

        if highlight_type is None:
            self.Highlights = []
        else:
            highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
            if highlight_type is not None:
                self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type]
        self.RefreshView()

    def AddHighlight(self, infos, start, end, highlight_type):
        EditorPanel.AddHighlight(self, infos, start, end, highlight_type)

        highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
        if infos[0] == "body" and highlight_type is not None:
            self.Highlights.append((infos[1], start, end, highlight_type))
            self.Editor.GotoPos(self.Editor.PositionFromLine(start[0]) + start[1])
            self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)

    def RemoveHighlight(self, infos, start, end, highlight_type):
        EditorPanel.RemoveHighlight(self, infos, start, end, highlight_type)

        highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
        if infos[0] == "body" and highlight_type is not None and \
           (infos[1], start, end, highlight_type) in self.Highlights:
            self.Highlights.remove((infos[1], start, end, highlight_type))
            self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)

    def ShowHighlights(self, start_pos, end_pos):
        for indent, start, end, highlight_type in self.Highlights:
            if start[0] == 0:
                highlight_start_pos = start[1] - indent
            else:
                highlight_start_pos = self.Editor.GetLineEndPosition(start[0] - 1) + start[1] - indent + 1
            if end[0] == 0:
                highlight_end_pos = end[1] - indent + 1
            else:
                highlight_end_pos = self.Editor.GetLineEndPosition(end[0] - 1) + end[1] - indent + 2
            if highlight_start_pos < end_pos and highlight_end_pos > start_pos:
                self.StartStyling(highlight_start_pos, 0xff)
                self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type)
                self.StartStyling(highlight_start_pos, 0x00)
                self.SetStyling(len(self.Editor.GetText()) - highlight_end_pos, wx.stc.STC_STYLE_DEFAULT)
Esempio n. 2
0
class Beremiz(IDEFrame):
    def _init_utils(self):
        self.ConfNodeMenu = wx.Menu(title='')
        self.RecentProjectsMenu = wx.Menu(title='')

        IDEFrame._init_utils(self)

    def _init_coll_FileMenu_Items(self, parent):
        AppendMenu(parent,
                   help='',
                   id=wx.ID_NEW,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'New') + '\tCTRL+N')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_OPEN,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Open') + '\tCTRL+O')
        parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"),
                          self.RecentProjectsMenu)
        parent.AppendSeparator()
        AppendMenu(parent,
                   help='',
                   id=wx.ID_SAVE,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Save') + '\tCTRL+S')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_SAVEAS,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Save as') + '\tCTRL+SHIFT+S')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_CLOSE,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Close Tab') + '\tCTRL+W')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_CLOSE_ALL,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Close Project') + '\tCTRL+SHIFT+W')
        parent.AppendSeparator()
        AppendMenu(parent,
                   help='',
                   id=wx.ID_PAGE_SETUP,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Page Setup') + '\tCTRL+ALT+P')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_PREVIEW,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Preview') + '\tCTRL+SHIFT+P')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_PRINT,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Print') + '\tCTRL+P')
        parent.AppendSeparator()
        AppendMenu(parent,
                   help='',
                   id=wx.ID_EXIT,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Quit') + '\tCTRL+Q')

        self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
        self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
        self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
        self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
        self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
        self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
        self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
        self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
        self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
        self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)

        self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None),
                               (wx.ID_OPEN, "open", _(u'Open'), None),
                               (wx.ID_SAVE, "save", _(u'Save'), None),
                               (wx.ID_SAVEAS, "saveas",
                                _(u'Save As...'), None),
                               (wx.ID_PRINT, "print", _(u'Print'), None)])

    def _RecursiveAddMenuItems(self, menu, items):
        for name, text, helpstr, children in items:
            if len(children) > 0:
                new_menu = wx.Menu(title='')
                menu.AppendMenu(wx.ID_ANY, text, new_menu)
                self._RecursiveAddMenuItems(new_menu, children)
            else:
                item = menu.Append(wx.ID_ANY, text, helpstr)
                self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), item)

    def _init_coll_AddMenu_Items(self, parent):
        IDEFrame._init_coll_AddMenu_Items(self, parent, False)
        self._RecursiveAddMenuItems(parent, GetAddMenuItems())

    def _init_coll_HelpMenu_Items(self, parent):
        def handler(event):
            return wx.MessageBox(version.GetCommunityHelpMsg(),
                                 _(u'Community support'),
                                 wx.OK | wx.ICON_INFORMATION)

        item = parent.Append(wx.ID_ANY, _(u'Community support'), '')
        self.Bind(wx.EVT_MENU, handler, item)

        parent.Append(help='',
                      id=wx.ID_ABOUT,
                      kind=wx.ITEM_NORMAL,
                      text=_(u'About'))
        self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)

    def _init_coll_ConnectionStatusBar_Fields(self, parent):
        parent.SetFieldsCount(3)

        parent.SetStatusText(number=0, text='')
        parent.SetStatusText(number=1, text='')
        parent.SetStatusText(number=2, text='')

        parent.SetStatusWidths([-1, 300, 200])

    def _init_ctrls(self, prnt):
        IDEFrame._init_ctrls(self, prnt)

        self.EditMenuSize = self.EditMenu.GetMenuItemCount()

        inspectorID = wx.NewId()
        self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID)
        accels = [
            wx.AcceleratorEntry(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord('I'),
                                inspectorID)
        ]

        for method, shortcut in [("Stop", wx.WXK_F4), ("Run", wx.WXK_F5),
                                 ("Transfer", wx.WXK_F6),
                                 ("Connect", wx.WXK_F7),
                                 ("Build", wx.WXK_F11)]:

            def OnMethodGen(obj, meth):
                def OnMethod(evt):
                    if obj.CTR is not None:
                        obj.CTR.CallMethod('_' + meth)
                    wx.CallAfter(self.RefreshStatusToolBar)

                return OnMethod

            newid = wx.NewId()
            self.Bind(wx.EVT_MENU, OnMethodGen(self, method), id=newid)
            accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut, newid)]

        self.SetAcceleratorTable(wx.AcceleratorTable(accels))

        self.LogConsole = CustomStyledTextCtrl(name='LogConsole',
                                               parent=self.BottomNoteBook,
                                               pos=wx.Point(0, 0),
                                               size=wx.Size(0, 0))
        self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged)
        self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged)
        self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI,
                             self.OnLogConsoleUpdateUI)
        self.LogConsole.SetReadOnly(True)
        self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR)

        # Define Log Console styles
        self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT,
                                     "face:%(mono)s,size:%(size)d" % faces)
        self.LogConsole.StyleClearAll()
        self.LogConsole.StyleSetSpec(
            1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces)
        self.LogConsole.StyleSetSpec(
            2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)

        # Define Log Console markers
        self.LogConsole.SetMarginSensitive(1, True)
        self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
        self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED")

        self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT)

        self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK,
                             self.OnLogConsoleMarginClick)
        self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED,
                             self.OnLogConsoleModified)

        self.MainTabs["LogConsole"] = (self.LogConsole, _("Console"))
        self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"])
        # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT)

        self.LogViewer = LogViewer(self.BottomNoteBook, self)
        self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log"))
        self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"])
        # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT)

        StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition,
                                   wx.DefaultSize,
                                   wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
        StatusToolBar.SetToolBitmapSize(wx.Size(25, 25))
        StatusToolBar.Realize()
        self.Panes["StatusToolBar"] = StatusToolBar
        self.AUIManager.AddPane(
            StatusToolBar,
            wx.aui.AuiPaneInfo().Name("StatusToolBar").Caption(
                _("Status ToolBar")).ToolbarPane().Top().Position(
                    1).LeftDockable(False).RightDockable(False))

        self.AUIManager.Update()

        self.ConnectionStatusBar = esb.EnhancedStatusBar(self,
                                                         style=wx.ST_SIZEGRIP)
        self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar)
        self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar,
                                          -1,
                                          range=100)
        self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar,
                                           esb.ESB_EXACT_FIT,
                                           esb.ESB_EXACT_FIT, 2)
        self.ProgressStatusBar.Hide()
        self.SetStatusBar(self.ConnectionStatusBar)

    def __init_execute_path(self):
        if os.name == 'nt':
            # on windows, desktop shortcut launches Beremiz.py
            # with working dir set to mingw/bin.
            # then we prefix CWD to PATH in order to ensure that
            # commands invoked by build process by default are
            # found here.
            os.environ["PATH"] = os.getcwd() + ';' + os.environ["PATH"]

    def __init__(self,
                 parent,
                 projectOpen=None,
                 buildpath=None,
                 ctr=None,
                 debug=True):
        # Add beremiz's icon in top left corner of the frame
        self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO)
        self.__init_execute_path()

        IDEFrame.__init__(self, parent, debug)
        self.Log = LogPseudoFile(self.LogConsole, self.SelectTab)

        self.local_runtime = None
        self.runtime_port = None
        self.local_runtime_tmpdir = None

        self.LastPanelSelected = None

        # Define Tree item icon list
        self.LocationImageList = wx.ImageList(16, 16)
        self.LocationImageDict = {}

        # Icons for location items
        for imgname, itemtype in [("CONFIGURATION", LOCATION_CONFNODE),
                                  ("RESOURCE", LOCATION_MODULE),
                                  ("PROGRAM", LOCATION_GROUP),
                                  ("VAR_INPUT", LOCATION_VAR_INPUT),
                                  ("VAR_OUTPUT", LOCATION_VAR_OUTPUT),
                                  ("VAR_LOCAL", LOCATION_VAR_MEMORY)]:
            self.LocationImageDict[itemtype] = self.LocationImageList.Add(
                GetBitmap(imgname))

        # Icons for other items
        for imgname, itemtype in [("Extension", ITEM_CONFNODE)]:
            self.TreeImageDict[itemtype] = self.TreeImageList.Add(
                GetBitmap(imgname))

        if projectOpen is not None:
            projectOpen = DecodeFileSystemPath(projectOpen, False)

        if projectOpen is not None and os.path.isdir(projectOpen):
            self.CTR = ProjectController(self, self.Log)
            self.Controler = self.CTR
            result, _err = self.CTR.LoadProject(projectOpen, buildpath)
            if not result:
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshConfigRecentProjects(os.path.abspath(projectOpen))
                self.RefreshAfterLoad()
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
        else:
            self.CTR = ctr
            self.Controler = ctr
            if ctr is not None:
                ctr.SetAppFrame(self, self.Log)
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshAfterLoad()
        if self.EnableDebug:
            self.DebugVariablePanel.SetDataProducer(self.CTR)

        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)

        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        self.RefreshAll()
        self.LogConsole.SetFocus()

    def RefreshTitle(self):
        name = _("Beremiz")
        if self.CTR is not None:
            projectname = self.CTR.GetProjectName()
            if self.CTR.ProjectTestModified():
                projectname = "~%s~" % projectname
            self.SetTitle("%s - %s" % (name, projectname))
        else:
            self.SetTitle(name)

    def StartLocalRuntime(self, taskbaricon=True):
        if (self.local_runtime is None) or (self.local_runtime.exitcode
                                            is not None):
            # create temporary directory for runtime working directory
            self.local_runtime_tmpdir = tempfile.mkdtemp()
            # choose an arbitrary random port for runtime
            self.runtime_port = int(random.random() * 1000) + 61131
            # launch local runtime
            self.local_runtime = ProcessLogger(
                self.Log,
                "\"%s\" \"%s\" -p %s -i localhost %s %s" %
                (sys.executable, Bpath("Beremiz_service.py"),
                 self.runtime_port, {
                     False: "-x 0",
                     True: "-x 1"
                 }[taskbaricon], self.local_runtime_tmpdir),
                no_gui=False,
                timeout=500,
                keyword=self.local_runtime_tmpdir,
                cwd=self.local_runtime_tmpdir)
            self.local_runtime.spin()
        return self.runtime_port

    def KillLocalRuntime(self):
        if self.local_runtime is not None:
            # shutdown local runtime
            self.local_runtime.kill(gently=False)
            # clear temp dir
            shutil.rmtree(self.local_runtime_tmpdir)

            self.local_runtime = None

    def OnOpenWidgetInspector(self, evt):
        # Activate the widget inspection tool
        from wx.lib.inspection import InspectionTool
        if not InspectionTool().initialized:
            InspectionTool().Init()

        # Find a widget to be selected in the tree.  Use either the
        # one under the cursor, if any, or this frame.
        wnd = wx.FindWindowAtPointer()
        if not wnd:
            wnd = self
        InspectionTool().Show(wnd, True)

    def OnLogConsoleFocusChanged(self, event):
        self.RefreshEditMenu()
        event.Skip()

    def OnLogConsoleUpdateUI(self, event):
        self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True)
        event.Skip()

    def OnLogConsoleMarginClick(self, event):
        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
        wx.CallAfter(self.SearchLineForError,
                     self.LogConsole.GetLine(line_idx))
        event.Skip()

    def OnLogConsoleModified(self, event):
        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
        line = self.LogConsole.GetLine(line_idx)
        if line:
            result = MATIEC_ERROR_MODEL.match(line)
            if result is not None:
                self.LogConsole.MarkerAdd(line_idx, 0)
        event.Skip()

    def SearchLineForError(self, line):
        if self.CTR is not None:
            result = MATIEC_ERROR_MODEL.match(line)
            if result is not None:
                first_line, first_column, last_line, last_column, _error = result.groups(
                )
                self.CTR.ShowError(self.Log,
                                   (int(first_line), int(first_column)),
                                   (int(last_line), int(last_column)))

    def CheckSaveBeforeClosing(self, title=_("Close Project")):
        """Function displaying an Error dialog in PLCOpenEditor.

        :returns: False if closing cancelled.
        """
        if self.CTR.ProjectTestModified():
            dialog = wx.MessageDialog(
                self, _("There are changes, do you want to save?"), title,
                wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
            answer = dialog.ShowModal()
            dialog.Destroy()
            if answer == wx.ID_YES:
                self.CTR.SaveProject()
            elif answer == wx.ID_CANCEL:
                return False

        for idx in xrange(self.TabsOpened.GetPageCount()):
            window = self.TabsOpened.GetPage(idx)
            if not window.CheckSaveBeforeClosing():
                return False

        return True

    def GetTabInfos(self, tab):
        if isinstance(tab, EditorPanel) and \
           not isinstance(tab, (Viewer,
                                TextViewer,
                                ResourceEditor,
                                ConfigurationEditor,
                                DataTypeEditor)):
            return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName())
        elif (isinstance(tab, TextViewer)
              and (tab.Controler is None
                   or isinstance(tab.Controler, MiniTextControler))):
            return ("confnode", None, tab.GetInstancePath())
        else:
            return IDEFrame.GetTabInfos(self, tab)

    def LoadTab(self, notebook, page_infos):
        if page_infos[0] == "confnode":
            if page_infos[1] is None:
                confnode = self.CTR
            else:
                confnode = self.CTR.GetChildByName(page_infos[1])
            return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:]))
        else:
            return IDEFrame.LoadTab(self, notebook, page_infos)

    # Strange hack required by WAMP connector, using twisted.
    # Twisted reactor needs to be stopped only before quit,
    # since it cannot be restarted
    ToDoBeforeQuit = []

    def AddToDoBeforeQuit(self, Thing):
        self.ToDoBeforeQuit.append(Thing)

    def TryCloseFrame(self):
        if self.CTR is None or self.CheckSaveBeforeClosing(
                _("Close Application")):
            if self.CTR is not None:
                self.CTR.KillDebugThread()
            self.KillLocalRuntime()

            self.SaveLastState()

            for Thing in self.ToDoBeforeQuit:
                Thing()
            self.ToDoBeforeQuit = []

            return True
        return False

    def OnCloseFrame(self, event):
        if self.TryCloseFrame():
            self.LogConsole.Disconnect(-1, -1, wx.wxEVT_KILL_FOCUS)
            event.Skip()
        else:
            # prevent event to continue, i.e. cancel closing
            event.Veto()

    def RefreshFileMenu(self):
        self.RefreshRecentProjectsMenu()

        MenuToolBar = self.Panes["MenuToolBar"]
        if self.CTR is not None:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                window = self.TabsOpened.GetPage(selected)
                viewer_is_modified = window.IsModified()
                is_viewer = isinstance(window, Viewer)
            else:
                viewer_is_modified = is_viewer = False
            if self.TabsOpened.GetPageCount() > 0:
                self.FileMenu.Enable(wx.ID_CLOSE, True)
                if is_viewer:
                    self.FileMenu.Enable(wx.ID_PREVIEW, True)
                    self.FileMenu.Enable(wx.ID_PRINT, True)
                    MenuToolBar.EnableTool(wx.ID_PRINT, True)
                else:
                    self.FileMenu.Enable(wx.ID_PREVIEW, False)
                    self.FileMenu.Enable(wx.ID_PRINT, False)
                    MenuToolBar.EnableTool(wx.ID_PRINT, False)
            else:
                self.FileMenu.Enable(wx.ID_CLOSE, False)
                self.FileMenu.Enable(wx.ID_PREVIEW, False)
                self.FileMenu.Enable(wx.ID_PRINT, False)
                MenuToolBar.EnableTool(wx.ID_PRINT, False)
            self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
            project_modified = self.CTR.ProjectTestModified(
            ) or viewer_is_modified
            self.FileMenu.Enable(wx.ID_SAVE, project_modified)
            MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
            self.FileMenu.Enable(wx.ID_SAVEAS, True)
            MenuToolBar.EnableTool(wx.ID_SAVEAS, True)
            self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
        else:
            self.FileMenu.Enable(wx.ID_CLOSE, False)
            self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
            self.FileMenu.Enable(wx.ID_PREVIEW, False)
            self.FileMenu.Enable(wx.ID_PRINT, False)
            MenuToolBar.EnableTool(wx.ID_PRINT, False)
            self.FileMenu.Enable(wx.ID_SAVE, False)
            MenuToolBar.EnableTool(wx.ID_SAVE, False)
            self.FileMenu.Enable(wx.ID_SAVEAS, False)
            MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
            self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)

    def RefreshRecentProjectsMenu(self):
        try:
            recent_projects = map(DecodeFileSystemPath,
                                  self.GetConfigEntry("RecentProjects", []))
        except Exception:
            recent_projects = []

        while self.RecentProjectsMenu.GetMenuItemCount() > 0:
            item = self.RecentProjectsMenu.FindItemByPosition(0)
            self.RecentProjectsMenu.RemoveItem(item)

        self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS,
                             len(recent_projects) > 0)
        for idx, projectpath in enumerate(recent_projects):
            text = u'&%d: %s' % (idx + 1, projectpath)

            item = self.RecentProjectsMenu.Append(wx.ID_ANY, text, '')
            self.Bind(wx.EVT_MENU,
                      self.GenerateOpenRecentProjectFunction(projectpath),
                      item)

    def GenerateOpenRecentProjectFunction(self, projectpath):
        def OpenRecentProject(event):
            if self.CTR is not None and not self.CheckSaveBeforeClosing():
                return

            self.OpenProject(projectpath)

        return OpenRecentProject

    def GenerateMenuRecursive(self, items, menu):
        for kind, infos in items:
            if isinstance(kind, list):
                text, id = infos
                submenu = wx.Menu('')
                self.GenerateMenuRecursive(kind, submenu)
                menu.AppendMenu(id, text, submenu)
            elif kind == wx.ITEM_SEPARATOR:
                menu.AppendSeparator()
            else:
                text, id, _help, callback = infos
                AppendMenu(menu, help='', id=id, kind=kind, text=text)
                if callback is not None:
                    self.Bind(wx.EVT_MENU, callback, id=id)

    def RefreshEditorToolBar(self):
        IDEFrame.RefreshEditorToolBar(self)
        self.AUIManager.GetPane("EditorToolBar").Position(2)
        self.AUIManager.GetPane("StatusToolBar").Position(1)
        self.AUIManager.Update()

    def RefreshStatusToolBar(self):
        StatusToolBar = self.Panes["StatusToolBar"]
        StatusToolBar.ClearTools()
        StatusToolBar.SetMinSize(StatusToolBar.GetToolBitmapSize())

        if self.CTR is not None:

            for confnode_method in self.CTR.StatusMethods:
                if "method" in confnode_method and confnode_method.get(
                        "shown", True):
                    tool = StatusToolBar.AddSimpleTool(
                        wx.ID_ANY,
                        GetBitmap(confnode_method.get("bitmap", "Unknown")),
                        confnode_method["tooltip"])
                    self.Bind(
                        wx.EVT_MENU,
                        self.GetMenuCallBackFunction(
                            confnode_method["method"]), tool)

            StatusToolBar.Realize()
            self.AUIManager.GetPane("StatusToolBar").BestSize(
                StatusToolBar.GetBestSize()).Show()
        else:
            self.AUIManager.GetPane("StatusToolBar").Hide()
        self.AUIManager.GetPane("EditorToolBar").Position(2)
        self.AUIManager.GetPane("StatusToolBar").Position(1)
        self.AUIManager.Update()

    def RefreshEditMenu(self):
        IDEFrame.RefreshEditMenu(self)
        if self.FindFocus() == self.LogConsole:
            self.EditMenu.Enable(wx.ID_COPY, True)
            self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True)

        if self.CTR is not None:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                panel = self.TabsOpened.GetPage(selected)
            else:
                panel = None
            if panel != self.LastPanelSelected:
                for i in xrange(self.EditMenuSize,
                                self.EditMenu.GetMenuItemCount()):
                    item = self.EditMenu.FindItemByPosition(self.EditMenuSize)
                    if item is not None:
                        if item.IsSeparator():
                            self.EditMenu.RemoveItem(item)
                        else:
                            self.EditMenu.Delete(item.GetId())
                self.LastPanelSelected = panel
                if panel is not None:
                    items = panel.GetConfNodeMenuItems()
                else:
                    items = []
                if len(items) > 0:
                    self.EditMenu.AppendSeparator()
                    self.GenerateMenuRecursive(items, self.EditMenu)
            if panel is not None:
                panel.RefreshConfNodeMenu(self.EditMenu)
        else:
            for i in xrange(self.EditMenuSize,
                            self.EditMenu.GetMenuItemCount()):
                item = self.EditMenu.FindItemByPosition(i)
                if item is not None:
                    if item.IsSeparator():
                        self.EditMenu.RemoveItem(item)
                    else:
                        self.EditMenu.Delete(item.GetId())
            self.LastPanelSelected = None
        self.MenuBar.UpdateMenus()

    def RefreshAll(self):
        self.RefreshStatusToolBar()

    def GetMenuCallBackFunction(self, method):
        """ Generate the callbackfunc for a given CTR method"""
        def OnMenu(event):
            # Disable button to prevent re-entrant call
            event.GetEventObject().Disable()
            # Call
            getattr(self.CTR, method)()
            # Re-enable button
            event.GetEventObject().Enable()

        return OnMenu

    def GetConfigEntry(self, entry_name, default):
        return cPickle.loads(
            str(self.Config.Read(entry_name, cPickle.dumps(default))))

    def ResetConnectionStatusBar(self):
        for field in xrange(self.ConnectionStatusBar.GetFieldsCount()):
            self.ConnectionStatusBar.SetStatusText('', field)

    def ResetView(self):
        IDEFrame.ResetView(self)
        if self.CTR is not None:
            self.CTR.CloseProject()
        self.CTR = None
        self.Log.flush()
        if self.EnableDebug:
            self.DebugVariablePanel.SetDataProducer(None)
            self.ResetConnectionStatusBar()

    def RefreshConfigRecentProjects(self, projectpath, err=False):
        try:
            recent_projects = map(DecodeFileSystemPath,
                                  self.GetConfigEntry("RecentProjects", []))
        except Exception:
            recent_projects = []
        if projectpath in recent_projects:
            recent_projects.remove(projectpath)
        if not err:
            recent_projects.insert(0, projectpath)
        self.Config.Write(
            "RecentProjects",
            cPickle.dumps(
                map(EncodeFileSystemPath,
                    recent_projects[:MAX_RECENT_PROJECTS])))
        self.Config.Flush()

    def ResetPerspective(self):
        IDEFrame.ResetPerspective(self)
        self.RefreshStatusToolBar()

    def OnNewProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        try:
            defaultpath = DecodeFileSystemPath(
                self.Config.Read("lastopenedfolder"))
        except Exception:
            defaultpath = os.path.expanduser("~")

        dialog = wx.DirDialog(self,
                              _("Choose an empty directory for new project"),
                              defaultpath)
        if dialog.ShowModal() == wx.ID_OK:
            projectpath = dialog.GetPath()
            self.Config.Write(
                "lastopenedfolder",
                EncodeFileSystemPath(os.path.dirname(projectpath)))
            self.Config.Flush()
            self.ResetView()
            ctr = ProjectController(self, self.Log)
            result = ctr.NewProject(projectpath)
            if not result:
                self.CTR = ctr
                self.Controler = self.CTR
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshConfigRecentProjects(projectpath)
                if self.EnableDebug:
                    self.DebugVariablePanel.SetDataProducer(self.CTR)
                self.RefreshAfterLoad()
                IDEFrame.OnAddNewProject(self, event)
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
            self.RefreshAll()
            self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
        dialog.Destroy()

    def OnOpenProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        try:
            defaultpath = DecodeFileSystemPath(
                self.Config.Read("lastopenedfolder"))
        except Exception:
            defaultpath = os.path.expanduser("~")

        dialog = wx.DirDialog(self,
                              _("Choose a project"),
                              defaultpath,
                              style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
        if dialog.ShowModal() == wx.ID_OK:
            self.OpenProject(dialog.GetPath())
        dialog.Destroy()

    def OpenProject(self, projectpath):
        if os.path.isdir(projectpath):
            self.Config.Write(
                "lastopenedfolder",
                EncodeFileSystemPath(os.path.dirname(projectpath)))
            self.Config.Flush()
            self.ResetView()
            self.CTR = ProjectController(self, self.Log)
            self.Controler = self.CTR
            result, err = self.CTR.LoadProject(projectpath)
            if not result:
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                if self.EnableDebug:
                    self.DebugVariablePanel.SetDataProducer(self.CTR)
                self.RefreshAfterLoad()
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
            self.RefreshAll()
            self.SearchResultPanel.ResetSearchResults()
        else:
            self.ShowErrorMessage(
                _("\"%s\" folder is not a valid Beremiz project\n") %
                projectpath)
            err = True
        self.RefreshConfigRecentProjects(projectpath, err)
        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)

    def OnCloseProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        self.ResetView()
        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
        self.RefreshAll()

    def RefreshAfterLoad(self):
        self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)

    def RefreshAfterSave(self):
        self.RefreshAll()
        self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)

    def OnSaveProjectMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.Save()
        if self.CTR is not None:
            self.CTR.SaveProject()
            self.RefreshAfterSave()

    def OnSaveProjectAsMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.SaveAs()
        if self.CTR is not None:
            self.CTR.SaveProjectAs()
            self.RefreshAfterSave()
            self.RefreshConfigRecentProjects(self.CTR.ProjectPath)

    def OnQuitMenu(self, event):
        self.Close()

    def OnAboutMenu(self, event):
        info = version.GetAboutDialogInfo()
        ShowAboutDialog(self, info)

    def OnProjectTreeItemBeginEdit(self, event):
        selected = event.GetItem()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE:
            event.Veto()
        else:
            IDEFrame.OnProjectTreeItemBeginEdit(self, event)

    def OnProjectTreeRightUp(self, event):
        item = event.GetItem()
        item_infos = self.ProjectTree.GetPyData(item)

        if item_infos["type"] == ITEM_CONFNODE:
            confnode_menu = wx.Menu(title='')

            confnode = item_infos["confnode"]
            if confnode is not None:
                menu_items = confnode.GetContextualMenuItems()
                if menu_items is not None:
                    for text, helpstr, callback in menu_items:
                        item = confnode_menu.Append(wx.ID_ANY, text, helpstr)
                        self.Bind(wx.EVT_MENU, callback, item)
                else:
                    for name, XSDClass, helpstr in confnode.CTNChildrenTypes:
                        if not hasattr(XSDClass, 'CTNMaxCount') or not confnode.Children.get(name) \
                                or len(confnode.Children[name]) < XSDClass.CTNMaxCount:
                            item = confnode_menu.Append(
                                wx.ID_ANY,
                                _("Add") + " " + name, helpstr)
                            self.Bind(
                                wx.EVT_MENU,
                                self.GetAddConfNodeFunction(name, confnode),
                                item)
            item = confnode_menu.Append(wx.ID_ANY, _("Delete"))
            self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), item)

            self.PopupMenu(confnode_menu)
            confnode_menu.Destroy()

            event.Skip()
        elif item_infos["type"] == ITEM_RESOURCE:
            # prevent last resource to be delted
            parent = self.ProjectTree.GetItemParent(item)
            parent_name = self.ProjectTree.GetItemText(parent)
            if parent_name == _("Resources"):
                IDEFrame.OnProjectTreeRightUp(self, event)
        else:
            IDEFrame.OnProjectTreeRightUp(self, event)

    def OnProjectTreeItemActivated(self, event):
        selected = event.GetItem()
        item_infos = self.ProjectTree.GetPyData(selected)
        if item_infos["type"] == ITEM_CONFNODE:
            item_infos["confnode"]._OpenView()
            event.Skip()
        elif item_infos["type"] == ITEM_PROJECT:
            self.CTR._OpenView()
        else:
            IDEFrame.OnProjectTreeItemActivated(self, event)

    def ProjectTreeItemSelect(self, select_item):
        if select_item is not None and select_item.IsOk():
            item_infos = self.ProjectTree.GetPyData(select_item)
            if item_infos["type"] == ITEM_CONFNODE:
                item_infos["confnode"]._OpenView(onlyopened=True)
            elif item_infos["type"] == ITEM_PROJECT:
                self.CTR._OpenView(onlyopened=True)
            else:
                IDEFrame.ProjectTreeItemSelect(self, select_item)

    def GetProjectElementWindow(self, element, tagname):
        is_a_CTN_tagname = len(tagname.split("::")) == 1
        if is_a_CTN_tagname:
            confnode = self.CTR.GetChildByName(tagname)
            return confnode.GetView()
        else:
            return IDEFrame.GetProjectElementWindow(self, element, tagname)

    def SelectProjectTreeItem(self, tagname):
        if self.ProjectTree is not None:
            root = self.ProjectTree.GetRootItem()
            if root.IsOk():
                words = tagname.split("::")
                if len(words) == 1:
                    if tagname == "Project":
                        self.SelectedItem = root
                        self.ProjectTree.SelectItem(root)
                        self.ResetSelectedItem()
                    else:
                        return self.RecursiveProjectTreeItemSelection(
                            root, [(word, ITEM_CONFNODE)
                                   for word in tagname.split(".")])
                elif words[0] == "R":
                    return self.RecursiveProjectTreeItemSelection(
                        root, [(words[2], ITEM_RESOURCE)])
                elif not os.path.exists(words[0]):
                    IDEFrame.SelectProjectTreeItem(self, tagname)

    def GetAddConfNodeFunction(self, name, confnode=None):
        def AddConfNodeMenuFunction(event):
            wx.CallAfter(self.AddConfNode, name, confnode)

        return AddConfNodeMenuFunction

    def GetDeleteMenuFunction(self, confnode):
        def DeleteMenuFunction(event):
            wx.CallAfter(self.DeleteConfNode, confnode)

        return DeleteMenuFunction

    def AddConfNode(self, ConfNodeType, confnode=None):
        if self.CTR.CheckProjectPathPerm():
            ConfNodeName = "%s_0" % ConfNodeType
            if confnode is not None:
                confnode.CTNAddChild(ConfNodeName, ConfNodeType)
            else:
                self.CTR.CTNAddChild(ConfNodeName, ConfNodeType)
            self._Refresh(TITLE, FILEMENU, PROJECTTREE)

    def DeleteConfNode(self, confnode):
        if self.CTR.CheckProjectPathPerm():
            dialog = wx.MessageDialog(
                self,
                _("Really delete node '%s'?") % confnode.CTNName(),
                _("Remove %s node") % confnode.CTNType,
                wx.YES_NO | wx.NO_DEFAULT)
            if dialog.ShowModal() == wx.ID_YES:
                confnode.CTNRemove()
                del confnode
                self._Refresh(TITLE, FILEMENU, PROJECTTREE)
            dialog.Destroy()

# -------------------------------------------------------------------------------
#                        Highlights showing functions
# -------------------------------------------------------------------------------

    def ShowHighlight(self, infos, start, end, highlight_type):
        config_name = self.Controler.GetProjectMainConfigurationName()
        if config_name is not None and infos[0] == ComputeConfigurationName(
                config_name):
            self.CTR._OpenView()
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                viewer = self.TabsOpened.GetPage(selected)
                viewer.AddHighlight(infos[1:], start, end, highlight_type)
        else:
            IDEFrame.ShowHighlight(self, infos, start, end, highlight_type)