IDEFrame.py
author Edouard Tisserant <edouard.tisserant@gmail.com>
Thu, 10 Dec 2020 11:37:27 +0100
changeset 2697 93333d206198
parent 2614 6b4061f6ced6
child 2737 38afed869ff6
permissions -rw-r--r--
Python Safe Globals now have more reliable triggering of OnChange call. Added "Onchange" object to accessible runtime variables that let user python code see count of changes and first and last values.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# This file is part of Beremiz, a Integrated Development Environment for
# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
#
# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
# See COPYING file for copyrights details.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

from __future__ import absolute_import
from __future__ import division
import sys
import base64
from future.builtins import \
    round, \
    str as text

import wx
import wx.grid
import wx.aui
from six.moves import cPickle, xrange

from editors.EditorPanel import EditorPanel
from editors.SFCViewer import SFC_Viewer
from editors.LDViewer import LD_Viewer
from editors.TextViewer import TextViewer
from editors.Viewer import Viewer, ZOOM_FACTORS
from editors.ResourceEditor import ConfigurationEditor, ResourceEditor
from editors.DataTypeEditor import DataTypeEditor
from PLCControler import *
from controls import CustomTree, LibraryPanel, PouInstanceVariablesPanel, SearchResultPanel
from controls.DebugVariablePanel import DebugVariablePanel
from dialogs import ProjectDialog, PouDialog, PouTransitionDialog, PouActionDialog, FindInPouDialog, SearchInProjectDialog
from util.BitmapLibrary import GetBitmap
from plcopen.types_enums import *

# Define PLCOpenEditor controls id
[
    ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK,
    ID_PLCOPENEDITORBOTTOMNOTEBOOK, ID_PLCOPENEDITORRIGHTNOTEBOOK,
    ID_PLCOPENEDITORPROJECTTREE, ID_PLCOPENEDITORMAINSPLITTER,
    ID_PLCOPENEDITORSECONDSPLITTER, ID_PLCOPENEDITORTHIRDSPLITTER,
    ID_PLCOPENEDITORLIBRARYPANEL, ID_PLCOPENEDITORLIBRARYSEARCHCTRL,
    ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT,
    ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED,
    ID_PLCOPENEDITOREDITORMENUTOOLBAR, ID_PLCOPENEDITOREDITORTOOLBAR,
    ID_PLCOPENEDITORPROJECTPANEL,
] = [wx.NewId() for _init_ctrls in range(17)]

# Define PLCOpenEditor EditMenu extra items id
[
    ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE,
    ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK,
    ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
    ID_PLCOPENEDITOREDITMENUFINDNEXT, ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
    ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, ID_PLCOPENEDITOREDITMENUADDRESOURCE
] = [wx.NewId() for _init_coll_EditMenu_Items in range(10)]

# Define PLCOpenEditor DisplayMenu extra items id
[
    ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE,
    ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE,
    ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN,
] = [wx.NewId() for _init_coll_DisplayMenu_Items in range(3)]

# -------------------------------------------------------------------------------
#                            EditorToolBar definitions
# -------------------------------------------------------------------------------

# Define PLCOpenEditor Toolbar items id
[
    ID_PLCOPENEDITOREDITORTOOLBARSELECTION, ID_PLCOPENEDITOREDITORTOOLBARCOMMENT,
    ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, ID_PLCOPENEDITOREDITORTOOLBARBLOCK,
    ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, ID_PLCOPENEDITOREDITORTOOLBARWIRE,
    ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, ID_PLCOPENEDITOREDITORTOOLBARRUNG,
    ID_PLCOPENEDITOREDITORTOOLBARCOIL, ID_PLCOPENEDITOREDITORTOOLBARCONTACT,
    ID_PLCOPENEDITOREDITORTOOLBARBRANCH, ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP,
    ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION,
    ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE,
    ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION,
] = [wx.NewId() for _init_coll_DefaultEditorToolBar_Items in range(18)]


# -------------------------------------------------------------------------------
#                               Helper Functions
# -------------------------------------------------------------------------------


def EncodeFileSystemPath(path, use_base64=True):
    path = path.encode(sys.getfilesystemencoding())
    if use_base64:
        return base64.encodestring(path)
    return path


def DecodeFileSystemPath(path, is_base64=True):
    if is_base64:
        path = base64.decodestring(path)
    return text(path, sys.getfilesystemencoding())


def AppendMenu(parent, help, id, kind, text):
    parent.Append(help=help, id=id, kind=kind, text=text)


[
    TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE,
    POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES
] = range(10)


def GetShortcutKeyCallbackFunction(viewer_function):
    def ShortcutKeyFunction(self, event):
        control = self.FindFocus()
        if control is not None and control.GetName() in ["Viewer", "TextViewer"]:
            getattr(control.ParentWindow, viewer_function)()
        elif isinstance(control, wx.stc.StyledTextCtrl):
            getattr(control, viewer_function)()
        elif isinstance(control, wx.TextCtrl):
            control.ProcessEvent(event)
    return ShortcutKeyFunction


def GetDeleteElementFunction(remove_function, parent_type=None, check_function=None):
    def DeleteElementFunction(self, selected):
        name = self.ProjectTree.GetItemText(selected)
        if check_function is None or check_function(name):
            if parent_type is not None:
                item_infos = self.ProjectTree.GetPyData(selected)
                parent_name = item_infos["tagname"].split("::")[1]
                remove_function(self.Controler, parent_name, name)
            else:
                remove_function(self.Controler, name)
    return DeleteElementFunction


if wx.Platform == '__WXMSW__':
    TAB_BORDER = 6
    NOTEBOOK_BORDER = 6
else:
    TAB_BORDER = 7
    NOTEBOOK_BORDER = 2


def SimplifyTabLayout(tabs, rect):
    for tab in tabs:
        if tab["pos"][0] == rect.x:
            others = [t for t in tabs if t != tab]
            others.sort(lambda x, y: cmp(x["pos"][0], y["pos"][0]))
            for other in others:
                if other["pos"][1] == tab["pos"][1] and \
                   other["size"][1] == tab["size"][1] and \
                   other["pos"][0] == tab["pos"][0] + tab["size"][0] + TAB_BORDER:

                    tab["size"] = (tab["size"][0] + other["size"][0] + TAB_BORDER, tab["size"][1])
                    tab["pages"].extend(other["pages"])
                    tabs.remove(other)

                    if tab["size"][0] == rect.width:
                        return True

        elif tab["pos"][1] == rect.y:
            others = [t for t in tabs if t != tab]
            others.sort(lambda x, y: cmp(x["pos"][1], y["pos"][1]))
            for other in others:
                if other["pos"][0] == tab["pos"][0] and \
                   other["size"][0] == tab["size"][0] and \
                   other["pos"][1] == tab["pos"][1] + tab["size"][1] + TAB_BORDER:

                    tab["size"] = (tab["size"][0], tab["size"][1] + other["size"][1] + TAB_BORDER)
                    tab["pages"].extend(other["pages"])
                    tabs.remove(other)

                    if tab["size"][1] == rect.height:
                        return True
    return False


def ComputeTabsLayout(tabs, rect):
    if len(tabs) == 0:
        return tabs
    if len(tabs) == 1:
        return tabs[0]
    split = None
    split_id = None
    for idx, tab in enumerate(tabs):
        if len(tab["pages"]) == 0:
            raise ValueError("Not possible")
        if tab["size"][0] == rect.width:
            if tab["pos"][1] == rect.y:
                split = (wx.TOP, tab["size"][1] / rect.height)
                split_rect = wx.Rect(rect.x, rect.y + tab["size"][1] + TAB_BORDER,
                                     rect.width, rect.height - tab["size"][1] - TAB_BORDER)
            elif tab["pos"][1] == rect.height + 1 - tab["size"][1]:
                split = (wx.BOTTOM, 1.0 - tab["size"][1] / rect.height)
                split_rect = wx.Rect(rect.x, rect.y,
                                     rect.width, rect.height - tab["size"][1] - TAB_BORDER)
            split_id = idx
            break
        elif tab["size"][1] == rect.height:
            if tab["pos"][0] == rect.x:
                split = (wx.LEFT, tab["size"][0] / rect.width)
                split_rect = wx.Rect(rect.x + tab["size"][0] + TAB_BORDER, rect.y,
                                     rect.width - tab["size"][0] - TAB_BORDER, rect.height)
            elif tab["pos"][0] == rect.width + 1 - tab["size"][0]:
                split = (wx.RIGHT, 1.0 - tab["size"][0] / rect.width)
                split_rect = wx.Rect(rect.x, rect.y,
                                     rect.width - tab["size"][0] - TAB_BORDER, rect.height)
            split_id = idx
            break
    if split is not None:
        split_tab = tabs.pop(split_id)
        return {"split": split,
                "tab": split_tab,
                "others": ComputeTabsLayout(tabs, split_rect)}
    else:
        if SimplifyTabLayout(tabs, rect):
            return ComputeTabsLayout(tabs, rect)
    return tabs


class IDEFrame(wx.Frame):
    """IDEFrame Base Class"""

    def InitEditorToolbarItems(self):
        """
        Initialize dictionary with lists of elements that need to be shown
        if POU in particular programming language is edited.
        """
        # Define behaviour of each Toolbar item according to current POU body type
        # Informations meaning are in this order:
        #  - Item is toggled
        #  - PLCOpenEditor mode where item is displayed (could be more then one)
        #  - Item id
        #  - Item callback function name
        #  - Item icon filename
        #  - Item tooltip text
        self.EditorToolBarItems = {
            "FBD":   [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
                       "move", _("Move the view")),
                      (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
                       "add_comment", _("Create a new comment")),
                      (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
                       "add_variable", _("Create a new variable")),
                      (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
                       "add_block", _("Create a new block")),
                      (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
                       "add_connection", _("Create a new connection"))],
            "LD":    [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
                       "move", _("Move the view")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
                       "add_comment", _("Create a new comment")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool",
                       "add_powerrail", _("Create a new power rail")),
                      (False, DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool",
                       "add_rung", _("Create a new rung")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool",
                       "add_coil", _("Create a new coil")),
                      (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool",
                       "add_contact", _("Create a new contact")),
                      (False, DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool",
                       "add_branch", _("Create a new branch")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
                       "add_variable", _("Create a new variable")),
                      (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
                       "add_block", _("Create a new block")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
                       "add_connection", _("Create a new connection"))],
            "SFC":   [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
                       "move", _("Move the view")),
                      (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
                       "add_comment", _("Create a new comment")),
                      (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool",
                       "add_initial_step", _("Create a new initial step")),
                      (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool",
                       "add_step", _("Create a new step")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool",
                       "add_transition", _("Create a new transition")),
                      (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool",
                       "add_action", _("Create a new action block")),
                      (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool",
                       "add_divergence", _("Create a new divergence")),
                      (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool",
                       "add_jump", _("Create a new jump")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
                       "add_variable", _("Create a new variable")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
                       "add_block", _("Create a new block")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
                       "add_connection", _("Create a new connection")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool",
                       "add_powerrail", _("Create a new power rail")),
                      (True, FREEDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool",
                       "add_contact", _("Create a new contact"))],
            "ST":    [],
            "IL":    [],
            "debug": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
                       "move", _("Move the view"))],
        }

    def _init_coll_MenuBar_Menus(self, parent):
        parent.Append(menu=self.FileMenu, title=_(u'&File'))
        parent.Append(menu=self.EditMenu, title=_(u'&Edit'))
        parent.Append(menu=self.DisplayMenu, title=_(u'&Display'))
        parent.Append(menu=self.HelpMenu, title=_(u'&Help'))

    def _init_coll_FileMenu_Items(self, parent):
        pass

    def _init_coll_AddMenu_Items(self, parent, add_config=True):
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE,
                   kind=wx.ITEM_NORMAL, text=_(u'&Data Type'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTION,
                   kind=wx.ITEM_NORMAL, text=_(u'&Function'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK,
                   kind=wx.ITEM_NORMAL, text=_(u'Function &Block'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDPROGRAM,
                   kind=wx.ITEM_NORMAL, text=_(u'&Program'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDRESOURCE,
                   kind=wx.ITEM_NORMAL, text=_(u'&Resource'))
        if add_config:
            AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
                       kind=wx.ITEM_NORMAL, text=_(u'&Configuration'))

    def _init_coll_EditMenu_Items(self, parent):
        AppendMenu(parent, help='', id=wx.ID_UNDO,
                   kind=wx.ITEM_NORMAL, text=_(u'Undo') + '\tCTRL+Z')
        AppendMenu(parent, help='', id=wx.ID_REDO,
                   kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y')
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_CUT,
                   kind=wx.ITEM_NORMAL, text=_(u'Cut') + '\tCTRL+X')
        AppendMenu(parent, help='', id=wx.ID_COPY,
                   kind=wx.ITEM_NORMAL, text=_(u'Copy') + '\tCTRL+C')
        AppendMenu(parent, help='', id=wx.ID_PASTE,
                   kind=wx.ITEM_NORMAL, text=_(u'Paste') + '\tCTRL+V')
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_FIND,
                   kind=wx.ITEM_NORMAL, text=_(u'Find') + '\tCTRL+F')
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDNEXT,
                   kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K')
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
                   kind=wx.ITEM_NORMAL, text=_(u'Find Previous') + '\tCTRL+SHIFT+K')
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT,
                   kind=wx.ITEM_NORMAL, text=_(u'Search in Project') + '\tCTRL+SHIFT+F')
        parent.AppendSeparator()
        add_menu = wx.Menu(title='')
        self._init_coll_AddMenu_Items(add_menu)
        parent.AppendMenu(wx.ID_ADD, _(u"&Add Element"), add_menu)
        AppendMenu(parent, help='', id=wx.ID_SELECTALL,
                   kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A')
        AppendMenu(parent, help='', id=wx.ID_DELETE,
                   kind=wx.ITEM_NORMAL, text=_(u'&Delete'))
        self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO)
        self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO)
        # self.Bind(wx.EVT_MENU, self.OnEnableUndoRedoMenu, id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO)
        self.Bind(wx.EVT_MENU, self.OnCutMenu, id=wx.ID_CUT)
        self.Bind(wx.EVT_MENU, self.OnCopyMenu, id=wx.ID_COPY)
        self.Bind(wx.EVT_MENU, self.OnPasteMenu, id=wx.ID_PASTE)
        self.Bind(wx.EVT_MENU, self.OnFindMenu, id=wx.ID_FIND)
        self.Bind(wx.EVT_MENU, self.OnFindNextMenu,
                  id=ID_PLCOPENEDITOREDITMENUFINDNEXT)
        self.Bind(wx.EVT_MENU, self.OnFindPreviousMenu,
                  id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS)
        self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu,
                  id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT)
        self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu,
                  id=ID_PLCOPENEDITOREDITMENUADDDATATYPE)
        self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("function"),
                  id=ID_PLCOPENEDITOREDITMENUADDFUNCTION)
        self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("functionBlock"),
                  id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK)
        self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("program"),
                  id=ID_PLCOPENEDITOREDITMENUADDPROGRAM)
        self.Bind(wx.EVT_MENU, self.AddResourceMenu,
                  id=ID_PLCOPENEDITOREDITMENUADDRESOURCE)
        self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu,
                  id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION)
        self.Bind(wx.EVT_MENU, self.OnSelectAllMenu, id=wx.ID_SELECTALL)
        self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=wx.ID_DELETE)

        self.AddToMenuToolBar([(wx.ID_UNDO, "undo", _(u'Undo'), None),
                               (wx.ID_REDO, "redo", _(u'Redo'), None),
                               None,
                               (wx.ID_CUT, "cut", _(u'Cut'), None),
                               (wx.ID_COPY, "copy", _(u'Copy'), None),
                               (wx.ID_PASTE, "paste", _(u'Paste'), None),
                               None,
                               (ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, "find", _(u'Search in Project'), None),
                               (ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN, "fullscreen", _(u'Toggle fullscreen mode'), None)])

    def _init_coll_DisplayMenu_Items(self, parent):
        AppendMenu(parent, help='', id=wx.ID_REFRESH,
                   kind=wx.ITEM_NORMAL, text=_(u'Refresh') + '\tCTRL+R')
        if self.EnableDebug:
            AppendMenu(parent, help='', id=wx.ID_CLEAR,
                       kind=wx.ITEM_NORMAL, text=_(u'Clear Errors') + '\tCTRL+K')
        parent.AppendSeparator()
        zoommenu = wx.Menu(title='')
        parent.AppendMenu(wx.ID_ZOOM_FIT, _("Zoom"), zoommenu)
        for idx, value in enumerate(ZOOM_FACTORS):
            new_id = wx.NewId()
            AppendMenu(zoommenu, help='', id=new_id,
                       kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%")
            self.Bind(wx.EVT_MENU, self.GenerateZoomFunction(idx), id=new_id)

        parent.AppendSeparator()
        AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE,
                   kind=wx.ITEM_NORMAL, text=_(u'Switch perspective') + '\tF12')
        self.Bind(wx.EVT_MENU, self.SwitchPerspective, id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE)

        AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN,
                   kind=wx.ITEM_NORMAL, text=_(u'Full screen') + '\tShift-F12')
        self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN)

        AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE,
                   kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective'))
        self.Bind(wx.EVT_MENU, self.OnResetPerspective, id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE)

        self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH)
        if self.EnableDebug:
            self.Bind(wx.EVT_MENU, self.OnClearErrorsMenu, id=wx.ID_CLEAR)

    def _init_coll_HelpMenu_Items(self, parent):
        pass

    def _init_utils(self):
        self.MenuBar = wx.MenuBar()

        self.FileMenu = wx.Menu(title='')
        self.EditMenu = wx.Menu(title='')
        self.DisplayMenu = wx.Menu(title='')
        self.HelpMenu = wx.Menu(title='')

        self._init_coll_MenuBar_Menus(self.MenuBar)
        self._init_coll_FileMenu_Items(self.FileMenu)
        self._init_coll_EditMenu_Items(self.EditMenu)
        self._init_coll_DisplayMenu_Items(self.DisplayMenu)
        self._init_coll_HelpMenu_Items(self.HelpMenu)

    def _init_icon(self, parent):
        if self.icon:
            self.SetIcon(self.icon)
        elif parent and parent.icon:
            self.SetIcon(parent.icon)

    def _init_ctrls(self, prnt):
        self._init_icon(prnt)
        self.SetClientSize(wx.Size(1000, 600))
        self.Bind(wx.EVT_ACTIVATE, self.OnActivated)

        self.TabsImageList = wx.ImageList(31, 16)
        self.TabsImageListIndexes = {}

        # -----------------------------------------------------------------------
        #                          Creating main structure
        # -----------------------------------------------------------------------

        self.AUIManager = wx.aui.AuiManager(self)
        self.AUIManager.SetDockSizeConstraint(0.5, 0.5)
        self.Panes = {}

        self.LeftNoteBook = wx.aui.AuiNotebook(
            self, ID_PLCOPENEDITORLEFTNOTEBOOK,
            style=(wx.aui.AUI_NB_TOP |
                   wx.aui.AUI_NB_TAB_SPLIT |
                   wx.aui.AUI_NB_TAB_MOVE |
                   wx.aui.AUI_NB_SCROLL_BUTTONS |
                   wx.aui.AUI_NB_TAB_EXTERNAL_MOVE))
        self.LeftNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
                               self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(
            self.LeftNoteBook,
            wx.aui.AuiPaneInfo().Name("ProjectPane").Left().Layer(1).
            BestSize(wx.Size(300, 500)).CloseButton(False))

        self.BottomNoteBook = wx.aui.AuiNotebook(
            self, ID_PLCOPENEDITORBOTTOMNOTEBOOK,
            style=(wx.aui.AUI_NB_TOP |
                   wx.aui.AUI_NB_TAB_SPLIT |
                   wx.aui.AUI_NB_TAB_MOVE |
                   wx.aui.AUI_NB_SCROLL_BUTTONS |
                   wx.aui.AUI_NB_TAB_EXTERNAL_MOVE))
        self.BottomNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
                                 self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(
            self.BottomNoteBook,
            wx.aui.AuiPaneInfo().Name("ResultPane").Bottom().Layer(0).
            BestSize(wx.Size(800, 300)).CloseButton(False))

        self.RightNoteBook = wx.aui.AuiNotebook(
            self, ID_PLCOPENEDITORRIGHTNOTEBOOK,
            style=(wx.aui.AUI_NB_TOP |
                   wx.aui.AUI_NB_TAB_SPLIT |
                   wx.aui.AUI_NB_TAB_MOVE |
                   wx.aui.AUI_NB_SCROLL_BUTTONS |
                   wx.aui.AUI_NB_TAB_EXTERNAL_MOVE))
        self.RightNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
                                self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(
            self.RightNoteBook,
            wx.aui.AuiPaneInfo().Name("LibraryPane").Right().Layer(0).
            BestSize(wx.Size(250, 400)).CloseButton(False))

        self.TabsOpened = wx.aui.AuiNotebook(
            self, ID_PLCOPENEDITORTABSOPENED,
            style=(wx.aui.AUI_NB_DEFAULT_STYLE |
                   wx.aui.AUI_NB_WINDOWLIST_BUTTON))
        self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGING,
                             self.OnPouSelectedChanging)
        self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED,
                             self.OnPouSelectedChanged)
        self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE,
                             self.OnPageClose)
        self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG,
                             self.OnPageDragged)
        self.AUIManager.AddPane(self.TabsOpened,
                                wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane"))

        # -----------------------------------------------------------------------
        #                    Creating PLCopen Project Types Tree
        # -----------------------------------------------------------------------

        self.MainTabs = {}

        self.ProjectPanel = wx.SplitterWindow(
            id=ID_PLCOPENEDITORPROJECTPANEL,
            name='ProjectPanel', parent=self.LeftNoteBook, point=wx.Point(0, 0),
            size=wx.Size(0, 0), style=wx.SP_3D)

        self.ProjectTree = CustomTree(id=ID_PLCOPENEDITORPROJECTTREE,
                                      name='ProjectTree',
                                      parent=self.ProjectPanel,
                                      pos=wx.Point(0, 0), size=wx.Size(0, 0),
                                      style=wx.SUNKEN_BORDER,
                                      agwStyle=(wx.TR_HAS_BUTTONS |
                                                wx.TR_SINGLE |
                                                wx.TR_EDIT_LABELS))
        self.ProjectTree.SetBackgroundBitmap(GetBitmap("custom_tree_background"),
                                             wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM)
        add_menu = wx.Menu()
        self._init_coll_AddMenu_Items(add_menu)
        self.ProjectTree.SetAddMenu(add_menu)
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnProjectTreeRightUp,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.ProjectTree.Bind(wx.EVT_LEFT_UP, self.OnProjectTreeLeftUp)
        self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnProjectTreeItemChanging,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnProjectTreeBeginDrag,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnProjectTreeItemBeginEdit,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnProjectTreeItemEndEdit,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnProjectTreeItemActivated,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.ProjectTree.Bind(wx.EVT_MOTION, self.OnProjectTreeMotion)

        # -----------------------------------------------------------------------
        #        Creating PLCopen Project POU Instance Variables Panel
        # -----------------------------------------------------------------------

        self.PouInstanceVariablesPanel = PouInstanceVariablesPanel(self.ProjectPanel, self, self.Controler, self.EnableDebug)

        self.MainTabs["ProjectPanel"] = (self.ProjectPanel, _("Project"))
        self.LeftNoteBook.AddPage(*self.MainTabs["ProjectPanel"])

        self.ProjectPanel.SplitHorizontally(self.ProjectTree, self.PouInstanceVariablesPanel, 300)

        # -----------------------------------------------------------------------
        #                            Creating Tool Bar
        # -----------------------------------------------------------------------

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

        EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR,
                                   wx.DefaultPosition, wx.DefaultSize,
                                   wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
        EditorToolBar.SetToolBitmapSize(wx.Size(25, 25))
        EditorToolBar.AddRadioTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION,
                                   GetBitmap("select"),
                                   wx.NullBitmap,
                                   _("Select an object"))
        EditorToolBar.Realize()
        self.Panes["EditorToolBar"] = EditorToolBar
        self.AUIManager.AddPane(EditorToolBar, wx.aui.AuiPaneInfo().
                                Name("EditorToolBar").Caption(_("Editor ToolBar")).
                                ToolbarPane().Top().Position(1).
                                LeftDockable(False).RightDockable(False))

        self.Bind(wx.EVT_MENU, self.OnSelectionTool,
                  id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION)

        # -----------------------------------------------------------------------
        #                            Creating Search Panel
        # -----------------------------------------------------------------------

        self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self)
        self.MainTabs["SearchResultPanel"] = (self.SearchResultPanel, _("Search"))
        self.BottomNoteBook.AddPage(*self.MainTabs["SearchResultPanel"])

        # -----------------------------------------------------------------------
        #                            Creating Library Panel
        # -----------------------------------------------------------------------

        self.LibraryPanel = LibraryPanel(self, True)
        self.MainTabs["LibraryPanel"] = (self.LibraryPanel, _("Library"))
        self.RightNoteBook.AddPage(*self.MainTabs["LibraryPanel"])

        self._init_utils()
        self.SetMenuBar(self.MenuBar)

        if self.EnableDebug:
            self.DebugVariablePanel = DebugVariablePanel(self.RightNoteBook, self.Controler, self)
            self.MainTabs["DebugVariablePanel"] = (self.DebugVariablePanel, _("Debugger"))
            self.RightNoteBook.AddPage(*self.MainTabs["DebugVariablePanel"])

        self.AUIManager.Update()

    def __init__(self, parent, enable_debug=False):
        wx.Frame.__init__(self, id=ID_PLCOPENEDITOR, name='IDEFrame',
                          parent=parent, pos=wx.DefaultPosition,
                          size=wx.Size(1000, 600),
                          style=wx.DEFAULT_FRAME_STYLE)

        self.UNEDITABLE_NAMES_DICT = dict([(_(n), n) for n in UNEDITABLE_NAMES])

        self.Controler = None
        self.Config = wx.ConfigBase.Get()
        self.EnableDebug = enable_debug

        self.InitEditorToolbarItems()
        self._init_ctrls(parent)

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

        # Icons for languages
        for language in LANGUAGES:
            self.TreeImageDict[language] = self.TreeImageList.Add(GetBitmap(language))

        # Icons for other items
        for imgname, itemtype in [
                # editables
                ("PROJECT",        ITEM_PROJECT),
                # ("POU",          ITEM_POU),
                # ("VARIABLE",     ITEM_VARIABLE),
                ("TRANSITION",     ITEM_TRANSITION),
                ("ACTION",         ITEM_ACTION),
                ("CONFIGURATION",  ITEM_CONFIGURATION),
                ("RESOURCE",       ITEM_RESOURCE),
                ("DATATYPE",       ITEM_DATATYPE),
                # uneditables
                ("DATATYPES",      ITEM_DATATYPES),
                ("FUNCTION",       ITEM_FUNCTION),
                ("FUNCTIONBLOCK",  ITEM_FUNCTIONBLOCK),
                ("PROGRAM",        ITEM_PROGRAM),
                ("VAR_LOCAL",      ITEM_VAR_LOCAL),
                ("VAR_LOCAL",      ITEM_VAR_GLOBAL),
                ("VAR_LOCAL",      ITEM_VAR_EXTERNAL),
                ("VAR_LOCAL",      ITEM_VAR_TEMP),
                ("VAR_INPUT",      ITEM_VAR_INPUT),
                ("VAR_OUTPUT",     ITEM_VAR_OUTPUT),
                ("VAR_INOUT",      ITEM_VAR_INOUT),
                ("TRANSITIONS",    ITEM_TRANSITIONS),
                ("ACTIONS",        ITEM_ACTIONS),
                ("CONFIGURATIONS", ITEM_CONFIGURATIONS),
                ("RESOURCES",      ITEM_RESOURCES),
                ("PROPERTIES",     ITEM_PROPERTIES)]:
            self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))

        # Assign icon list to TreeCtrls
        self.ProjectTree.SetImageList(self.TreeImageList)
        self.PouInstanceVariablesPanel.SetTreeImageList(self.TreeImageList)

        self.CurrentEditorToolBar = []
        self.CurrentMenu = None
        self.SelectedItem = None
        self.LastToolTipItem = None
        self.SearchParams = None
        self.Highlights = {}
        self.DrawingMode = FREEDRAWING_MODE
        # self.DrawingMode = DRIVENDRAWING_MODE
        self.AuiTabCtrl = []

        # Save default perspective
        notebooks = {}
        for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
                                     (self.BottomNoteBook, "bottomnotebook"),
                                     (self.RightNoteBook, "rightnotebook")]:
            notebooks[entry_name] = self.SaveTabLayout(notebook)
        self.DefaultPerspective = {
            "perspective": self.AUIManager.SavePerspective(),
            "notebooks": notebooks,
        }

        # Initialize Printing configuring elements
        self.PrintData = wx.PrintData()
        self.PrintData.SetPaperId(wx.PAPER_A4)
        self.PrintData.SetPrintMode(wx.PRINT_MODE_PRINTER)
        self.PageSetupData = wx.PageSetupDialogData(self.PrintData)
        self.PageSetupData.SetMarginTopLeft(wx.Point(10, 15))
        self.PageSetupData.SetMarginBottomRight(wx.Point(10, 20))

        self.SetRefreshFunctions()
        self.SetDeleteFunctions()

        wx.CallAfter(self.InitFindDialog)

    def __del__(self):
        self.FindDialog.Destroy()

    def InitFindDialog(self):
        self.FindDialog = FindInPouDialog(self)
        self.FindDialog.Hide()

    def Show(self):
        wx.Frame.Show(self)
        wx.CallAfter(self.RestoreLastState)

    def OnActivated(self, event):
        if event.GetActive():
            wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        event.Skip()

    def SelectTab(self, tab):
        for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]:
            idx = notebook.GetPageIndex(tab)
            if idx != wx.NOT_FOUND and idx != notebook.GetSelection():
                notebook.SetSelection(idx)
                return

    # -------------------------------------------------------------------------------
    #                Saving and restoring frame organization functions
    # -------------------------------------------------------------------------------

    def GetTabInfos(self, tab):
        for page_name, (page_ref, _page_title) in self.MainTabs.iteritems():
            if page_ref == tab:
                return ("main", page_name)
        return None

    def SaveTabLayout(self, notebook):
        tabs = []
        for child in notebook.GetChildren():
            if isinstance(child, wx.aui.AuiTabCtrl):
                if child.GetPageCount() > 0:
                    pos = child.GetPosition()
                    tab = {"pos": (pos.x, pos.y), "pages": []}
                    tab_size = child.GetSize()
                    for page_idx in xrange(child.GetPageCount()):
                        page = child.GetWindowFromIdx(page_idx)
                        if "size" not in tab:
                            tab["size"] = (tab_size[0], tab_size[1] + page.GetSize()[1])
                        tab_infos = self.GetTabInfos(page)
                        if tab_infos is not None:
                            tab["pages"].append((tab_infos, page_idx == child.GetActivePage()))
                    tabs.append(tab)
        tabs.sort(lambda x, y: cmp(x["pos"], y["pos"]))
        size = notebook.GetSize()
        return ComputeTabsLayout(tabs, wx.Rect(1, 1, size[0] - NOTEBOOK_BORDER, size[1] - NOTEBOOK_BORDER))

    def LoadTab(self, notebook, page_infos):
        if page_infos[0] == "main":
            infos = self.MainTabs.get(page_infos[1])
            if infos is not None:
                page_ref, page_title = infos
                notebook.AddPage(page_ref, page_title)
                return notebook.GetPageIndex(page_ref)
        elif page_infos[0] == "editor":
            tagname = page_infos[1]
            page_ref = self.EditProjectElement(GetElementType(tagname), tagname)
            if page_ref is not None:
                page_ref.RefreshView()
                return notebook.GetPageIndex(page_ref)
        elif page_infos[0] == "debug":
            instance_path = page_infos[1]
            instance_infos = self.Controler.GetInstanceInfos(instance_path, self.EnableDebug)
            if instance_infos is not None:
                return notebook.GetPageIndex(self.OpenDebugViewer(instance_infos["class"], instance_path, instance_infos["type"]))
        return None

    def LoadTabLayout(self, notebook, tabs, mode="all", first_index=None):
        if isinstance(tabs, list):
            if len(tabs) == 0:
                return
            raise ValueError("Not supported")

        if "split" in tabs:
            self.LoadTabLayout(notebook, tabs["others"])

            split_dir, _split_ratio = tabs["split"]
            first_index = self.LoadTabLayout(notebook, tabs["tab"], mode="first")
            notebook.Split(first_index, split_dir)
            self.LoadTabLayout(notebook, tabs["tab"], mode="others", first_index=first_index)

        elif mode == "first":
            return self.LoadTab(notebook, tabs["pages"][0][0])
        else:
            selected = first_index
            if mode == "others":
                add_tabs = tabs["pages"][1:]
            else:
                add_tabs = tabs["pages"]
            for page_infos, page_selected in add_tabs:
                page_idx = self.LoadTab(notebook, page_infos)
                if page_selected:
                    selected = page_idx
            if selected is not None:
                wx.CallAfter(notebook.SetSelection, selected)

    def ResetPerspective(self):
        if self.DefaultPerspective is not None:
            self.AUIManager.LoadPerspective(self.DefaultPerspective["perspective"])

            for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]:
                for dummy in xrange(notebook.GetPageCount()):
                    notebook.RemovePage(0)

            notebooks = self.DefaultPerspective["notebooks"]
            for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
                                         (self.BottomNoteBook, "bottomnotebook"),
                                         (self.RightNoteBook, "rightnotebook")]:
                self.LoadTabLayout(notebook, notebooks.get(entry_name))

            self._Refresh(EDITORTOOLBAR)

    def RestoreLastState(self):
        frame_size = None
        if self.Config.HasEntry("framesize"):
            frame_size = cPickle.loads(str(self.Config.Read("framesize")))

        if frame_size is None:
            self.Maximize()
        else:
            self.SetClientSize(frame_size)

    def SaveLastState(self):
        if not self.IsMaximized():
            self.Config.Write("framesize", cPickle.dumps(self.GetClientSize()))
        elif self.Config.HasEntry("framesize"):
            self.Config.DeleteEntry("framesize")

        self.Config.Flush()

    # -------------------------------------------------------------------------------
    #                               General Functions
    # -------------------------------------------------------------------------------

    def SetRefreshFunctions(self):
        self.RefreshFunctions = {
            TITLE: self.RefreshTitle,
            EDITORTOOLBAR: self.RefreshEditorToolBar,
            FILEMENU: self.RefreshFileMenu,
            EDITMENU: self.RefreshEditMenu,
            DISPLAYMENU: self.RefreshDisplayMenu,
            PROJECTTREE: self.RefreshProjectTree,
            POUINSTANCEVARIABLESPANEL: self.RefreshPouInstanceVariablesPanel,
            LIBRARYTREE: self.RefreshLibraryPanel,
            SCALING: self.RefreshScaling,
            PAGETITLES: self.RefreshPageTitles}

    def _Refresh(self, *elements):
        """Call Editor refresh functions.

        :param elements: List of elements to refresh.
        """
        try:
            for element in elements:
                self.RefreshFunctions[element]()
        except wx.PyDeadObjectError:
            # ignore exceptions caused by refresh while quitting
            pass

    def OnPageClose(self, event):
        """Callback function when AUINotebook Page closed with CloseButton

        :param event: AUINotebook Event.
        """
        selected = self.TabsOpened.GetSelection()
        if selected > -1:
            window = self.TabsOpened.GetPage(selected)

            if window.CheckSaveBeforeClosing():

                # Refresh all window elements that have changed
                wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
                wx.CallAfter(self.RefreshTabCtrlEvent)
                wx.CallAfter(self.CloseFindInPouDialog)
                event.Skip()
            else:
                event.Veto()

    def GetCopyBuffer(self, primary_selection=False):
        data = None
        if primary_selection and wx.Platform == '__WXMSW__':
            return data
        else:
            wx.TheClipboard.UsePrimarySelection(primary_selection)

        if not wx.TheClipboard.IsOpened():
            dataobj = wx.TextDataObject()
            if wx.TheClipboard.Open():
                success = False
                try:
                    success = wx.TheClipboard.GetData(dataobj)
                except wx._core.PyAssertionError:
                    pass
                wx.TheClipboard.Close()
                if success:
                    data = dataobj.GetText()
        return data

    def SetCopyBuffer(self, text, primary_selection=False):
        if primary_selection and wx.Platform == '__WXMSW__':
            return
        else:
            wx.TheClipboard.UsePrimarySelection(primary_selection)
        if not wx.TheClipboard.IsOpened():
            data = wx.TextDataObject()
            data.SetText(text)
            if wx.TheClipboard.Open():
                wx.TheClipboard.SetData(data)
                wx.TheClipboard.Flush()
                wx.TheClipboard.Close()
        wx.CallAfter(self.RefreshEditMenu)

    def GetDrawingMode(self):
        return self.DrawingMode

    def RefreshScaling(self):
        for i in xrange(self.TabsOpened.GetPageCount()):
            editor = self.TabsOpened.GetPage(i)
            editor.RefreshScaling()

    def EditProjectSettings(self):
        old_values = self.Controler.GetProjectProperties()
        dialog = ProjectDialog(self)
        dialog.SetValues(old_values)
        if dialog.ShowModal() == wx.ID_OK:
            new_values = dialog.GetValues()
            new_values["creationDateTime"] = old_values["creationDateTime"]
            if new_values != old_values:
                self.Controler.SetProjectProperties(None, new_values)
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU,
                              PROJECTTREE, POUINSTANCEVARIABLESPANEL, SCALING)
        dialog.Destroy()

    # -------------------------------------------------------------------------------
    #                            Notebook Unified Functions
    # -------------------------------------------------------------------------------

    def AddPage(self, window, text):
        """Function that add a tab in Notebook, calling refresh for tab DClick event
        for wx.aui.AUINotebook.

        :param window: Panel to display in tab.
        :param text: title for the tab ctrl.
        """
        self.TabsOpened.AddPage(window, text)
        self.RefreshTabCtrlEvent()

    def DeletePage(self, window):
        for idx in xrange(self.TabsOpened.GetPageCount()):
            if self.TabsOpened.GetPage(idx) == window:
                self.TabsOpened.DeletePage(idx)
                self.RefreshTabCtrlEvent()
                return

    def DeleteAllPages(self):
        """Function that fix difference in deleting all tabs between
        wx.Notebook and wx.aui.AUINotebook.
        """
        for dummy in xrange(self.TabsOpened.GetPageCount()):
            self.TabsOpened.DeletePage(0)
        self.RefreshTabCtrlEvent()

    def SetPageBitmap(self, idx, bitmap):
        """Function that fix difference in setting picture on tab between
        wx.Notebook and wx.aui.AUINotebook.

        :param idx: Tab index.
        :param bitmap: wx.Bitmap to define on tab.
        :returns: True if operation succeeded
        """
        return self.TabsOpened.SetPageBitmap(idx, bitmap)

    # -------------------------------------------------------------------------------
    #                         Dialog Message Functions
    # -------------------------------------------------------------------------------

    def ShowErrorMessage(self, message):
        """Function displaying an Error dialog in editor.

        :param message: The message to display.
        """
        dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR)
        dialog.ShowModal()
        dialog.Destroy()

    def CheckSaveBeforeClosing(self, title=_("Close Project")):
        """Function displaying an question dialog if project is not saved"

        :returns: False if closing cancelled.
        """
        if not self.Controler.ProjectIsSaved():
            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.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

    # -------------------------------------------------------------------------------
    #                            File Menu Functions
    # -------------------------------------------------------------------------------

    def RefreshFileMenu(self):
        pass

    def ResetView(self):
        self.DeleteAllPages()
        self.ProjectTree.DeleteAllItems()
        self.ProjectTree.Enable(False)
        self.PouInstanceVariablesPanel.ResetView()
        self.LibraryPanel.ResetTree()
        self.LibraryPanel.SetController(None)
        if self.EnableDebug:
            self.DebugVariablePanel.ResetView()
        self.Controler = None

    def OnCloseTabMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected >= 0:
            self.TabsOpened.DeletePage(selected)
            if self.TabsOpened.GetPageCount() > 0:
                new_index = min(selected, self.TabsOpened.GetPageCount() - 1)
                self.TabsOpened.SetSelection(new_index)
        # Refresh all window elements that have changed
        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        self.RefreshTabCtrlEvent()

    def OnPageSetupMenu(self, event):
        dialog = wx.PageSetupDialog(self, self.PageSetupData)
        if dialog.ShowModal() == wx.ID_OK:
            self.PageSetupData = wx.PageSetupDialogData(dialog.GetPageSetupData())
            self.PrintData = wx.PrintData(self.PageSetupData.GetPrintData())
        dialog.Destroy()

    def OnPreviewMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            data = wx.PrintDialogData(self.PrintData)
            properties = self.Controler.GetProjectProperties(window.IsDebugging())
            page_size = map(int, properties["pageSize"])
            margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight())
            printout = GraphicPrintout(window, page_size, margins, True)
            printout2 = GraphicPrintout(window, page_size, margins, True)
            preview = wx.PrintPreview(printout, printout2, data)

            if preview.Ok():
                preview_frame = wx.PreviewFrame(preview, self, _("Print preview"), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT)

                preview_frame.Initialize()

                preview_canvas = preview.GetCanvas()
                preview_canvas.SetMinSize(preview_canvas.GetVirtualSize())
                preview_frame.Fit()

                preview_frame.Show(True)

    def OnPrintMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            dialog_data = wx.PrintDialogData(self.PrintData)
            dialog_data.SetToPage(1)
            properties = self.Controler.GetProjectProperties(window.IsDebugging())
            page_size = map(int, properties["pageSize"])
            margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight())
            printer = wx.Printer(dialog_data)
            printout = GraphicPrintout(window, page_size, margins)

            if not printer.Print(self, printout, True) and printer.GetLastError() != wx.PRINTER_CANCELLED:
                self.ShowErrorMessage(_("There was a problem printing.\nPerhaps your current printer is not set correctly?"))
            printout.Destroy()

    def OnPropertiesMenu(self, event):
        self.EditProjectSettings()

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

    # -------------------------------------------------------------------------------
    #                            Edit Menu Functions
    # -------------------------------------------------------------------------------

    def RefreshEditMenu(self):
        MenuToolBar = self.Panes["MenuToolBar"]
        if self.Controler is not None:
            selected = self.TabsOpened.GetSelection()
            if selected > -1:
                window = self.TabsOpened.GetPage(selected)
                undo, redo = window.GetBufferState()
            else:
                undo, redo = self.Controler.GetBufferState()
            self.EditMenu.Enable(wx.ID_UNDO, undo)
            MenuToolBar.EnableTool(wx.ID_UNDO, undo)
            self.EditMenu.Enable(wx.ID_REDO, redo)
            MenuToolBar.EnableTool(wx.ID_REDO, redo)
            # self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True)
            # self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO,
            #                self.Controler.IsProjectBufferEnabled())
            self.EditMenu.Enable(wx.ID_FIND, selected > -1)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT,
                                 selected > -1 and self.SearchParams is not None)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
                                 selected > -1 and self.SearchParams is not None)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
            MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
            self.EditMenu.Enable(wx.ID_ADD, True)
            self.EditMenu.Enable(wx.ID_DELETE, True)
            if self.TabsOpened.GetPageCount() > 0:
                self.EditMenu.Enable(wx.ID_CUT, True)
                MenuToolBar.EnableTool(wx.ID_CUT, True)
                self.EditMenu.Enable(wx.ID_COPY, True)
                MenuToolBar.EnableTool(wx.ID_COPY, True)
                if self.GetCopyBuffer() is not None:
                    self.EditMenu.Enable(wx.ID_PASTE, True)
                    MenuToolBar.EnableTool(wx.ID_PASTE, True)
                else:
                    self.EditMenu.Enable(wx.ID_PASTE, False)
                    MenuToolBar.EnableTool(wx.ID_PASTE, False)
                self.EditMenu.Enable(wx.ID_SELECTALL, True)
            else:
                self.EditMenu.Enable(wx.ID_CUT, False)
                MenuToolBar.EnableTool(wx.ID_CUT, False)
                self.EditMenu.Enable(wx.ID_COPY, False)
                MenuToolBar.EnableTool(wx.ID_COPY, False)
                self.EditMenu.Enable(wx.ID_PASTE, False)
                MenuToolBar.EnableTool(wx.ID_PASTE, False)
                self.EditMenu.Enable(wx.ID_SELECTALL, False)
        else:
            self.EditMenu.Enable(wx.ID_UNDO, False)
            MenuToolBar.EnableTool(wx.ID_UNDO, False)
            self.EditMenu.Enable(wx.ID_REDO, False)
            MenuToolBar.EnableTool(wx.ID_REDO, False)
            # self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, False)
            self.EditMenu.Enable(wx.ID_CUT, False)
            MenuToolBar.EnableTool(wx.ID_CUT, False)
            self.EditMenu.Enable(wx.ID_COPY, False)
            MenuToolBar.EnableTool(wx.ID_COPY, False)
            self.EditMenu.Enable(wx.ID_PASTE, False)
            MenuToolBar.EnableTool(wx.ID_PASTE, False)
            self.EditMenu.Enable(wx.ID_SELECTALL, False)
            self.EditMenu.Enable(wx.ID_FIND, False)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT, False)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, False)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False)
            MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False)
            self.EditMenu.Enable(wx.ID_ADD, False)
            self.EditMenu.Enable(wx.ID_DELETE, False)

    def CloseTabsWithoutModel(self, refresh=True):
        idxs = range(self.TabsOpened.GetPageCount())
        idxs.reverse()
        for idx in idxs:
            window = self.TabsOpened.GetPage(idx)
            if window.HasNoModel():
                self.TabsOpened.DeletePage(idx)
        if refresh:
            self.RefreshEditor()

    def OnUndoMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.Undo()
        else:
            self.Controler.LoadPrevious()
        self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE,
                      SCALING, PAGETITLES)

    def OnRedoMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.Redo()
        else:
            self.Controler.LoadNext()
        self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE,
                      SCALING, PAGETITLES)

    def OnEnableUndoRedoMenu(self, event):
        self.Controler.EnableProjectBuffer(event.IsChecked())
        self.RefreshEditMenu()

    OnCutMenu = GetShortcutKeyCallbackFunction("Cut")
    OnCopyMenu = GetShortcutKeyCallbackFunction("Copy")
    OnPasteMenu = GetShortcutKeyCallbackFunction("Paste")

    def OnSelectAllMenu(self, event):
        control = self.FindFocus()
        if control is not None and control.GetName() == "Viewer":
            control.Parent.SelectAll()
        elif isinstance(control, wx.stc.StyledTextCtrl):
            control.SelectAll()
        elif isinstance(control, wx.TextCtrl):
            control.SetSelection(0, control.GetLastPosition())
        elif isinstance(control, wx.ComboBox):
            control.SetMark(0, control.GetLastPosition() + 1)

    def SetDeleteFunctions(self):
        self.DeleteFunctions = {
            ITEM_DATATYPE: GetDeleteElementFunction(
                PLCControler.ProjectRemoveDataType,
                check_function=self.CheckDataTypeIsUsedBeforeDeletion),
            ITEM_POU: GetDeleteElementFunction(
                PLCControler.ProjectRemovePou,
                check_function=self.CheckPouIsUsedBeforeDeletion),
            ITEM_TRANSITION: GetDeleteElementFunction(
                PLCControler.ProjectRemovePouTransition, ITEM_POU),
            ITEM_ACTION: GetDeleteElementFunction(
                PLCControler.ProjectRemovePouAction, ITEM_POU),
            ITEM_CONFIGURATION: GetDeleteElementFunction(
                PLCControler.ProjectRemoveConfiguration),
            ITEM_RESOURCE: GetDeleteElementFunction(
                PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION)
        }

    def OnDeleteMenu(self, event):
        window = self.FindFocus()
        if window == self.ProjectTree or window is None:
            selected = self.ProjectTree.GetSelection()
            if selected is not None and selected.IsOk():
                function = self.DeleteFunctions.get(self.ProjectTree.GetPyData(selected)["type"], None)
                if function is not None:
                    function(self, selected)
                    self.CloseTabsWithoutModel()
                    self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE,
                                  POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
        elif isinstance(window, (Viewer, TextViewer)):
            event = wx.KeyEvent(wx.EVT_CHAR._getEvtType())
            event.m_keyCode = wx.WXK_DELETE
            window.ProcessEvent(event)

    def OnFindMenu(self, event):
        if not self.FindDialog.IsShown():
            self.FindDialog.Show()
            self.FindDialog.FindPattern.SetFocus()

    def CloseFindInPouDialog(self):
        selected = self.TabsOpened.GetSelection()
        if selected == -1 and self.FindDialog.IsShown():
            self.FindDialog.Hide()

    def OnFindNextMenu(self, event):
        self.FindInPou(1)

    def OnFindPreviousMenu(self, event):
        self.FindInPou(-1)

    def FindInPou(self, direction, search_params=None):
        if search_params is not None:
            self.SearchParams = search_params
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.Find(direction, self.SearchParams)

    def OnSearchInProjectMenu(self, event):
        dialog = SearchInProjectDialog(self)
        if dialog.ShowModal() == wx.ID_OK:
            criteria = dialog.GetCriteria()
            if len(criteria) > 0:
                result = self.Controler.SearchInProject(criteria)
                self.ClearSearchResults()
                self.SearchResultPanel.SetSearchResults(criteria, result)
                self.SelectTab(self.SearchResultPanel)

    # -------------------------------------------------------------------------------
    #                             Display Menu Functions
    # -------------------------------------------------------------------------------

    def RefreshDisplayMenu(self):
        if self.Controler is not None:
            if self.TabsOpened.GetPageCount() > 0:
                self.DisplayMenu.Enable(wx.ID_REFRESH, True)
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    window = self.TabsOpened.GetPage(selected)
                    if isinstance(window, Viewer):
                        self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, True)
                        zoommenu = self.DisplayMenu.FindItemById(wx.ID_ZOOM_FIT).GetSubMenu()
                        zoomitem = zoommenu.FindItemByPosition(window.GetScale())
                        zoomitem.Check(True)
                    else:
                        self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
                else:
                    self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
            else:
                self.DisplayMenu.Enable(wx.ID_REFRESH, False)
                self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
            if self.EnableDebug:
                self.DisplayMenu.Enable(wx.ID_CLEAR, True)
        else:
            self.DisplayMenu.Enable(wx.ID_REFRESH, False)
            if self.EnableDebug:
                self.DisplayMenu.Enable(wx.ID_CLEAR, False)
            self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)

    def OnRefreshMenu(self, event):
        self.RefreshEditor()

    def OnClearErrorsMenu(self, event):
        self.ClearErrors()

    def GenerateZoomFunction(self, idx):
        def ZoomFunction(event):
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                window = self.TabsOpened.GetPage(selected)
                window.SetScale(idx)
                window.RefreshVisibleElements()
                window.RefreshScrollBars()
            event.Skip()
        return ZoomFunction

    def OnResetPerspective(self, event):
        self.ResetPerspective()

    # -------------------------------------------------------------------------------
    #                      Project Editor Panels Management Functions
    # -------------------------------------------------------------------------------

    def OnPageDragged(self, event):
        wx.CallAfter(self.RefreshTabCtrlEvent)
        event.Skip()

    def OnAllowNotebookDnD(self, event):
        event.Allow()

    def RefreshTabCtrlEvent(self):
        auitabctrl = []
        for child in self.TabsOpened.GetChildren():
            if isinstance(child, wx.aui.AuiTabCtrl):
                auitabctrl.append(child)
                if child not in self.AuiTabCtrl:
                    child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child))
        self.AuiTabCtrl = auitabctrl
        if self.TabsOpened.GetPageCount() == 0:
            pane = self.AUIManager.GetPane(self.TabsOpened)
            if pane.IsMaximized():
                self.AUIManager.RestorePane(pane)
            self.AUIManager.Update()

    def EnsureTabVisible(self, tab):
        notebook = tab.GetParent()
        notebook.SetSelection(notebook.GetPageIndex(tab))

    def OnPouSelectedChanging(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected >= 0:
            window = self.TabsOpened.GetPage(selected)
            if not window.IsDebugging():
                window.ResetBuffer()
        event.Skip()

    def OnPouSelectedChanged(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected >= 0:
            window = self.TabsOpened.GetPage(selected)
            tagname = window.GetTagName()
            if not window.IsDebugging():
                self.SelectProjectTreeItem(tagname)
                self.PouInstanceVariablesPanel.SetPouType(tagname)
                window.RefreshView()
                self.EnsureTabVisible(self.LibraryPanel)
            else:
                instance_path = window.GetInstancePath()
                if tagname == "":
                    instance_path = instance_path.rsplit(".", 1)[0]
                    tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug)
                self.EnsureTabVisible(self.DebugVariablePanel)
                wx.CallAfter(self.PouInstanceVariablesPanel.SetPouType, tagname, instance_path)
        wx.CallAfter(self._Refresh, FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
        event.Skip()

    def RefreshEditor(self):
        selected = self.TabsOpened.GetSelection()
        if selected >= 0:
            window = self.TabsOpened.GetPage(selected)
            tagname = window.GetTagName()
            if not window.IsDebugging():
                self.SelectProjectTreeItem(tagname)
                self.PouInstanceVariablesPanel.SetPouType(tagname)
            else:
                instance_path = window.GetInstancePath()
                if tagname == "":
                    instance_path = instance_path.rsplit(".", 1)[0]
                    tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug)
                self.PouInstanceVariablesPanel.SetPouType(tagname, instance_path)
            for child in self.TabsOpened.GetChildren():
                if isinstance(child, wx.aui.AuiTabCtrl):
                    active_page = child.GetActivePage()
                    if active_page >= 0:
                        window = child.GetWindowFromIdx(active_page)
                        window.RefreshView()
            self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)

    def RefreshEditorNames(self, old_tagname, new_tagname):
        for i in xrange(self.TabsOpened.GetPageCount()):
            editor = self.TabsOpened.GetPage(i)
            if editor.GetTagName() == old_tagname:
                editor.SetTagName(new_tagname)

    def IsOpened(self, tagname):
        for idx in xrange(self.TabsOpened.GetPageCount()):
            if self.TabsOpened.GetPage(idx).IsViewing(tagname):
                return idx
        return None

    def RefreshPageTitles(self):
        for idx in xrange(self.TabsOpened.GetPageCount()):
            window = self.TabsOpened.GetPage(idx)
            icon = window.GetIcon()
            if icon is not None:
                self.SetPageBitmap(idx, icon)
            self.TabsOpened.SetPageText(idx, window.GetTitle())

    def GetTabsOpenedDClickFunction(self, tabctrl):
        def OnTabsOpenedDClick(event):
            pos = event.GetPosition()
            if tabctrl.TabHitTest(pos.x, pos.y, None):
                self.SwitchPerspective(event)
            event.Skip()
        return OnTabsOpenedDClick

    def SwitchPerspective(self, evt):
        pane = self.AUIManager.GetPane(self.TabsOpened)
        if pane.IsMaximized():
            self.AUIManager.RestorePane(pane)
        else:
            self.AUIManager.MaximizePane(pane)
        self.AUIManager.Update()

    def SwitchFullScrMode(self, evt):
        show = not self.IsFullScreen()
        self.ShowFullScreen(show)

    # -------------------------------------------------------------------------------
    #                         Types Tree Management Functions
    # -------------------------------------------------------------------------------

    def RefreshProjectTree(self):
        # Extract current selected item tagname
        selected = self.ProjectTree.GetSelection()
        if selected is not None and selected.IsOk():
            item_infos = self.ProjectTree.GetPyData(selected)
            tagname = item_infos.get("tagname", None)
        else:
            tagname = None

        # Refresh treectrl items according to project infos
        if self.Controler:
            infos = self.Controler.GetProjectInfos()
            root = self.ProjectTree.GetRootItem()
            if root is None or not root.IsOk():
                root = self.ProjectTree.AddRoot(infos["name"])
            self.GenerateProjectTreeBranch(root, infos)
            self.ProjectTree.Expand(root)

        # Select new item corresponding to previous selected item
        if tagname is not None:
            self.SelectProjectTreeItem(tagname)

    def GenerateProjectTreeBranch(self, root, infos, item_alone=False):
        to_delete = []
        item_name = infos["name"]
        if infos["type"] in ITEMS_UNEDITABLE:
            if len(infos["values"]) == 1:
                return self.GenerateProjectTreeBranch(root, infos["values"][0], True)
            item_name = _(item_name)
        self.ProjectTree.SetItemText(root, item_name)
        self.ProjectTree.SetPyData(root, infos)
        highlight_colours = self.Highlights.get(infos.get("tagname", None), (wx.Colour(255, 255, 255, 0), wx.BLACK))
        self.ProjectTree.SetItemBackgroundColour(root, highlight_colours[0])
        self.ProjectTree.SetItemTextColour(root, highlight_colours[1])
        self.ProjectTree.SetItemExtraImage(root, None)
        if infos["type"] == ITEM_POU:
            self.ProjectTree.SetItemImage(
                root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])])
            if item_alone:
                self.ProjectTree.SetItemExtraImage(root, self.Controler.GetPouType(infos["name"]))
        elif "icon" in infos and infos["icon"] is not None:
            icon_name = infos["icon"]
            if icon_name not in self.TreeImageDict:
                self.TreeImageDict[icon_name] = self.TreeImageList.Add(GetBitmap(icon_name))
            self.ProjectTree.SetItemImage(root, self.TreeImageDict[icon_name])
        elif infos["type"] in self.TreeImageDict:
            self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]])

        item, root_cookie = self.ProjectTree.GetFirstChild(root)
        for values in infos["values"]:
            if values["type"] not in ITEMS_UNEDITABLE or len(values["values"]) > 0:
                if item is None or not item.IsOk():
                    item = self.ProjectTree.AppendItem(root, "")
                    item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
                self.GenerateProjectTreeBranch(item, values)
                item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
        while item is not None and item.IsOk():
            to_delete.append(item)
            item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
        for item in to_delete:
            self.ProjectTree.Delete(item)

    TagNamePartsItemTypes = {
        "D": [ITEM_DATATYPE],
        "P": [ITEM_POU],
        "T": [ITEM_POU, ITEM_TRANSITION],
        "A": [ITEM_POU, ITEM_ACTION],
        "C": [ITEM_CONFIGURATION],
        "R": [ITEM_CONFIGURATION, ITEM_RESOURCE]}

    def SelectProjectTreeItem(self, tagname):
        result = False
        if self.ProjectTree is not None:
            root = self.ProjectTree.GetRootItem()
            if root is not None and root.IsOk():
                words = tagname.split("::")
                result = self.RecursiveProjectTreeItemSelection(
                    root, zip(words[1:], self.TagNamePartsItemTypes.get(words[0], [])))
        return result

    def RecursiveProjectTreeItemSelection(self, root, items):
        found = False
        item, root_cookie = self.ProjectTree.GetFirstChild(root)
        while item is not None and item.IsOk() and not found:
            item_infos = self.ProjectTree.GetPyData(item)
            if (item_infos["name"].split(":")[-1].strip(), item_infos["type"]) == items[0]:
                if len(items) == 1:
                    self.SelectedItem = item
                    self.ProjectTree.SelectItem(item)
                    self.ResetSelectedItem()
                    return True
                else:
                    found = self.RecursiveProjectTreeItemSelection(item, items[1:])
            else:
                found = self.RecursiveProjectTreeItemSelection(item, items)
            item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
        return found

    def ResetSelectedItem(self):
        self.SelectedItem = None

    def OnProjectTreeBeginDrag(self, event):
        selected_item = (self.SelectedItem
                         if self.SelectedItem is not None
                         else event.GetItem())
        if selected_item.IsOk() and self.ProjectTree.GetPyData(selected_item)["type"] == ITEM_POU:
            block_name = self.ProjectTree.GetItemText(selected_item)
            block_type = self.Controler.GetPouType(block_name)
            if block_type != "program":
                data = wx.TextDataObject(str((block_name, block_type, "")))
                dragSource = wx.DropSource(self.ProjectTree)
                dragSource.SetData(data)
                dragSource.DoDragDrop()
            self.ResetSelectedItem()

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

    def OnProjectTreeItemEndEdit(self, event):
        message = None
        abort = False
        new_name = event.GetLabel()
        if new_name != "":
            if not TestIdentifier(new_name):
                message = _("\"%s\" is not a valid identifier!") % new_name
            elif new_name.upper() in IEC_KEYWORDS:
                message = _("\"%s\" is a keyword. It can't be used!") % new_name
            else:
                item = event.GetItem()
                old_name = self.ProjectTree.GetItemText(item)
                item_infos = self.ProjectTree.GetPyData(item)
                if item_infos["type"] == ITEM_PROJECT:
                    self.Controler.SetProjectProperties(name=new_name)
                elif item_infos["type"] == ITEM_DATATYPE:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectDataTypeNames() if name != old_name]:
                        message = _("\"%s\" data type already exists!") % new_name
                        abort = True
                    if not abort:
                        self.Controler.ChangeDataTypeName(old_name, new_name)
                        self.RefreshEditorNames(ComputeDataTypeName(old_name),
                                                ComputeDataTypeName(new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_POU:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames() if name != old_name]:
                        message = _("\"%s\" pou already exists!") % new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        self.Controler.ChangePouName(old_name, new_name)
                        self.RefreshEditorNames(ComputePouName(old_name),
                                                ComputePouName(new_name))
                        self.RefreshLibraryPanel()
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_TRANSITION:
                    pou_item = self.ProjectTree.GetItemParent(event.GetItem())
                    pou_name = self.ProjectTree.GetItemText(pou_item)
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        message = _("A POU named \"%s\" already exists!") % new_name
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]:
                        message = _("A variable with \"%s\" as name already exists in this pou!") % new_name
                    else:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangePouTransitionName(words[1], old_name, new_name)
                        self.RefreshEditorNames(ComputePouTransitionName(words[1], old_name),
                                                ComputePouTransitionName(words[1], new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_ACTION:
                    pou_item = self.ProjectTree.GetItemParent(event.GetItem())
                    pou_name = self.ProjectTree.GetItemText(pou_item)
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        message = _("A POU named \"%s\" already exists!") % new_name
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]:
                        message = _("A variable with \"%s\" as name already exists in this pou!") % new_name
                    else:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangePouActionName(words[1], old_name, new_name)
                        self.RefreshEditorNames(ComputePouActionName(words[1], old_name),
                                                ComputePouActionName(words[1], new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_CONFIGURATION:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames() if name != old_name]:
                        message = _("\"%s\" config already exists!") % new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        self.Controler.ChangeConfigurationName(old_name, new_name)
                        self.RefreshEditorNames(ComputeConfigurationName(old_name),
                                                ComputeConfigurationName(new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_RESOURCE:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames()]:
                        message = _("\"%s\" config already exists!") % new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangeConfigurationResourceName(words[1], old_name, new_name)
                        self.RefreshEditorNames(ComputeConfigurationResourceName(words[1], old_name),
                                                ComputeConfigurationResourceName(words[1], new_name))
                        self.RefreshPageTitles()
            if message or abort:
                if message:
                    self.ShowErrorMessage(message)
                event.Veto()
            else:
                wx.CallAfter(self.RefreshProjectTree)
                self.RefreshEditor()
                self._Refresh(TITLE, FILEMENU, EDITMENU)
                event.Skip()

    def OnProjectTreeItemActivated(self, event):
        selected = event.GetItem()
        item_infos = self.ProjectTree.GetPyData(selected)
        if item_infos["type"] == ITEM_PROJECT:
            self.EditProjectSettings()
        else:
            if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU,
                                      ITEM_CONFIGURATION, ITEM_RESOURCE,
                                      ITEM_TRANSITION, ITEM_ACTION]:
                self.EditProjectElement(item_infos["type"], item_infos["tagname"])
            event.Skip()

    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"] in [ITEM_DATATYPE, ITEM_POU,
                                      ITEM_CONFIGURATION, ITEM_RESOURCE,
                                      ITEM_TRANSITION, ITEM_ACTION]:
                self.EditProjectElement(item_infos["type"], item_infos["tagname"], True)
                self.PouInstanceVariablesPanel.SetPouType(item_infos["tagname"])

    def OnProjectTreeLeftUp(self, event):
        if self.SelectedItem is not None:
            self.ProjectTree.SelectItem(self.SelectedItem)
            self.ProjectTreeItemSelect(self.SelectedItem)
            self.ResetSelectedItem()
        event.Skip()

    def OnProjectTreeMotion(self, event):
        if not event.Dragging():
            pt = wx.Point(event.GetX(), event.GetY())
            item, flags = self.ProjectTree.HitTest(pt)
            if item is not None and item.IsOk() and flags & wx.TREE_HITTEST_ONITEMLABEL:
                item_infos = self.ProjectTree.GetPyData(item)
                if item != self.LastToolTipItem and self.LastToolTipItem is not None:
                    self.ProjectTree.SetToolTip(None)
                    self.LastToolTipItem = None
                if self.LastToolTipItem != item and \
                   item_infos["type"] in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]:
                    bodytype = self.Controler.GetEditedElementBodyType(
                        item_infos["tagname"])
                    if item_infos["type"] == ITEM_POU:
                        block_type = {
                            "program": _("Program"),
                            "functionBlock": _("Function Block"),
                            "function": _("Function")
                        }[self.Controler.GetPouType(item_infos["name"])]
                    elif item_infos["type"] == ITEM_TRANSITION:
                        block_type = "Transition"
                    else:
                        block_type = "Action"
                    self.LastToolTipItem = item
                    wx.CallAfter(self.ProjectTree.SetToolTipString,
                                 "%s : %s : %s" % (
                                     block_type, bodytype, item_infos["name"]))
            elif self.LastToolTipItem is not None:
                self.ProjectTree.SetToolTip(None)
                self.LastToolTipItem = None
        event.Skip()

    def OnProjectTreeItemChanging(self, event):
        if self.ProjectTree.GetPyData(event.GetItem())["type"] not in ITEMS_UNEDITABLE and self.SelectedItem is None:
            self.SelectedItem = event.GetItem()
            event.Veto()
        else:
            event.Skip()

    def GetProjectElementWindow(self, element, tagname):
        new_window = None
        if self.Controler.GetEditedElement(tagname) is not None:
            new_window = None
            if element == ITEM_CONFIGURATION:
                new_window = ConfigurationEditor(self.TabsOpened, tagname, self, self.Controler)
                new_window.SetIcon(GetBitmap("CONFIGURATION"))
            elif element == ITEM_RESOURCE:
                new_window = ResourceEditor(self.TabsOpened, tagname, self, self.Controler)
                new_window.SetIcon(GetBitmap("RESOURCE"))
            elif element in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]:
                bodytype = self.Controler.GetEditedElementBodyType(tagname)
                if bodytype == "FBD":
                    new_window = Viewer(self.TabsOpened, tagname, self, self.Controler)
                    new_window.RefreshScaling(False)
                elif bodytype == "LD":
                    new_window = LD_Viewer(self.TabsOpened, tagname, self, self.Controler)
                    new_window.RefreshScaling(False)
                elif bodytype == "SFC":
                    new_window = SFC_Viewer(self.TabsOpened, tagname, self, self.Controler)
                    new_window.RefreshScaling(False)
                else:
                    new_window = TextViewer(self.TabsOpened, tagname, self, self.Controler)
                    new_window.SetTextSyntax(bodytype)
                    if bodytype == "IL":
                        new_window.SetKeywords(IL_KEYWORDS)
                    else:
                        new_window.SetKeywords(ST_KEYWORDS)
                if element == ITEM_POU:
                    pou_type = self.Controler.GetEditedElementType(tagname)[1].upper()
                    icon = GetBitmap(pou_type, bodytype)
                elif element == ITEM_TRANSITION:
                    icon = GetBitmap("TRANSITION", bodytype)
                elif element == ITEM_ACTION:
                    icon = GetBitmap("ACTION", bodytype)
                new_window.SetIcon(icon)
            elif element == ITEM_DATATYPE:
                new_window = DataTypeEditor(self.TabsOpened, tagname, self, self.Controler)
                new_window.SetIcon(GetBitmap("DATATYPE"))
        return new_window

    def EditProjectElement(self, element, tagname, onlyopened=False):
        openedidx = self.IsOpened(tagname)
        if openedidx is not None:
            old_selected = self.TabsOpened.GetSelection()
            if old_selected != openedidx:
                if old_selected >= 0:
                    self.TabsOpened.GetPage(old_selected).ResetBuffer()
                self.TabsOpened.SetSelection(openedidx)
            self._Refresh(FILEMENU, EDITMENU, EDITORTOOLBAR, PAGETITLES)
        elif not onlyopened:
            if isinstance(element, EditorPanel):
                new_window = element
            else:
                new_window = self.GetProjectElementWindow(element, tagname)

            if new_window is not None:
                self.AddPage(new_window, "")
                openedidx = self.IsOpened(tagname)
                old_selected = self.TabsOpened.GetSelection()
                if old_selected != openedidx:
                    if old_selected >= 0:
                        self.TabsOpened.GetPage(old_selected).ResetBuffer()
                for i in xrange(self.TabsOpened.GetPageCount()):
                    window = self.TabsOpened.GetPage(i)
                    if window == new_window:
                        self.TabsOpened.SetSelection(i)
                        window.SetFocus()
                self.RefreshPageTitles()
            return new_window

    def OnProjectTreeRightUp(self, event):
        item = event.GetItem()
        self.ProjectTree.SelectItem(item)
        self.ProjectTreeItemSelect(item)
        name = self.ProjectTree.GetItemText(item)
        item_infos = self.ProjectTree.GetPyData(item)

        menu = None
        if item_infos["type"] in ITEMS_UNEDITABLE + [ITEM_PROJECT]:
            if item_infos["type"] == ITEM_PROJECT:
                name = "Project"
            else:
                name = self.UNEDITABLE_NAMES_DICT[name]

            if name == "Data Types":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add DataType"))
                self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, id=new_id)

            elif name in ["Functions", "Function Blocks", "Programs", "Project"]:
                menu = wx.Menu(title='')

                if name != "Project":
                    new_id = wx.NewId()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add POU"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction({"Functions": "function", "Function Blocks": "functionBlock", "Programs": "program"}[name]), id=new_id)

                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Paste POU"))
                self.Bind(wx.EVT_MENU, self.OnPastePou, id=new_id)
                if self.GetCopyBuffer() is None:
                    menu.Enable(new_id, False)

            elif name == "Configurations":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Configuration"))
                self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu, id=new_id)

            elif name == "Transitions":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Transition"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetPyData(parent)["type"]
                while parent_type != ITEM_POU:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetPyData(parent)["type"]
                self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(self.ProjectTree.GetItemText(parent)), id=new_id)

            elif name == "Actions":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetPyData(parent)["type"]
                while parent_type != ITEM_POU:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetPyData(parent)["type"]
                self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(self.ProjectTree.GetItemText(parent)), id=new_id)

            elif name == "Resources":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetPyData(parent)["type"]
                while parent_type not in [ITEM_CONFIGURATION, ITEM_PROJECT]:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetPyData(parent)["type"]
                parent_name = None
                if parent_type == ITEM_PROJECT:
                    config_names = self.Controler.GetProjectConfigNames()
                    if len(config_names) > 0:
                        parent_name = config_names[0]
                else:
                    parent_name = self.ProjectTree.GetItemText(parent)
                if parent_name is not None:
                    self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(parent_name), id=new_id)

        else:
            if item_infos["type"] == ITEM_POU:
                menu = wx.Menu(title='')
                if self.Controler.GetPouBodyType(name) == "SFC":
                    new_id = wx.NewId()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Transition"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(name), id=new_id)
                    new_id = wx.NewId()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(name), id=new_id)
                    menu.AppendSeparator()

                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Copy POU"))
                self.Bind(wx.EVT_MENU, self.OnCopyPou, id=new_id)

                pou_type = self.Controler.GetPouType(name)
                if pou_type in ["function", "functionBlock"]:
                    change_menu = wx.Menu(title='')
                    if pou_type == "function":
                        new_id = wx.NewId()
                        AppendMenu(change_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Function Block"))
                        self.Bind(wx.EVT_MENU, self.GenerateChangePouTypeFunction(name, "functionBlock"), id=new_id)
                    new_id = wx.NewId()
                    AppendMenu(change_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Program"))
                    self.Bind(wx.EVT_MENU, self.GenerateChangePouTypeFunction(name, "program"), id=new_id)
                    menu.AppendMenu(wx.NewId(), _("Duplicate as..."), change_menu)
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Rename"))
                self.Bind(wx.EVT_MENU, self.OnRenamePouMenu, id=new_id)

            elif item_infos["type"] == ITEM_CONFIGURATION:
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource"))
                self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(name), id=new_id)

            elif item_infos["type"] in [ITEM_DATATYPE, ITEM_TRANSITION, ITEM_ACTION, ITEM_RESOURCE]:
                menu = wx.Menu(title='')

            if menu is not None:
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete"))
                self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id)

        if menu is not None:
            self.FindFocus().PopupMenu(menu)
            menu.Destroy()

        self.ResetSelectedItem()

        event.Skip()

    # -------------------------------------------------------------------------------
    #                         Instances Tree Management Functions
    # -------------------------------------------------------------------------------

    def GetTreeImage(self, var_class):
        return self.TreeImageDict[var_class]

    def RefreshPouInstanceVariablesPanel(self):
        self.PouInstanceVariablesPanel.RefreshView()

    def OpenDebugViewer(self, instance_category, instance_path, instance_type):
        openedidx = self.IsOpened(instance_path)
        new_window = None
        if openedidx is not None:
            old_selected = self.TabsOpened.GetSelection()
            if old_selected != openedidx:
                if old_selected >= 0:
                    self.TabsOpened.GetPage(old_selected).ResetBuffer()
                self.TabsOpened.SetSelection(openedidx)

        elif instance_category in ITEMS_VARIABLE:
            if self.Controler.IsNumType(instance_type, True):
                self.AddDebugVariable(instance_path, True)

        else:
            bodytype = self.Controler.GetEditedElementBodyType(instance_type, True)
            if bodytype == "FBD":
                new_window = Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            elif bodytype == "LD":
                new_window = LD_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            elif bodytype == "SFC":
                new_window = SFC_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            else:
                new_window = TextViewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.SetTextSyntax(bodytype)
                if bodytype == "IL":
                    new_window.SetKeywords(IL_KEYWORDS)
                else:
                    new_window.SetKeywords(ST_KEYWORDS)

            if new_window is not None:
                if instance_category in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]:
                    pou_type = self.Controler.GetEditedElementType(instance_type, True)[1].upper()
                    icon = GetBitmap(pou_type, bodytype)
                elif instance_category == ITEM_TRANSITION:
                    icon = GetBitmap("TRANSITION", bodytype)
                elif instance_category == ITEM_ACTION:
                    icon = GetBitmap("ACTION", bodytype)

        if new_window is not None:
            new_window.SetIcon(icon)
            self.AddPage(new_window, "")
            new_window.RefreshView()
            new_window.SetFocus()
            self.RefreshPageTitles()
        return new_window

    def ResetGraphicViewers(self):
        if self.EnableDebug:
            self.DebugVariablePanel.ResetGraphicsValues()

    def CloseObsoleteDebugTabs(self):
        if self.EnableDebug:
            idxs = range(self.TabsOpened.GetPageCount())
            idxs.reverse()
            for idx in idxs:
                editor = self.TabsOpened.GetPage(idx)
                if isinstance(editor, Viewer) and editor.IsDebugging():
                    instance_infos = self.Controler.GetInstanceInfos(editor.GetInstancePath(), self.EnableDebug)
                    if instance_infos is None:
                        self.TabsOpened.DeletePage(idx)
                    else:
                        editor.SubscribeAllDataConsumers()
                elif editor.IsDebugging() and hasattr(editor, 'SubscribeAllDataConsumers'):
                    editor.SubscribeAllDataConsumers()
            self.DebugVariablePanel.SubscribeAllDataConsumers()

    def AddDebugVariable(self, iec_path, force=False, graph=False):
        if self.EnableDebug:
            self.DebugVariablePanel.InsertValue(iec_path, force=force, graph=graph)
            self.EnsureTabVisible(self.DebugVariablePanel)

    # -------------------------------------------------------------------------------
    #                         Library Panel Management Function
    # -------------------------------------------------------------------------------

    def RefreshLibraryPanel(self):
        self.LibraryPanel.RefreshTree()

    # -------------------------------------------------------------------------------
    #                          ToolBars Management Functions
    # -------------------------------------------------------------------------------

    def AddToMenuToolBar(self, items):
        MenuToolBar = self.Panes["MenuToolBar"]
        if MenuToolBar.GetToolsCount() > 0:
            MenuToolBar.AddSeparator()
        for toolbar_item in items:
            if toolbar_item is None:
                MenuToolBar.AddSeparator()
            else:
                id, bitmap, help, callback = toolbar_item
                MenuToolBar.AddSimpleTool(id=id, shortHelpString=help, bitmap=GetBitmap(bitmap))
                if callback is not None:
                    self.Bind(wx.EVT_TOOL, callback, id=id)
        MenuToolBar.Realize()
        self.AUIManager.GetPane("MenuToolBar").BestSize(MenuToolBar.GetBestSize())

    def ResetEditorToolBar(self):
        EditorToolBar = self.Panes["EditorToolBar"]

        for item in self.CurrentEditorToolBar:
            self.Unbind(wx.EVT_MENU, id=item)

            if EditorToolBar:
                EditorToolBar.DeleteTool(item)

        if EditorToolBar:
            EditorToolBar.Realize()
            self.AUIManager.GetPane("EditorToolBar").BestSize(EditorToolBar.GetBestSize())
            self.AUIManager.GetPane("EditorToolBar").Hide()
            self.AUIManager.Update()

    def RefreshEditorToolBar(self):
        selected = self.TabsOpened.GetSelection()
        menu = None
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            if isinstance(window, (Viewer, TextViewer)):
                if not window.IsDebugging():
                    menu = self.Controler.GetEditedElementBodyType(window.GetTagName())
                else:
                    menu = "debug"
        if menu is not None and menu != self.CurrentMenu:
            self.ResetEditorToolBar()
            self.CurrentMenu = menu
            self.CurrentEditorToolBar = []
            EditorToolBar = self.Panes["EditorToolBar"]
            if EditorToolBar:
                for radio, modes, id, method, picture, help in self.EditorToolBarItems[menu]:
                    if modes & self.DrawingMode:
                        if radio or self.DrawingMode == FREEDRAWING_MODE:
                            EditorToolBar.AddRadioTool(id, GetBitmap(picture), wx.NullBitmap, help)
                        else:
                            EditorToolBar.AddSimpleTool(id, GetBitmap(picture), help)
                        self.Bind(wx.EVT_MENU, getattr(self, method), id=id)
                        self.CurrentEditorToolBar.append(id)
                EditorToolBar.Realize()
                self.AUIManager.GetPane("EditorToolBar").Show()
                self.AUIManager.Update()
                self.AUIManager.GetPane("EditorToolBar").BestSize(EditorToolBar.GetBestSize())
                self.AUIManager.Update()
        elif menu is None:
            self.ResetEditorToolBar()
            self.CurrentMenu = menu
        self.ResetCurrentMode()

    # -------------------------------------------------------------------------------
    #                           EditorToolBar Items Functions
    # -------------------------------------------------------------------------------

    def ResetCurrentMode(self):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.SetMode(MODE_SELECTION)
        EditorToolBar = self.Panes["EditorToolBar"]
        if EditorToolBar:
            EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, False)
            EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, True)

    def ResetToolToggle(self, id):
        tool = self.Panes["EditorToolBar"].FindById(id)
        tool.SetToggle(False)

    def OnSelectionTool(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_SELECTION)

    def OnMotionTool(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_MOTION)

    def OnCommentTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOMMENT)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_COMMENT)

    def OnVariableTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARVARIABLE)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_VARIABLE)

    def OnBlockTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARBLOCK)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_BLOCK)

    def OnConnectionTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONNECTION)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_CONNECTION)

    def OnPowerRailTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_POWERRAIL)

    def OnRungTool(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).AddLadderRung()
        event.Skip()

    def OnCoilTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOIL)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_COIL)
        event.Skip()

    def OnContactTool(self, event):
        if self.DrawingMode == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONTACT)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.DrawingMode == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_CONTACT)
            else:
                self.TabsOpened.GetPage(selected).AddLadderContact()

    def OnBranchTool(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).AddLadderBranch()

    def OnInitialStepTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_INITIALSTEP)

    def OnStepTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARSTEP)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_STEP)
            else:
                self.TabsOpened.GetPage(selected).AddStep()

    def OnActionBlockTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_ACTION)
            else:
                self.TabsOpened.GetPage(selected).AddStepAction()

    def OnTransitionTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARTRANSITION)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_TRANSITION)

    def OnDivergenceTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_DIVERGENCE)
            else:
                self.TabsOpened.GetPage(selected).AddDivergence()

    def OnJumpTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARJUMP)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_JUMP)
            else:
                self.TabsOpened.GetPage(selected).AddJump()

    # -------------------------------------------------------------------------------
    #                         Add Project Elements Functions
    # -------------------------------------------------------------------------------

    def OnAddNewProject(self, event):
        # Asks user to create main program after creating new project
        AddProgramDialog = self.GenerateAddPouFunction('program', True)
        # Checks that user created main program
        if AddProgramDialog(event):
            self.Controler.SetProjectDefaultConfiguration()

    def OnAddDataTypeMenu(self, event):
        tagname = self.Controler.ProjectAddDataType()
        if tagname is not None:
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)
            self.EditProjectElement(ITEM_DATATYPE, tagname)

    def GenerateAddPouFunction(self, pou_type, type_readonly=False):
        def OnAddPouMenu(event):
            dialog = PouDialog(self, pou_type, type_readonly)
            dialog.SetPouNames(self.Controler.GetProjectPouNames())
            dialog.SetPouElementNames(self.Controler.GetProjectPouVariableNames())
            dialog.SetValues({"pouName": self.Controler.GenerateNewName(None, None, "%s%%d" % pou_type)})
            pou_created = False
            if dialog.ShowModal() == wx.ID_OK:
                values = dialog.GetValues()
                tagname = self.Controler.ProjectAddPou(values["pouName"], values["pouType"], values["language"])
                if tagname is not None:
                    self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE)
                    self.EditProjectElement(ITEM_POU, tagname)
                    dialog.Destroy()
                    pou_created = True
            dialog.Destroy()
            return pou_created
        return OnAddPouMenu

    def GenerateAddTransitionFunction(self, pou_name):
        def OnAddTransitionMenu(event):
            dialog = PouTransitionDialog(self)
            dialog.SetPouNames(self.Controler.GetProjectPouNames())
            dialog.SetPouElementNames(self.Controler.GetProjectPouVariableNames(pou_name))
            dialog.SetValues({"transitionName": self.Controler.GenerateNewName(None, None, "transition%d")})
            if dialog.ShowModal() == wx.ID_OK:
                values = dialog.GetValues()
                tagname = self.Controler.ProjectAddPouTransition(pou_name, values["transitionName"], values["language"])
                if tagname is not None:
                    self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)
                    self.EditProjectElement(ITEM_TRANSITION, tagname)
            dialog.Destroy()
        return OnAddTransitionMenu

    def GenerateAddActionFunction(self, pou_name):
        def OnAddActionMenu(event):
            dialog = PouActionDialog(self)
            dialog.SetPouNames(self.Controler.GetProjectPouNames())
            dialog.SetPouElementNames(self.Controler.GetProjectPouVariableNames(pou_name))
            dialog.SetValues({"actionName": self.Controler.GenerateNewName(None, None, "action%d")})
            if dialog.ShowModal() == wx.ID_OK:
                values = dialog.GetValues()
                tagname = self.Controler.ProjectAddPouAction(pou_name, values["actionName"], values["language"])
                if tagname is not None:
                    self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)
                    self.EditProjectElement(ITEM_ACTION, tagname)
            dialog.Destroy()
        return OnAddActionMenu

    def OnAddConfigurationMenu(self, event):
        tagname = self.Controler.ProjectAddConfiguration()
        if tagname is not None:
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)
            self.EditProjectElement(ITEM_CONFIGURATION, tagname)

    def AddResourceMenu(self, event):
        config_names = self.Controler.GetProjectConfigNames()
        if len(config_names) > 0:
            tagname = self.Controler.ProjectAddConfigurationResource(config_names[0])
            if tagname is not None:
                self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)
                self.EditProjectElement(ITEM_RESOURCE, tagname)

    def GenerateAddResourceFunction(self, config_name):
        def OnAddResourceMenu(event):
            tagname = self.Controler.ProjectAddConfigurationResource(config_name)
            if tagname is not None:
                self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)
                self.EditProjectElement(ITEM_RESOURCE, tagname)
        return OnAddResourceMenu

    def GenerateChangePouTypeFunction(self, name, new_type):
        def OnChangePouTypeMenu(event):
            selected = self.ProjectTree.GetSelection()
            if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU:
                self.Controler.ProjectChangePouType(name, new_type)
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE)
        return OnChangePouTypeMenu

    def OnCopyPou(self, event):
        selected = self.ProjectTree.GetSelection()
        pou_name = self.ProjectTree.GetItemText(selected)

        pou_xml = self.Controler.GetPouXml(pou_name)
        if pou_xml is not None:
            self.SetCopyBuffer(pou_xml)
            self._Refresh(EDITMENU)

    def OnPastePou(self, event):
        selected = self.ProjectTree.GetSelection()

        if self.ProjectTree.GetPyData(selected)["type"] != ITEM_PROJECT:
            pou_type = self.ProjectTree.GetItemText(selected)
            pou_type = self.UNEDITABLE_NAMES_DICT[pou_type]  # one of 'Functions', 'Function Blocks' or 'Programs'
            pou_type = {'Functions': 'function', 'Function Blocks': 'functionBlock', 'Programs': 'program'}[pou_type]
        else:
            pou_type = None

        pou_xml = self.GetCopyBuffer()

        result = self.Controler.PastePou(pou_type, pou_xml)

        if not isinstance(result, tuple):
            self.ShowErrorMessage(result)
        else:
            self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE)
            self.EditProjectElement(ITEM_POU, result[0])

    # -------------------------------------------------------------------------------
    #                        Remove Project Elements Functions
    # -------------------------------------------------------------------------------

    def CheckElementIsUsedBeforeDeletion(self, check_function, title, name):
        if not check_function(name):
            return True

        dialog = wx.MessageDialog(
            self,
            _("\"%s\" is used by one or more POUs. Do you wish to continue?") % name,
            title, wx.YES_NO | wx.ICON_QUESTION)
        answer = dialog.ShowModal()
        dialog.Destroy()
        return answer == wx.ID_YES

    def CheckDataTypeIsUsedBeforeDeletion(self, name):
        return self.CheckElementIsUsedBeforeDeletion(
            self.Controler.DataTypeIsUsed,
            _("Remove Datatype"), name)

    def CheckPouIsUsedBeforeDeletion(self, name):
        return self.CheckElementIsUsedBeforeDeletion(
            self.Controler.PouIsUsed,
            _("Remove Pou"), name)

    def OnRemoveDataTypeMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_DATATYPE:
            name = self.ProjectTree.GetItemText(selected)
            if self.CheckDataTypeIsUsedBeforeDeletion(name):
                self.Controler.ProjectRemoveDataType(name)
                tagname = ComputeDataTypeName(name)
                idx = self.IsOpened(tagname)
                if idx is not None:
                    self.TabsOpened.DeletePage(idx)
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE)

    def OnRenamePouMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU:
            wx.CallAfter(self.ProjectTree.EditLabel, selected)

    def OnRemovePouMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU:
            name = self.ProjectTree.GetItemText(selected)
            if self.CheckPouIsUsedBeforeDeletion(name):
                self.Controler.ProjectRemovePou(name)
                tagname = ComputePouName(name)
                idx = self.IsOpened(tagname)
                if idx is not None:
                    self.TabsOpened.DeletePage(idx)
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)

    def OnRemoveTransitionMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        item_infos = self.ProjectTree.GetPyData(selected)
        if item_infos["type"] == ITEM_TRANSITION:
            transition = self.ProjectTree.GetItemText(selected)
            pou_name = item_infos["tagname"].split("::")[1]
            self.Controler.ProjectRemovePouTransition(pou_name, transition)
            tagname = ComputePouTransitionName(pou_name, transition)
            idx = self.IsOpened(tagname)
            if idx is not None:
                self.TabsOpened.DeletePage(idx)
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)

    def OnRemoveActionMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        item_infos = self.ProjectTree.GetPyData(selected)
        if item_infos["type"] == ITEM_ACTION:
            action = self.ProjectTree.GetItemText(selected)
            pou_name = item_infos["tagname"].split("::")[1]
            self.Controler.ProjectRemovePouAction(pou_name, action)
            tagname = ComputePouActionName(pou_name, action)
            idx = self.IsOpened(tagname)
            if idx is not None:
                self.TabsOpened.DeletePage(idx)
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)

    def OnRemoveConfigurationMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFIGURATION:
            name = self.ProjectTree.GetItemText(selected)
            self.Controler.ProjectRemoveConfiguration(name)
            tagname = ComputeConfigurationName(name)
            idx = self.IsOpened(tagname)
            if idx is not None:
                self.TabsOpened.DeletePage(idx)
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)

    def OnRemoveResourceMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        item_infos = self.ProjectTree.GetPyData(selected)
        if item_infos["type"] == ITEM_RESOURCE:
            resource = self.ProjectTree.GetItemText(selected)
            config_name = item_infos["tagname"].split("::")[1]
            self.Controler.ProjectRemoveConfigurationResource(config_name, resource)
            tagname = ComputeConfigurationResourceName(config_name, selected)
            idx = self.IsOpened(tagname)
            if idx is not None:
                self.TabsOpened.DeletePage(idx)
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)

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

    def ShowHighlight(self, infos, start, end, highlight_type):
        self.SelectProjectTreeItem(infos[0])
        if infos[1] == "name":
            self.Highlights[infos[0]] = highlight_type
            self.RefreshProjectTree()
            self.ProjectTree.Unselect()
        else:
            self.EditProjectElement(GetElementType(infos[0]), infos[0])
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                viewer = self.TabsOpened.GetPage(selected)
                viewer.AddHighlight(infos[1:], start, end, highlight_type)

    def ShowError(self, infos, start, end):
        self.ShowHighlight(infos, start, end, ERROR_HIGHLIGHT)

    def ShowSearchResult(self, infos, start, end):
        self.ShowHighlight(infos, start, end, SEARCH_RESULT_HIGHLIGHT)

    def ClearHighlights(self, highlight_type=None):
        if highlight_type is None:
            self.Highlights = {}
        else:
            self.Highlights = dict([(name, highlight) for name, highlight in self.Highlights.iteritems() if highlight != highlight_type])
        self.RefreshProjectTree()
        for i in xrange(self.TabsOpened.GetPageCount()):
            viewer = self.TabsOpened.GetPage(i)
            viewer.ClearHighlights(highlight_type)

    def ClearErrors(self):
        self.ClearHighlights(ERROR_HIGHLIGHT)

    def ClearSearchResults(self):
        self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)

# -------------------------------------------------------------------------------
#                               Viewer Printout
# -------------------------------------------------------------------------------


def UPPER_DIV(x, y):
    return (x // y) + {True: 0, False: 1}[(x % y) == 0]


class GraphicPrintout(wx.Printout):
    def __init__(self, viewer, page_size, margins, preview=False):
        wx.Printout.__init__(self)
        self.Viewer = viewer
        self.PageSize = page_size
        if self.PageSize[0] == 0 or self.PageSize[1] == 0:
            self.PageSize = (1050, 1485)
        self.Preview = preview
        self.Margins = margins
        self.FontSize = 5
        self.TextMargin = 3

        maxx, maxy = viewer.GetMaxSize()
        self.PageGrid = (UPPER_DIV(maxx, self.PageSize[0]),
                         UPPER_DIV(maxy, self.PageSize[1]))

    def GetPageNumber(self):
        return self.PageGrid[0] * self.PageGrid[1]

    def HasPage(self, page):
        return page <= self.GetPageNumber()

    def GetPageInfo(self):
        page_number = self.GetPageNumber()
        return (1, page_number, 1, page_number)

    def OnBeginDocument(self, startPage, endPage):
        dc = self.GetDC()
        if not self.Preview and isinstance(dc, wx.PostScriptDC):
            dc.SetResolution(720)
        super(GraphicPrintout, self).OnBeginDocument(startPage, endPage)

    def OnPrintPage(self, page):
        dc = self.GetDC()
        dc.SetBackground(wx.WHITE_BRUSH)
        dc.Clear()
        dc.SetUserScale(1.0, 1.0)
        dc.SetDeviceOrigin(0, 0)
        dc.printing = not self.Preview

        # Get the size of the DC in pixels
        ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
        pw, ph = self.GetPageSizePixels()
        dw, dh = dc.GetSizeTuple()
        Xscale = (dw * ppiPrinterX) / (pw * 25.4)
        Yscale = (dh * ppiPrinterY) / (ph * 25.4)

        fontsize = self.FontSize * Yscale

        margin_left = self.Margins[0].x * Xscale
        margin_top = self.Margins[0].y * Yscale
        area_width = dw - self.Margins[1].x * Xscale - margin_left
        area_height = dh - self.Margins[1].y * Yscale - margin_top

        dc.SetPen(MiterPen(wx.BLACK))
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.DrawRectangle(margin_left, margin_top, area_width, area_height)

        dc.SetFont(wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
        dc.SetTextForeground(wx.BLACK)
        block_name = " - ".join(self.Viewer.GetTagName().split("::")[1:])
        _text_width, text_height = dc.GetTextExtent(block_name)
        dc.DrawText(block_name, margin_left, margin_top - text_height - self.TextMargin)
        dc.DrawText(_("Page: %d") % page, margin_left, margin_top + area_height + self.TextMargin)

        # Calculate the position on the DC for centering the graphic
        posX = area_width * ((page - 1) % self.PageGrid[0])
        posY = area_height * ((page - 1) // self.PageGrid[0])

        scaleX = area_width / self.PageSize[0]
        scaleY = area_height / self.PageSize[1]
        scale = min(scaleX, scaleY)

        # Set the scale and origin
        dc.SetDeviceOrigin(-posX + margin_left, -posY + margin_top)
        dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale)
        dc.SetUserScale(scale, scale)

        self.Viewer.DoDrawing(dc, True)

        return True