PLCOpenEditor.py
author laurent
Sun, 08 Jan 2012 19:16:58 +0100
changeset 616 8a60ffcfd70b
parent 613 c487c54c1cfe
child 618 6e48943e821e
permissions -rw-r--r--
Adding support for drag'n dropping variable from global defined in configurations and resources to POU variable panel or body editor for declaring external variables
Adding support for drag'n dropping located variables from topology panel to configurations and resources variable panel for declaring global located variables
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
#based on the plcopen standard. 
#
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
#See COPYING file for copyrights details.
#
#This library 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.1 of the License, or (at your option) any later version.
#
#This library 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 library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from datetime import datetime
import wx, wx.grid

if wx.VERSION >= (2, 8, 0):
    import wx.aui
    USE_AUI = True
else:
    USE_AUI = False

import os, re, platform, sys, time, traceback, getopt
import cPickle

CWD = os.path.split(os.path.realpath(__file__))[0]
base_folder = os.path.split(CWD)[0]
sys.path.append(base_folder)
from docutils import *

from types import TupleType

__version__ = "$Revision: 1.130 $"

if __name__ == '__main__':
    # Usage message displayed when help request or when error detected in 
    # command line
    def usage():
        print "\nUsage of PLCOpenEditor.py :"
        print "\n   %s [Filepath]\n"%sys.argv[0]

    # Parse options given to PLCOpenEditor in command line
    try:
        opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)
    
    # Extract if help has been requested
    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
    
    # Extract the optional filename to open
    fileOpen = None
    if len(args) > 1:
        usage()
        sys.exit()
    elif len(args) == 1:
        fileOpen = args[0]
    
    # Create wxApp (Need to create App before internationalization because of
    # Windows) 
    app = wx.PySimpleApp()

# Import module for internationalization
import gettext
import __builtin__

# Get folder containing translation files
localedir = os.path.join(CWD,"locale")
# Get the default language
langid = wx.LANGUAGE_DEFAULT
# Define translation domain (name of translation files)
domain = "PLCOpenEditor"

# Define locale for wx
loc = __builtin__.__dict__.get('loc', None)
if loc is None:
    test_loc = wx.Locale(langid)
    test_loc.AddCatalogLookupPathPrefix(localedir)
    if test_loc.AddCatalog(domain):
        loc = wx.Locale(langid)
    else:
        loc = wx.Locale(wx.LANGUAGE_ENGLISH)
    __builtin__.__dict__['loc'] = loc
# Define location for searching translation files
loc.AddCatalogLookupPathPrefix(localedir)
# Define locale domain
loc.AddCatalog(domain)

if __name__ == '__main__':
    __builtin__.__dict__['_'] = wx.GetTranslation

from SFCViewer import *
from LDViewer import *
from Viewer import *
from TextViewer import *
from GraphicViewer import *
from RessourceEditor import *
from DataTypeEditor import *
from PLCControler import *
from SearchResultPanel import SearchResultPanel
from controls import CustomGrid, CustomTable

# Define PLCOpenEditor controls id
[ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK, 
 ID_PLCOPENEDITORBOTTOMNOTEBOOK, ID_PLCOPENEDITORRIGHTNOTEBOOK, 
 ID_PLCOPENEDITORTYPESTREE, ID_PLCOPENEDITORINSTANCESTREE, 
 ID_PLCOPENEDITORMAINSPLITTER, ID_PLCOPENEDITORSECONDSPLITTER, 
 ID_PLCOPENEDITORTHIRDSPLITTER, ID_PLCOPENEDITORLIBRARYPANEL, 
 ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT, 
 ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED,
 ID_PLCOPENEDITORTOOLBAR, ID_PLCOPENEDITORDEFAULTTOOLBAR, 
 ID_PLCOPENEDITORSFCTOOLBAR, ID_PLCOPENEDITORFBDTOOLBAR, 
 ID_PLCOPENEDITORLDTOOLBAR,
] = [wx.NewId() for _init_ctrls in range(19)]

# Define PLCOpenEditor FileMenu extra items id
[ID_PLCOPENEDITORFILEMENUGENERATE, 
] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)]

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


#-------------------------------------------------------------------------------
#                            ToolBars definitions
#-------------------------------------------------------------------------------

# Define PLCOpenEditor Toolbar items id
[ID_PLCOPENEDITORTOOLBARSELECTION, ID_PLCOPENEDITORTOOLBARCOMMENT,
 ID_PLCOPENEDITORTOOLBARVARIABLE, ID_PLCOPENEDITORTOOLBARBLOCK,
 ID_PLCOPENEDITORTOOLBARCONNECTION, ID_PLCOPENEDITORTOOLBARWIRE,
 ID_PLCOPENEDITORTOOLBARPOWERRAIL, ID_PLCOPENEDITORTOOLBARRUNG,
 ID_PLCOPENEDITORTOOLBARCOIL, ID_PLCOPENEDITORTOOLBARCONTACT,
 ID_PLCOPENEDITORTOOLBARBRANCH, ID_PLCOPENEDITORTOOLBARINITIALSTEP,
 ID_PLCOPENEDITORTOOLBARSTEP, ID_PLCOPENEDITORTOOLBARTRANSITION,
 ID_PLCOPENEDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITORTOOLBARDIVERGENCE,
 ID_PLCOPENEDITORTOOLBARJUMP, ID_PLCOPENEDITORTOOLBARMOTION, 
] = [wx.NewId() for _init_coll_DefaultToolBar_Items in range(18)]

# 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
ToolBarItems = {
    "FBD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITORTOOLBARMOTION, "OnMotionTool",
              "move.png", _("Move the view")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITORTOOLBARCOMMENT, "OnCommentTool",
              "add_comment.png", _("Create a new comment")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITORTOOLBARVARIABLE, "OnVariableTool",
              "add_variable.png", _("Create a new variable")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITORTOOLBARBLOCK, "OnBlockTool",
              "add_block.png", _("Create a new block")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARCONNECTION, "OnConnectionTool", 
              "add_connection.png", _("Create a new connection"))],
    "LD"  : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITORTOOLBARMOTION, "OnMotionTool",
              "move.png", _("Move the view")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARCOMMENT, "OnCommentTool", 
              "add_comment.png", _("Create a new comment")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARPOWERRAIL, "OnPowerRailTool", 
              "add_powerrail.png", _("Create a new power rail")),
             (False, DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARRUNG, "OnRungTool", 
              "add_rung.png", _("Create a new rung")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARCOIL, "OnCoilTool", 
              "add_coil.png", _("Create a new coil")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARCONTACT, "OnContactTool", 
              "add_contact.png", _("Create a new contact")),
             (False, DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARBRANCH, "OnBranchTool", 
              "add_branch.png", _("Create a new branch")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARVARIABLE, "OnVariableTool", 
              "add_variable.png", _("Create a new variable")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARBLOCK, "OnBlockTool", 
              "add_block.png", _("Create a new block")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARCONNECTION, "OnConnectionTool", 
              "add_connection.png", _("Create a new connection"))],
    "SFC" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITORTOOLBARMOTION, "OnMotionTool",
              "move.png", _("Move the view")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARCOMMENT, "OnCommentTool", 
              "add_comment.png", _("Create a new comment")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARINITIALSTEP, "OnInitialStepTool", 
              "add_initial_step.png", _("Create a new initial step")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARSTEP, "OnStepTool", 
              "add_step.png", _("Create a new step")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARTRANSITION, "OnTransitionTool", 
              "add_transition.png", _("Create a new transition")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARACTIONBLOCK, "OnActionBlockTool", 
              "add_action.png", _("Create a new action block")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARDIVERGENCE, "OnDivergenceTool", 
              "add_divergence.png", _("Create a new divergence")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARJUMP, "OnJumpTool", 
              "add_jump.png", _("Create a new jump")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARVARIABLE, "OnVariableTool", 
              "add_variable.png", _("Create a new variable")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARBLOCK, "OnBlockTool", 
              "add_block.png", _("Create a new block")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARCONNECTION, "OnConnectionTool", 
              "add_connection.png", _("Create a new connection")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARPOWERRAIL, "OnPowerRailTool", 
              "add_powerrail.png", _("Create a new power rail")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITORTOOLBARCONTACT, "OnContactTool", 
              "add_contact.png", _("Create a new contact"))],
    "ST"  : [],
    "IL"  : []
}

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

# Compatibility function for wx versions < 2.6
def AppendMenu(parent, help, id, kind, text):
    if wx.VERSION >= (2, 6, 0):
        parent.Append(help=help, id=id, kind=kind, text=text)
    else:
        parent.Append(helpString=help, id=id, kind=kind, item=text)

[TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, 
 INSTANCESTREE, 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.Parent, viewer_function)()
        elif isinstance(control, wx.stc.StyledTextCtrl):
            getattr(control, viewer_function)()
        elif isinstance(control, wx.TextCtrl):
            control.ProcessEvent(event)
    return ShortcutKeyFunction

def GetParentName(tree, item, parent_type):
    parent_item = tree.GetItemParent(item)
    parent_item_type = tree.GetPyData(parent_item)
    while parent_item_type != parent_type:
        parent_item = tree.GetItemParent(parent_item)
        parent_item_type = tree.GetPyData(parent_item)
    return tree.GetItemText(parent_item)

def GetDeleteElementFunction(remove_function, parent_type=None, check_function=None):
    def DeleteElementFunction(self, selected):
        name = self.TypesTree.GetItemText(selected)
        if check_function is None or not check_function(self.Controler, name):
            if parent_type is not None:
                parent_name = GetParentName(self.TypesTree, selected, parent_type)
                remove_function(self.Controler, parent_name, name)
            else:
                remove_function(self.Controler, name)
        else:
            self.ShowErrorMessage(_("\"%s\" is used by one or more POUs. It can't be removed!")%name)
    return DeleteElementFunction


#-------------------------------------------------------------------------------
#                              IDEFrame Base Class
#-------------------------------------------------------------------------------

UNEDITABLE_NAMES_DICT = dict([(_(name), name) for name in UNEDITABLE_NAMES])

class IDEFrame(wx.Frame):
    
    # Compatibility function for wx versions < 2.6
    if wx.VERSION < (2, 6, 0):
        def Bind(self, event, function, id = None):
            if id is not None:
                event(self, id, function)
            else:
                event(self, function)
    
    def _init_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_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'))
        #AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO,
        #      kind=wx.ITEM_CHECK, text=_(u'Enable Undo/Redo'))
        enable_undo_redo = _(u'Enable Undo/Redo') # Keeping text in translations for possible menu reactivation
        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=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT,
              kind=wx.ITEM_NORMAL, text=_(u'Search in Project'))
        parent.AppendSeparator()
        addmenu = wx.Menu(title='')
        parent.AppendMenu(wx.ID_ADD, _("Add Element"), addmenu)
        AppendMenu(addmenu, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE,
              kind=wx.ITEM_NORMAL, text=_(u'Data Type'))
        AppendMenu(addmenu, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTION,
              kind=wx.ITEM_NORMAL, text=_(u'Function'))
        AppendMenu(addmenu, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK,
              kind=wx.ITEM_NORMAL, text=_(u'Function Block'))
        AppendMenu(addmenu, help='', id=ID_PLCOPENEDITOREDITMENUADDPROGRAM,
              kind=wx.ITEM_NORMAL, text=_(u'Program'))
        AppendMenu(addmenu, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
              kind=wx.ITEM_NORMAL, text=_(u'Configuration'))
        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.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.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)

    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)
        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_coll_MainLibrarySizer_Items(self, parent):
        parent.AddWindow(self.LibraryTree, 0, border=0, flag=wx.GROW)
        parent.AddSizer(self.LibraryComment, 0, border=0, flag=wx.GROW)

    def _init_coll_MainLibrarySizer_Growables(self, parent):
        parent.AddGrowableCol(0)
        parent.AddGrowableRow(0)

    def _init_sizers(self):
        self.MainLibrarySizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
        
        self._init_coll_MainLibrarySizer_Growables(self.MainLibrarySizer)
        self._init_coll_MainLibrarySizer_Items(self.MainLibrarySizer)
        
        self.LibraryPanel.SetSizer(self.MainLibrarySizer)
        
    def _init_ctrls(self, prnt):
        wx.Frame.__init__(self, id=ID_PLCOPENEDITOR, name='IDEFrame',
              parent=prnt, pos=wx.DefaultPosition, size=wx.Size(1000, 600),
              style=wx.DEFAULT_FRAME_STYLE)
        self._init_utils()
        self.SetClientSize(wx.Size(1000, 600))
        self.SetMenuBar(self.MenuBar)
        
        self.TabsImageList = wx.ImageList(31, 16)
        self.TabsImageListIndexes = {}
        
        #-----------------------------------------------------------------------
        #                          Creating main structure
        #-----------------------------------------------------------------------
        
        if USE_AUI:
            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.AUIManager.AddPane(self.LeftNoteBook, 
                  wx.aui.AuiPaneInfo().Caption(_("Project")).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.AUIManager.AddPane(self.BottomNoteBook, 
                  wx.aui.AuiPaneInfo().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.AUIManager.AddPane(self.RightNoteBook, 
                  wx.aui.AuiPaneInfo().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.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())
        
        else:
            self.MainSplitter = wx.SplitterWindow(id=ID_PLCOPENEDITORMAINSPLITTER,
                  name='MainSplitter', parent=self, point=wx.Point(0, 0),
                  size=wx.Size(0, 0), style=wx.SP_3D)
            self.MainSplitter.SetNeedUpdating(True)
            self.MainSplitter.SetMinimumPaneSize(1)
            
            self.LeftNoteBook = wx.Notebook(id=ID_PLCOPENEDITORLEFTNOTEBOOK,
                  name='LeftNoteBook', parent=self.MainSplitter, pos=wx.Point(0,
                  0), size=wx.Size(0, 0), style=0)
        
            self.SecondSplitter = wx.SplitterWindow(id=ID_PLCOPENEDITORSECONDSPLITTER,
                  name='SecondSplitter', parent=self.MainSplitter, point=wx.Point(0, 0),
                  size=wx.Size(0, 0), style=wx.SP_3D)
            self.SecondSplitter.SetMinimumPaneSize(1)
                
            self.MainSplitter.SplitVertically(self.LeftNoteBook, self.SecondSplitter, 200)
            
            self.ThirdSplitter = wx.SplitterWindow(id=ID_PLCOPENEDITORTHIRDSPLITTER,
                  name='ThirdSplitter', parent=self.SecondSplitter, point=wx.Point(0, 0),
                  size=wx.Size(0, 0), style=wx.SP_3D)
            self.ThirdSplitter.SetMinimumPaneSize(1)
            
            self.BottomNoteBook = wx.Notebook(id=ID_PLCOPENEDITORBOTTOMNOTEBOOK,
                  name='BottomNoteBook', parent=self.SecondSplitter, pos=wx.Point(0,
                  0), size=wx.Size(0, 0), style=0)
            
            self.SecondSplitter.SplitHorizontally(self.ThirdSplitter, self.BottomNoteBook, -200)
            
            self.TabsOpened = wx.Notebook(id=ID_PLCOPENEDITORTABSOPENED,
                  name='TabsOpened', parent=self.ThirdSplitter, pos=wx.Point(0,
                  0), size=wx.Size(0, 0), style=0)
            self.TabsOpened.SetImageList(self.TabsImageList)
            if wx.VERSION >= (2, 6, 0):
                self.TabsOpened.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING,
                    self.OnPouSelectedChanged, id=ID_PLCOPENEDITORTABSOPENED)
            else:
                wx.EVT_NOTEBOOK_PAGE_CHANGING(self.TabsOpened, ID_PLCOPENEDITORTABSOPENED,
                    self.OnPouSelectedChanged)
            
            self.RightNoteBook = wx.Notebook(id=ID_PLCOPENEDITORRIGHTNOTEBOOK,
                  name='RightNoteBook', parent=self.ThirdSplitter, pos=wx.Point(0,
                  0), size=wx.Size(0, 0), style=0)
            
            self.ThirdSplitter.SplitVertically(self.TabsOpened, self.RightNoteBook, -250)
        
        #-----------------------------------------------------------------------
        #                       Creating PLCopen Project tree
        #-----------------------------------------------------------------------
        
        self.TypesTree = wx.TreeCtrl(id=ID_PLCOPENEDITORTYPESTREE,
                  name='TypesTree', parent=self.LeftNoteBook, 
                  pos=wx.Point(0, 0), size=wx.Size(0, 0),
                  style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_EDIT_LABELS)
        if wx.Platform == '__WXMSW__':
            self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnTypesTreeRightUp,
                  id=ID_PLCOPENEDITORTYPESTREE)
            self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTypesTreeItemSelected,
                  id=ID_PLCOPENEDITORTYPESTREE)
        else:
            if wx.VERSION >= (2, 6, 0):
                self.TypesTree.Bind(wx.EVT_RIGHT_UP, self.OnTypesTreeRightUp)
                self.TypesTree.Bind(wx.EVT_LEFT_UP, self.OnTypesTreeLeftUp)
            else:
                wx.EVT_RIGHT_UP(self.TypesTree, self.OnTypesTreeRightUp)
                wx.EVT_LEFT_UP(self.TypesTree, self.OnTypesTreeLeftUp)
            self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnTypesTreeItemChanging,
                  id=ID_PLCOPENEDITORTYPESTREE)
        self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTypesTreeBeginDrag,
              id=ID_PLCOPENEDITORTYPESTREE)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTypesTreeItemBeginEdit,
              id=ID_PLCOPENEDITORTYPESTREE)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTypesTreeItemEndEdit,
              id=ID_PLCOPENEDITORTYPESTREE)
        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTypesTreeItemActivated,
              id=ID_PLCOPENEDITORTYPESTREE)
        self.LeftNoteBook.AddPage(self.TypesTree, _("Types"))

        #-----------------------------------------------------------------------
        #                       Creating PLCopen Project tree
        #-----------------------------------------------------------------------
        
        self.InstancesTree = wx.TreeCtrl(id=ID_PLCOPENEDITORINSTANCESTREE,
                  name='InstancesTree', parent=self.LeftNoteBook, 
                  pos=wx.Point(0, 0), size=wx.Size(0, 0),
                  style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER)
        if self.EnableDebug:
            if wx.VERSION >= (2, 6, 0):
                self.InstancesTree.Bind(wx.EVT_RIGHT_UP, self.OnInstancesTreeRightUp)
            else:
                wx.EVT_RIGHT_UP(self.InstancesTree, self.OnInstancesTreeRightUp)
            self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnInstancesTreeBeginDrag,
                  id=ID_PLCOPENEDITORINSTANCESTREE)
            self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnInstancesTreeItemActivated,
                  id=ID_PLCOPENEDITORINSTANCESTREE)
        self.LeftNoteBook.AddPage(self.InstancesTree, _("Instances"))
        
        #-----------------------------------------------------------------------
        #                            Creating Tool Bar
        #-----------------------------------------------------------------------

        if USE_AUI:
            ToolBar = wx.ToolBar(self, ID_PLCOPENEDITORTOOLBAR, wx.DefaultPosition, wx.DefaultSize,
                    wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
            ToolBar.SetToolBitmapSize(wx.Size(25, 25))
            ToolBar.AddRadioTool(ID_PLCOPENEDITORTOOLBARSELECTION, 
                  wx.Bitmap(os.path.join(CWD, 'Images', 'select.png')), wx.NullBitmap, _("Select an object"))
            ToolBar.Realize()
            self.Panes["ToolBar"] = ToolBar
            self.AUIManager.AddPane(ToolBar, wx.aui.AuiPaneInfo().
                      Name("ToolBar").Caption(_("Toolbar")).
                      ToolbarPane().Top().
                      LeftDockable(False).RightDockable(False))
        else:
            self.ToolBar = self.CreateToolBar(wx.TB_HORIZONTAL|wx.TB_FLAT|wx.NO_BORDER, 
                  ID_PLCOPENEDITORTOOLBAR, 'ToolBar')
            self.ToolBar.SetToolBitmapSize(wx.Size(25, 25))
            self.ToolBar.AddRadioTool(ID_PLCOPENEDITORTOOLBARSELECTION, 
                  wx.Bitmap(os.path.join(CWD, 'Images', 'select.png')), wx.NullBitmap, _("Select an object"))
            self.ToolBar.Realize()
            
        self.Bind(wx.EVT_TOOL, self.OnSelectionTool, 
              id=ID_PLCOPENEDITORTOOLBARSELECTION)
        
        self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self)
        self.BottomNoteBook.AddPage(self.SearchResultPanel, _("Search"))

        self.LibraryPanel = wx.Panel(id=ID_PLCOPENEDITORLIBRARYPANEL,
              name='LibraryPanel', parent=self.RightNoteBook, pos=wx.Point(0,
              0), size=wx.Size(0, 0), style=0)
        self.RightNoteBook.AddPage(self.LibraryPanel, _("Library"))
        
        self.LibraryTree = wx.TreeCtrl(id=ID_PLCOPENEDITORLIBRARYTREE,
                  name='LibraryTree', parent=self.LibraryPanel, 
                  pos=wx.Point(0, 0), size=wx.Size(0, 0),
                  style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT)
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnLibraryTreeItemSelected,
              id=ID_PLCOPENEDITORLIBRARYTREE)
        self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnLibraryTreeBeginDrag,
              id=ID_PLCOPENEDITORLIBRARYTREE)
        
        self.LibraryComment = wx.TextCtrl(id=ID_PLCOPENEDITORLIBRARYCOMMENT,
              name='LibraryComment', parent=self.LibraryPanel, 
              pos=wx.Point(0, 0), size=wx.Size(0, 160), 
              style=wx.TE_READONLY|wx.TE_MULTILINE)
        
        self._init_sizers()
        
        if self.EnableDebug:
            self.DebugVariablePanel = DebugVariablePanel(self.RightNoteBook, self.Controler)
            self.RightNoteBook.AddPage(self.DebugVariablePanel, _("Debugger"))
        
        if USE_AUI:
            self.AUIManager.Update()
    
    ## Constructor of the PLCOpenEditor class.
    #  @param parent The parent window.
    #  @param controler The controler been used by PLCOpenEditor (default: None).
    #  @param fileOpen The filepath to open if no controler defined (default: None).
    #  @param debug The filepath to open if no controler defined (default: False).
    def __init__(self, parent, enable_debug = False):
        self.Controler = None
        self.Config = wx.ConfigBase.Get()
        self.EnableDebug = enable_debug
        
        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(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%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(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname)))
        
        # Assign icon list to TreeCtrls
        self.TypesTree.SetImageList(self.TreeImageList)
        self.InstancesTree.SetImageList(self.TreeImageList)
        
        self.CurrentToolBar = []
        self.CurrentLanguage = ""
        self.SelectedItem = None
        self.Highlights = {}
        self.DrawingMode = FREEDRAWING_MODE
        #self.DrawingMode = DRIVENDRAWING_MODE
        if USE_AUI:
            self.AuiTabCtrl = []
        
        # 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()
    
    def Show(self):
        wx.Frame.Show(self)
        wx.CallAfter(self.RestoreFrameSize)
    
    def RestoreFrameSize(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 SaveFrameSize(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,
            TOOLBAR : self.RefreshToolBar,
            FILEMENU : self.RefreshFileMenu,
            EDITMENU : self.RefreshEditMenu,
            DISPLAYMENU : self.RefreshDisplayMenu,
            TYPESTREE : self.RefreshTypesTree,
            INSTANCESTREE : self.RefreshInstancesTree, 
            LIBRARYTREE : self.RefreshLibraryTree,
            SCALING : self.RefreshScaling,
            PAGETITLES: self.RefreshPageTitles}

    ## Call PLCOpenEditor refresh functions.
    #  @param elements List of elements to refresh.
    def _Refresh(self, *elements):
        for element in elements:
            self.RefreshFunctions[element]()

    ## Callback function when AUINotebook Page closed with CloseButton
    #  @param event AUINotebook Event.
    def OnPageClose(self, event):
        wx.CallAfter(self.RefreshTabCtrlEvent)
        event.Skip()

    def GetCopyBuffer(self):
        data = None
        if wx.TheClipboard.IsOpened() or wx.TheClipboard.Open():
            dataobj = wx.TextDataObject()
            if wx.TheClipboard.GetData(dataobj):
                data = dataobj.GetText()
        if wx.TheClipboard.IsOpened():
            wx.TheClipboard.Close()
        return data
        
    def SetCopyBuffer(self, text):
        if wx.TheClipboard.IsOpened() or wx.TheClipboard.Open():
            data = wx.TextDataObject()
            data.SetText(text)
            wx.TheClipboard.SetData(data)
            wx.TheClipboard.Flush()
        if wx.TheClipboard.IsOpened():
            wx.TheClipboard.Close()
        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 ShowProperties(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, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, 
                              TYPESTREE, INSTANCESTREE, SCALING)
        dialog.Destroy()

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

    ## Function that generate bitmap for
    # for wx.aui.AUINotebook.
    #  @param window Panel to display in tab.
    #  @param text title for the tab ctrl.
    def GenerateBitmap(self, icon1_name, icon2_name = None):
        # Find index of bitmap if already created
        index = self.TabsImageListIndexes.get((icon1_name, icon2_name), None)
        # Return index or bitmap if found
        if index is not None:
            if USE_AUI:
                return self.TabsImageList.GetBitmap(index)
            else:
                return index
        if icon2_name is None:
            # Bitmap with only one icon
            bitmap = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon1_name))
        else:
            # Bitmap with two icon
            icon1 = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon1_name))
            icon2 = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon2_name))
            
            # Calculate bitmap size
            width = icon1.GetWidth() + icon2.GetWidth() - 1
            height = max(icon1.GetHeight(), icon2.GetHeight())
            # Create bitmap with both icons
            bitmap = wx.EmptyBitmap(width, height)
            dc = wx.MemoryDC()
            dc.SelectObject(bitmap)
            dc.Clear()
            dc.DrawBitmap(icon1, 0, 0)
            dc.DrawBitmap(icon2, icon1.GetWidth() - 1, 0)
            dc.Destroy()
            
            # Store bitmap in ImageList
            index = self.TabsImageList.Add(bitmap)
            # Save bitmap index in ImageList in dictionary
            self.TabsImageListIndexes[(icon1_name, icon2_name)] = index
        if USE_AUI:
            return bitmap
        else:
            return index
    
    ## 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.
    def AddPage(self, window, text):
        self.TabsOpened.AddPage(window, text)
        self.RefreshTabCtrlEvent()
    
    ## 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.
    def DeletePage(self, window):
        for idx in xrange(self.TabsOpened.GetPageCount()):
            if self.TabsOpened.GetPage(idx) == window:
                self.TabsOpened.DeletePage(idx)
                self.RefreshTabCtrlEvent()
                return 
        
    ## Function that fix difference in deleting all tabs between 
    # wx.Notebook and wx.aui.AUINotebook.
    def DeleteAllPages(self):
        if USE_AUI:
            for idx in xrange(self.TabsOpened.GetPageCount()):
                self.TabsOpened.DeletePage(0)
        else:
            self.TabsOpened.DeleteAllPages()
        self.RefreshTabCtrlEvent()

    ## 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.
    #  @return True if operation succeeded
    def SetPageBitmap(self, idx, bitmap):
        if USE_AUI:
            return self.TabsOpened.SetPageBitmap(idx, bitmap)
        else:
            return self.TabsOpened.SetPageImage(idx, bitmap)

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

    ## Function displaying an Error dialog in PLCOpenEditor.
    #  @param message The message to display.
    def ShowErrorMessage(self, message):
        dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR)
        dialog.ShowModal()
        dialog.Destroy()

    ## Function displaying an Error dialog in PLCOpenEditor.
    #  @return False if closing cancelled.
    def CheckSaveBeforeClosing(self, title=_("Close Project")):
        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
        return True

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

    def RefreshFileMenu(self):
        pass

    def ResetView(self):
        self.DeleteAllPages()
        self.TypesTree.DeleteAllItems()
        self.InstancesTree.DeleteAllItems()
        self.LibraryTree.DeleteAllItems()
        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, TOOLBAR, 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.ShowProperties()

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

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

    def RefreshEditMenu(self):
        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)
            self.EditMenu.Enable(wx.ID_REDO, redo)
            #self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True)
            #self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, 
            #                self.Controler.IsProjectBufferEnabled())
            self.EditMenu.Enable(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)
                self.EditMenu.Enable(wx.ID_COPY, True)
                if self.GetCopyBuffer() is not None:
                    self.EditMenu.Enable(wx.ID_PASTE, True)
                else:
                    self.EditMenu.Enable(wx.ID_PASTE, False)
                self.EditMenu.Enable(wx.ID_SELECTALL, True)
            else:
                self.EditMenu.Enable(wx.ID_CUT, False)
                self.EditMenu.Enable(wx.ID_COPY, False)
                self.EditMenu.Enable(wx.ID_PASTE, False)
                self.EditMenu.Enable(wx.ID_SELECTALL, False)
        else:
            self.EditMenu.Enable(wx.ID_UNDO, False)
            self.EditMenu.Enable(wx.ID_REDO, False)
            #self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, False)
            self.EditMenu.Enable(wx.ID_CUT, False)
            self.EditMenu.Enable(wx.ID_COPY, False)
            self.EditMenu.Enable(wx.ID_PASTE, False)
            self.EditMenu.Enable(wx.ID_SELECTALL, False)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False)
            self.EditMenu.Enable(wx.ID_ADD, False)
            self.EditMenu.Enable(wx.ID_DELETE, False)
    
    def CloseTabsWithoutModel(self):
        idxs = range(self.TabsOpened.GetPageCount())
        idxs.reverse()
        for idx in idxs:
            window = self.TabsOpened.GetPage(idx)
            if window.HasNoModel():
                self.TabsOpened.DeletePage(idx)

    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, TYPESTREE, INSTANCESTREE, 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, TYPESTREE, INSTANCESTREE, 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)
    
    DeleteFunctions = {
        ITEM_DATATYPE: GetDeleteElementFunction(PLCControler.ProjectRemoveDataType, check_function=PLCControler.DataTypeIsUsed),
        ITEM_POU: GetDeleteElementFunction(PLCControler.ProjectRemovePou, check_function=PLCControler.PouIsUsed),
        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.TypesTree or window is None:
            selected = self.TypesTree.GetSelection()
            if selected.IsOk():
                type = self.TypesTree.GetPyData(selected)
                function = self.DeleteFunctions.get(type, None)
                if function is not None:
                    function(self, selected)
                    self.CloseTabsWithoutModel()
                    self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, TYPESTREE, 
                                  INSTANCESTREE, LIBRARYTREE)
        elif isinstance(window, (Viewer, TextViewer)):
            event = wx.KeyEvent(wx.EVT_CHAR._getEvtType())
            event.m_keyCode = wx.WXK_DELETE
            window.ProcessEvent(event)

    def OnSearchInProjectMenu(self, event):
        dialog = SearchInProjectDialog(self)
        if dialog.ShowModal() == wx.ID_OK:
            criteria = dialog.GetCriteria()
            result = self.Controler.SearchInProject(criteria)
            self.ClearSearchResults()
            self.SearchResultPanel.SetSearchResults(criteria, result)
            self.BottomNoteBook.SetSelection(self.BottomNoteBook.GetPageIndex(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


#-------------------------------------------------------------------------------
#                      Project Editor Panels Management Functions
#-------------------------------------------------------------------------------
    
    def OnPageDragged(self, event):
        wx.CallAfter(self.RefreshTabCtrlEvent)
        event.Skip()
    
    def RefreshTabCtrlEvent(self):
        if USE_AUI:
            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 OnPouSelectedChanged(self, event):
        old_selected = self.TabsOpened.GetSelection()
        if old_selected >= 0:
            window = self.TabsOpened.GetPage(old_selected)
            if not window.IsDebugging():
                window.ResetBuffer()
        selected = event.GetSelection()
        if selected >= 0:
            window = self.TabsOpened.GetPage(selected)
            if not window.IsDebugging():
                print "Is not Debugging"
                wx.CallAfter(self.SelectTypesTreeItem, window.GetTagName())
            else:
                print "Is Debugging"
                wx.CallAfter(self.SelectInstancesTreeItem, self.InstancesTree.GetRootItem(), window.GetInstancePath())
            window.RefreshView()
            self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, TOOLBAR)
        event.Skip()

    def RefreshEditor(self):
        selected = self.TabsOpened.GetSelection()
        if USE_AUI:
            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()
        elif selected >= 0:
            window = self.TabsOpened.GetPage(idx)
            window.RefreshView()
    
    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):
                pane = self.AUIManager.GetPane(self.TabsOpened)
                if pane.IsMaximized():
                    self.AUIManager.RestorePane(pane)
                else:
                    self.AUIManager.MaximizePane(pane)
                self.AUIManager.Update()
            event.Skip()
        return OnTabsOpenedDClick

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

    def RefreshTypesTree(self):
        infos = self.Controler.GetProjectInfos()
        root = self.TypesTree.GetRootItem()
        if not root.IsOk():
            root = self.TypesTree.AddRoot(infos["name"])
        self.GenerateTypesTreeBranch(root, infos)
        self.TypesTree.Expand(root)

    def ResetSelectedItem(self):
        self.SelectedItem = None

    def GenerateTypesTreeBranch(self, root, infos, topology=False):
        to_delete = []
        item_name = infos["name"]
        if infos["type"] in ITEMS_UNEDITABLE:
            item_name = _(item_name)
        self.TypesTree.SetItemText(root, item_name)
        self.TypesTree.SetPyData(root, infos["type"])
        highlight_colours = self.Highlights.get(infos.get("tagname", None), (wx.WHITE, wx.BLACK))
        self.TypesTree.SetItemBackgroundColour(root, highlight_colours[0])
        self.TypesTree.SetItemTextColour(root, highlight_colours[1])
        if infos["type"] == ITEM_POU:
            self.TypesTree.SetItemImage(root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])])
        else:
            self.TypesTree.SetItemImage(root, self.TreeImageDict[infos["type"]])      
            
        if wx.VERSION >= (2, 6, 0):
            item, root_cookie = self.TypesTree.GetFirstChild(root)
        else:
            item, root_cookie = self.TypesTree.GetFirstChild(root, 0)
        for values in infos["values"]:
            if not item.IsOk():
                item = self.TypesTree.AppendItem(root, "")
                if wx.Platform != '__WXMSW__':
                    item, root_cookie = self.TypesTree.GetNextChild(root, root_cookie)
            self.GenerateTypesTreeBranch(item, values)
            item, root_cookie = self.TypesTree.GetNextChild(root, root_cookie)
        while item.IsOk():
            to_delete.append(item)
            item, root_cookie = self.TypesTree.GetNextChild(root, root_cookie)
        for item in to_delete:
            self.TypesTree.Delete(item)

    def SelectTypesTreeItem(self, tagname):
        if self.TypesTree is not None:
            root = self.TypesTree.GetRootItem()
            words = tagname.split("::")
            if words[0] == "D":
                return self.RecursiveTypesTreeItemSelection(root, [(words[1], ITEM_DATATYPE)])
            elif words[0] == "P":
                return self.RecursiveTypesTreeItemSelection(root, [(words[1], ITEM_POU)])
            elif words[0] == "T":
                return self.RecursiveTypesTreeItemSelection(root, [(words[1], ITEM_POU), (words[2], ITEM_TRANSITION)])
            elif words[0] == "A":
                return self.RecursiveTypesTreeItemSelection(root, [(words[1], ITEM_POU), (words[2], ITEM_ACTION)])
            elif words[0] == "C":
                return self.RecursiveTypesTreeItemSelection(root, [(words[1], ITEM_CONFIGURATION)])
            elif words[0] == "R":
                return self.RecursiveTypesTreeItemSelection(root, [(words[1], ITEM_CONFIGURATION), (words[2], ITEM_RESOURCE)])
        return False

    def RecursiveTypesTreeItemSelection(self, root, items):
        found = False
        if self.TypesTree is not None:
            if wx.VERSION >= (2, 6, 0):
                item, root_cookie = self.TypesTree.GetFirstChild(root)
            else:
                item, root_cookie = self.TypesTree.GetFirstChild(root, 0)
            while item.IsOk() and not found:
                if (self.TypesTree.GetItemText(item), self.TypesTree.GetPyData(item)) == items[0]:
                    if len(items) == 1:
                        self.SelectedItem = item
                        self.TypesTree.SelectItem(item)
                        wx.CallAfter(self.ResetSelectedItem)
                        return True
                    else:
                        found = self.RecursiveTypesTreeItemSelection(item, items[1:])
                else:
                    found = self.RecursiveTypesTreeItemSelection(item, items)
                item, root_cookie = self.TypesTree.GetNextChild(root, root_cookie)
        return found

    def OnTypesTreeBeginDrag(self, event):
        if wx.Platform == '__WXMSW__':
            self.SelectedItem = event.GetItem()
        if self.SelectedItem is not None and self.TypesTree.GetPyData(self.SelectedItem) == ITEM_POU:
            block_name = self.TypesTree.GetItemText(self.SelectedItem)
            block_type = self.Controler.GetPouType(block_name)
            if block_type != "program":
                data = wx.TextDataObject(str((block_name, block_type, "")))
                dragSource = wx.DropSource(self.TypesTree)
                dragSource.SetData(data)
                dragSource.DoDragDrop()
            self.ResetSelectedItem()

    def OnTypesTreeItemBeginEdit(self, event):
        selected = event.GetItem()
        if self.TypesTree.GetPyData(selected) in ITEMS_UNEDITABLE:
            event.Veto()
        else:
            event.Skip()

    def OnTypesTreeItemEndEdit(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.TypesTree.GetItemText(item)
                itemtype = self.TypesTree.GetPyData(item)
                if itemtype == ITEM_PROJECT:
                    self.Controler.SetProjectProperties(name = new_name)
                elif itemtype == 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(self.Controler.ComputeDataTypeName(old_name), 
                                                self.Controler.ComputeDataTypeName(new_name))
                        self.RefreshPageTitles()
                elif itemtype == 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.GetProjectPouVariables()]:
                        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(self.Controler.ComputePouName(old_name), 
                                                self.Controler.ComputePouName(new_name))
                        self.RefreshLibraryTree()
                        self.RefreshPageTitles()
                elif itemtype == ITEM_TRANSITION:
                    pou_name = GetParentName(self.TypesTree, item, ITEM_POU)
                    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.GetProjectPouVariables(pou_name) if name != old_name]:
                        message = _("A variable with \"%s\" as name already exists in this pou!")%new_name
                    else:
                        self.Controler.ChangePouTransitionName(pou_name, old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputePouTransitionName(pou_name, old_name), 
                                                self.Controler.ComputePouTransitionName(pou_name, new_name))
                        self.RefreshPageTitles()
                elif itemtype == ITEM_ACTION:
                    pou_name = GetParentName(self.TypesTree, item, ITEM_POU)
                    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.GetProjectPouVariables(pou_name) if name != old_name]:
                        message = _("A variable with \"%s\" as name already exists in this pou!")%new_name
                    else:
                        self.Controler.ChangePouActionName(pou_name, old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputePouActionName(pou_name, old_name), 
                                                self.Controler.ComputePouActionName(pou_name, new_name))
                        self.RefreshPageTitles()
                elif itemtype == 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.GetProjectPouVariables()]:
                        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(self.Controler.ComputeConfigurationName(old_name), 
                                                self.Controler.ComputeConfigurationName(new_name))
                        self.RefreshPageTitles()
                elif itemtype == ITEM_RESOURCE:
                    config_name = GetParentName(self.TypesTree, item, ITEM_CONFIGURATION)
                    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.GetProjectPouVariables()]:
                        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.ChangeConfigurationResourceName(config_name, old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputeConfigurationResourceName(config_name, old_name), 
                                                self.Controler.ComputeConfigurationResourceName(config_name, new_name))
                        self.RefreshPageTitles()
            if message or abort:
                if message:
                    self.ShowErrorMessage(message)
                item = event.GetItem()
                wx.CallAfter(self.TypesTree.EditLabel, item)
                event.Veto()
            else:
                wx.CallAfter(self.RefreshTypesTree)
                self.RefreshEditor()
                self._Refresh(TITLE, FILEMENU, EDITMENU)
                event.Skip()
    
    def OnTypesTreeItemActivated(self, event):
        selected = event.GetItem()
        name = self.TypesTree.GetItemText(selected)
        data = self.TypesTree.GetPyData(selected)
        if UNEDITABLE_NAMES_DICT.get(name, name) == "Properties":
            self.ShowProperties()
        if data == ITEM_DATATYPE:
            self.EditProjectElement(data, self.Controler.ComputeDataTypeName(name))
        elif data == ITEM_POU:
            self.EditProjectElement(data, self.Controler.ComputePouName(name))
        elif data == ITEM_CONFIGURATION:
            self.EditProjectElement(data, self.Controler.ComputeConfigurationName(name))
        elif data == ITEM_RESOURCE:
            config_name = GetParentName(self.TypesTree, selected, ITEM_CONFIGURATION)
            self.EditProjectElement(data, self.Controler.ComputeConfigurationResourceName(config_name, name))
        elif data in [ITEM_TRANSITION, ITEM_ACTION]:
            pou_name = GetParentName(self.TypesTree, selected, ITEM_POU)
            if data == ITEM_TRANSITION:
                tagname = self.Controler.ComputePouTransitionName(pou_name, name)
            elif data == ITEM_ACTION:
                tagname = self.Controler.ComputePouActionName(pou_name, name)
            self.EditProjectElement(data, tagname)
        event.Skip()
    
    def TypesTreeItemSelect(self, select_item):
        name = self.TypesTree.GetItemText(select_item)
        data = self.TypesTree.GetPyData(select_item)
        if data == ITEM_DATATYPE:
            self.EditProjectElement(data, self.Controler.ComputeDataTypeName(name), True)
        elif data == ITEM_POU:
            self.EditProjectElement(data, self.Controler.ComputePouName(name), True)
        elif data == ITEM_CONFIGURATION:
            self.EditProjectElement(data, self.Controler.ComputeConfigurationName(name), True)
        elif data == ITEM_RESOURCE:
            config_name = GetParentName(self.TypesTree, select_item, ITEM_CONFIGURATION)
            self.EditProjectElement(data, self.Controler.ComputeConfigurationResourceName(config_name, name), True)
        elif data in [ITEM_TRANSITION, ITEM_ACTION]:
            pou_name = GetParentName(self.TypesTree, select_item, ITEM_POU)
            if data == ITEM_TRANSITION:
                tagname = self.Controler.ComputePouTransitionName(pou_name, name)
            elif data == ITEM_ACTION:
                tagname = self.Controler.ComputePouActionName(pou_name, name)
            self.EditProjectElement(data, tagname, True)
    
    def OnTypesTreeLeftUp(self, event):
        if self.SelectedItem is not None:
            self.TypesTree.SelectItem(self.SelectedItem)
            self.TypesTreeItemSelect(self.SelectedItem)
            wx.CallAfter(self.ResetSelectedItem)
        event.Skip()
    
    def OnTypesTreeItemSelected(self, event):
        self.TypesTreeItemSelect(event.GetItem())
        event.Skip()
    
    def OnTypesTreeItemChanging(self, event):
        if self.TypesTree.GetPyData(event.GetItem()) not in ITEMS_UNEDITABLE and self.SelectedItem is None:
            self.SelectedItem = event.GetItem()
            event.Veto()
        else:
            event.Skip()
    
    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, TOOLBAR, PAGETITLES)
        elif not onlyopened:
            new_window = None
            if element == ITEM_CONFIGURATION:
                new_window = ConfigurationEditor(self.TabsOpened, tagname, self, self.Controler)
                new_window.SetIcon(self.GenerateBitmap("CONFIGURATION"))
                self.AddPage(new_window, "")
            elif element == ITEM_RESOURCE:
                new_window = ResourceEditor(self.TabsOpened, tagname, self, self.Controler)
                new_window.SetIcon(self.GenerateBitmap("RESOURCE"))
                self.AddPage(new_window, "")
            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 = self.GenerateBitmap(pou_type, bodytype)
                elif element == ITEM_TRANSITION:
                    icon = self.GenerateBitmap("TRANSITION", bodytype)
                elif element == ITEM_ACTION:
                    icon = self.GenerateBitmap("ACTION", bodytype)
                new_window.SetIcon(icon)
                self.AddPage(new_window, "")
                words = tagname.split("::")
            elif element == ITEM_DATATYPE:
                new_window = DataTypeEditor(self.TabsOpened, tagname, self, self.Controler)
                new_window.SetIcon(self.GenerateBitmap("DATATYPE"))
                self.AddPage(new_window, "")
            elif isinstance(element, EditorPanel):
                new_window = element
                self.AddPage(element, "")
            if new_window is not None:
                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()
    
    def OnTypesTreeRightUp(self, event):
        if wx.Platform == '__WXMSW__':
            item = event.GetItem()
        else:
            item, flags = self.TypesTree.HitTest(wx.Point(event.GetX(), event.GetY()))
        self.TypesTree.SelectItem(item)
        self.TypesTreeItemSelect(item)
        name = self.TypesTree.GetItemText(item)
        type = self.TypesTree.GetPyData(item)
        if 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(), _("Change POU Type To"), 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)
            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)
            self.PopupMenu(menu)
        elif 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)
            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)
            self.PopupMenu(menu)
        elif type in [ITEM_DATATYPE, ITEM_TRANSITION, ITEM_ACTION, ITEM_RESOURCE]:
            menu = wx.Menu(title='')
            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)
            self.PopupMenu(menu)
        elif type in ITEMS_UNEDITABLE:
            name = 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)
                self.PopupMenu(menu)
            elif name in ["Functions", "Function Blocks", "Programs"]:
                menu = wx.Menu(title='')

                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)

                self.PopupMenu(menu)
            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)
                self.PopupMenu(menu)
            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.TypesTree.GetItemParent(item)
                parent_type = self.TypesTree.GetPyData(parent)
                while parent_type != ITEM_POU:
                    parent = self.TypesTree.GetItemParent(parent)
                    parent_type = self.TypesTree.GetPyData(parent)
                self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(self.TypesTree.GetItemText(parent)), id=new_id)
                self.PopupMenu(menu)
            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.TypesTree.GetItemParent(item)
                parent_type = self.TypesTree.GetPyData(parent)
                while parent_type != ITEM_POU:
                    parent = self.TypesTree.GetItemParent(parent)
                    parent_type = self.TypesTree.GetPyData(parent)
                self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(self.TypesTree.GetItemText(parent)), id=new_id)
                self.PopupMenu(menu)
            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.TypesTree.GetItemParent(item)
                parent_type = self.TypesTree.GetPyData(parent)
                while parent_type != ITEM_CONFIGURATION:
                    parent = self.TypesTree.GetItemParent(parent)
                    parent_type = self.TypesTree.GetPyData(parent)
                self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(self.TypesTree.GetItemText(parent)), id=new_id)
                self.PopupMenu(menu)
        event.Skip()


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

    def RefreshInstancesTree(self):
        infos = self.Controler.GetProjectTopology(self.EnableDebug)
        root = self.InstancesTree.GetRootItem()
        if not root.IsOk():
            root = self.InstancesTree.AddRoot(infos["name"])
        self.GenerateInstancesTreeBranch(root, infos)
        self.InstancesTree.Expand(root)

    def GenerateInstancesTreeBranch(self, root, infos):
        to_delete = []
        if infos.get("elmt_type", None) is not None:
            self.InstancesTree.SetItemText(root, "%s (%s)"%(infos["name"], infos["elmt_type"]))
        else:
            self.InstancesTree.SetItemText(root, infos["name"])
        self.InstancesTree.SetPyData(root, (infos["type"], infos.get("tagname", None)))
        self.InstancesTree.SetItemImage(root, self.TreeImageDict[infos["type"]])
            
        if wx.VERSION >= (2, 6, 0):
            item, root_cookie = self.InstancesTree.GetFirstChild(root)
        else:
            item, root_cookie = self.InstancesTree.GetFirstChild(root, 0)
        for values in infos["values"]:
            if not item.IsOk():
                item = self.InstancesTree.AppendItem(root, "")
                if wx.Platform != '__WXMSW__':
                    item, root_cookie = self.InstancesTree.GetNextChild(root, root_cookie)
            self.GenerateInstancesTreeBranch(item, values)
            item, root_cookie = self.InstancesTree.GetNextChild(root, root_cookie)
        while item.IsOk():
            to_delete.append(item)
            item, root_cookie = self.InstancesTree.GetNextChild(root, root_cookie)
        for item in to_delete:
            self.InstancesTree.Delete(item)
        if infos["type"] in [ITEM_CONFIGURATION, ITEM_RESOURCE]:
            self.InstancesTree.Expand(root)

    def OnInstancesTreeBeginDrag(self, event):
        if self.Controler.DebugAvailable():
            selected_item = event.GetItem()
            selected_infos = self.InstancesTree.GetPyData(selected_item)
            if selected_item is not None and selected_infos[0] in ITEMS_VARIABLE:
                var_path = self.InstancesTree.GetItemText(selected_item).split(" (")[0]
                parent_item = self.InstancesTree.GetItemParent(selected_item)
                while self.InstancesTree.GetPyData(parent_item)[0] != ITEM_PROJECT:
                    parent_name = self.InstancesTree.GetItemText(parent_item).split(" (")[0]
                    var_path = "%s.%s"%(parent_name, var_path)
                    parent_item = self.InstancesTree.GetItemParent(parent_item)
                data = wx.TextDataObject(str((var_path, "debug")))
                dragSource = wx.DropSource(self.InstancesTree)
                dragSource.SetData(data)
                dragSource.DoDragDrop()
            event.Skip()
        else:
            event.Veto()

    def OnInstancesTreeItemActivated(self, event):
        if self.Controler.DebugAvailable():
            selected_item = event.GetItem()
            selected_infos = self.InstancesTree.GetPyData(selected_item)
            if selected_item is not None and selected_infos[0] in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM, ITEM_TRANSITION, ITEM_ACTION]:
                instance_path = self.InstancesTree.GetItemText(selected_item).split(" (")[0]
                parent_item = self.InstancesTree.GetItemParent(selected_item)
                while self.InstancesTree.GetPyData(parent_item)[0] != ITEM_PROJECT:
                    parent_name = self.InstancesTree.GetItemText(parent_item).split(" (")[0]
                    instance_path = "%s.%s"%(parent_name, instance_path)
                    parent_item = self.InstancesTree.GetItemParent(parent_item)
                openedidx = self.IsOpened(instance_path)
                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 selected_infos[1] is not None:
                    bodytype = self.Controler.GetEditedElementBodyType(selected_infos[1], True)
                    if bodytype == "FBD":
                        new_window = Viewer(self.TabsOpened, selected_infos[1], self, self.Controler, True, instance_path)
                        new_window.RefreshScaling(False)
                    elif bodytype == "LD":
                        new_window = LD_Viewer(self.TabsOpened, selected_infos[1], self, self.Controler, True, instance_path)
                        new_window.RefreshScaling(False)
                    elif bodytype == "SFC":
                        new_window = SFC_Viewer(self.TabsOpened, selected_infos[1], self, self.Controler, True, instance_path)
                        new_window.RefreshScaling(False)
                    else:
                        new_window = TextViewer(self.TabsOpened, selected_infos[1], 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 selected_infos[0] in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]:
                        pou_type = self.Controler.GetEditedElementType(selected_infos[1], True)[1].upper()
                        icon = self.GenerateBitmap(pou_type, bodytype)
                    elif selected_infos[0] == ITEM_TRANSITION:
                        icon = self.GenerateBitmap("TRANSITION", bodytype)
                    elif selected_infos[0] == ITEM_ACTION:
                        icon = self.GenerateBitmap("ACTION", bodytype)
                    new_window.SetIcon(icon)
                    self.AddPage(new_window, "")
                    new_window.SetFocus()
                    self.RefreshPageTitles()
            if selected_item is not None and selected_infos[0] in ITEMS_VARIABLE:
                var_path, var_type = self.InstancesTree.GetItemText(selected_item).split(" (")
                var_type = var_type.split(")")[0]
                
                if self.Controler.IsOfType(var_type, "ANY_NUM", True) or\
                   self.Controler.IsOfType(var_type, "ANY_BIT", True):
                    parent_item = self.InstancesTree.GetItemParent(selected_item)
                    while self.InstancesTree.GetPyData(parent_item)[0] != ITEM_PROJECT:
                        parent_name = self.InstancesTree.GetItemText(parent_item).split(" (")[0]
                        var_path = "%s.%s"%(parent_name, var_path)
                        parent_item = self.InstancesTree.GetItemParent(parent_item)
                    
                    self.OpenGraphicViewer(var_path)
        event.Skip()

    def OpenGraphicViewer(self, var_path):
        new_window = GraphicViewer(self.TabsOpened, self, self.Controler, var_path)
        self.AddPage(new_window, "")
        new_window.SetFocus()
        self.RefreshPageTitles()

    def OnInstancesTreeRightUp(self, event):
        if self.Controler.DebugAvailable():
            if wx.Platform == '__WXMSW__':
                selected_item = event.GetItem()
            else:
                selected_item = self.InstancesTree.GetSelection()
            selected_infos = self.InstancesTree.GetPyData(selected_item)
            if selected_item is not None and selected_infos[0] in ITEMS_VARIABLE:
                var_path, var_type = self.InstancesTree.GetItemText(selected_item).split(" (")
                var_type = var_type.split(")")[0]
                
                if self.Controler.IsOfType(var_type, "ANY_NUM", True) or\
                   self.Controler.IsOfType(var_type, "ANY_BIT", True):
                    parent_item = self.InstancesTree.GetItemParent(selected_item)
                    while self.InstancesTree.GetPyData(parent_item)[0] != ITEM_PROJECT:
                        parent_name = self.InstancesTree.GetItemText(parent_item).split(" (")[0]
                        var_path = "%s.%s"%(parent_name, var_path)
                        parent_item = self.InstancesTree.GetItemParent(parent_item)
                    
                    menu = wx.Menu(title='')
                    new_id = wx.NewId()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Graphic Panel"))
                    self.Bind(wx.EVT_MENU, self.AddVariableGraphicFunction(var_path), id=new_id)
                    new_id = wx.NewId()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("CSV Log"))
                    self.PopupMenu(menu)
        event.Skip()

    def AddVariableGraphicFunction(self, iec_path):
        def AddVariableGraphic(event):
            self.OpenGraphicViewer(iec_path)
            event.Skip()
        return AddVariableGraphic

    def SelectInstancesTreeItem(self, root, instancepath):
        found = False
        if self.InstancesTree is not None:
            paths = instancepath.split(".", 1)
            if wx.VERSION >= (2, 6, 0):
                item, root_cookie = self.InstancesTree.GetFirstChild(root)
            else:
                item, root_cookie = self.InstancesTree.GetFirstChild(root, 0)
            while item.IsOk() and not found:
                name = self.InstancesTree.GetItemText(item).split(" (")[0]
                if name == paths[0]:
                    if len(paths) == 1:
                        self.InstancesTree.SelectItem(item)
                        return True
                    else:
                        found = self.SelectInstancesTreeItem(item, paths[1])
                item, root_cookie = self.InstancesTree.GetNextChild(root, root_cookie)
        return found

    def ResetGraphicViewers(self):
        for i in xrange(self.TabsOpened.GetPageCount()):
            editor = self.TabsOpened.GetPage(i)
            if isinstance(editor, GraphicViewer):
                editor.ResetView()

    def CloseDebugTabs(self):
        if self.EnableDebug:
            idxs = range(self.TabsOpened.GetPageCount())
            idxs.reverse()
            for idx in idxs:
                window = self.TabsOpened.GetPage(idx)
                if window.IsDebugging():
                    self.TabsOpened.DeletePage(idx)
            self.DebugVariablePanel.ResetGrid()
    
    def AddDebugVariable(self, iec_path):
        if self.EnableDebug:
            self.DebugVariablePanel.InsertValue(iec_path)
            
#-------------------------------------------------------------------------------
#                         Library Tree Management Functions
#-------------------------------------------------------------------------------

    def RefreshLibraryTree(self):
        if self.Controler is not None:
            to_delete = []
            blocktypes = self.Controler.GetBlockTypes()
            root = self.LibraryTree.GetRootItem()
            if not root.IsOk():
                root = self.LibraryTree.AddRoot("")
            if wx.VERSION >= (2, 6, 0):
                category_item, root_cookie = self.LibraryTree.GetFirstChild(root)
            else:
                category_item, root_cookie = self.LibraryTree.GetFirstChild(root, 0)
            for category in blocktypes:
                category_name = category["name"]
                if not category_item.IsOk():
                    category_item = self.LibraryTree.AppendItem(root, _(category_name))
                    if wx.Platform != '__WXMSW__':
                        category_item, root_cookie = self.LibraryTree.GetNextChild(root, root_cookie)
                else:
                    self.LibraryTree.SetItemText(category_item, _(category_name))
                self.LibraryTree.SetPyData(category_item, {"type" : CATEGORY})
                if wx.VERSION >= (2, 6, 0):
                    blocktype_item, category_cookie = self.LibraryTree.GetFirstChild(category_item)
                else:
                    blocktype_item, category_cookie = self.LibraryTree.GetFirstChild(category_item, 0)        
                for blocktype in category["list"]:
                    if not blocktype_item.IsOk():
                        blocktype_item = self.LibraryTree.AppendItem(category_item, blocktype["name"])
                        if wx.Platform != '__WXMSW__':
                            blocktype_item, category_cookie = self.LibraryTree.GetNextChild(category_item, category_cookie)
                    else:
                        self.LibraryTree.SetItemText(blocktype_item, blocktype["name"])
                    self.LibraryTree.SetPyData(blocktype_item, {"type" : BLOCK, "block_type" : blocktype["type"], "inputs" : tuple([type for name, type, modifier in blocktype["inputs"]])})
                    blocktype_item, category_cookie = self.LibraryTree.GetNextChild(category_item, category_cookie)
                while blocktype_item.IsOk():
                    to_delete.append(blocktype_item)
                    blocktype_item, category_cookie = self.LibraryTree.GetNextChild(category_item, category_cookie)
                category_item, root_cookie = self.LibraryTree.GetNextChild(root, root_cookie)
            while category_item.IsOk():
                to_delete.append(category_item)
                category_item, root_cookie = self.LibraryTree.GetNextChild(root, root_cookie)
            for item in to_delete:
                self.LibraryTree.Delete(item)

    def OnLibraryTreeItemSelected(self, event):
        selected = event.GetItem()
        pydata = self.LibraryTree.GetPyData(selected)
        if pydata is not None and pydata["type"] != CATEGORY:
            blocktype = self.Controler.GetBlockType(self.LibraryTree.GetItemText(selected), pydata["inputs"])
            if blocktype:
                comment = blocktype["comment"]
                self.LibraryComment.SetValue(_(comment) + blocktype.get("usage", ""))
            else:
                self.LibraryComment.SetValue("")
        else:
            self.LibraryComment.SetValue("")
        event.Skip()

    def OnLibraryTreeBeginDrag(self, event):
        selected = event.GetItem()
        pydata = self.LibraryTree.GetPyData(selected)
        if pydata is not None and pydata["type"] == BLOCK:
            data = wx.TextDataObject(str((self.LibraryTree.GetItemText(selected), 
                pydata["block_type"], "", pydata["inputs"])))
            dragSource = wx.DropSource(self.LibraryTree)
            dragSource.SetData(data)
            dragSource.DoDragDrop()

#-------------------------------------------------------------------------------
#                          ToolBar Management Functions
#-------------------------------------------------------------------------------

    def ResetToolBar(self):
        if USE_AUI:
            ToolBar = self.Panes["ToolBar"]
        else:
            ToolBar = self.ToolBar
        
        for item in self.CurrentToolBar:
            if wx.VERSION >= (2, 6, 0):
                self.Unbind(wx.EVT_MENU, id=item)
            else:
                self.Disconnect(id=item, eventType=wx.wxEVT_COMMAND_MENU_SELECTED) 
        
            if ToolBar:
                ToolBar.DeleteTool(item)
        
        if ToolBar:
            ToolBar.Realize()
            if USE_AUI:
                self.AUIManager.GetPane("ToolBar").BestSize(ToolBar.GetBestSize())
                self.AUIManager.Update()

    def RefreshToolBar(self):
        selected = self.TabsOpened.GetSelection()
        language = None
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            if not window.IsDebugging():
                language = self.Controler.GetEditedElementBodyType(window.GetTagName())
        if language is not None and language != self.CurrentLanguage:
            self.ResetToolBar()
            self.CurrentLanguage = language
            self.CurrentToolBar = []
            if USE_AUI:
                ToolBar = self.Panes["ToolBar"]
            else:
                ToolBar = self.ToolBar
            if ToolBar:
                for radio, modes, id, method, picture, help in ToolBarItems[language]:
                    if modes & self.DrawingMode:
                        if radio or self.DrawingMode == FREEDRAWING_MODE:
                            ToolBar.AddRadioTool(id, wx.Bitmap(os.path.join(CWD, "Images", picture)), wx.NullBitmap, help)
                        else:
                            ToolBar.AddSimpleTool(id, wx.Bitmap(os.path.join(CWD, "Images", picture)), help)
                        self.Bind(wx.EVT_TOOL, getattr(self, method), id=id)
                        self.CurrentToolBar.append(id)
                ToolBar.Realize()
                if USE_AUI:
                    self.AUIManager.GetPane("ToolBar").BestSize(ToolBar.GetBestSize())
                    self.AUIManager.Update()
        elif not language:
            self.ResetToolBar()
            self.CurrentLanguage = language
        self.ResetCurrentMode()


#-------------------------------------------------------------------------------
#                           ToolBar Items Functions
#-------------------------------------------------------------------------------

    def ResetCurrentMode(self):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.SetMode(MODE_SELECTION)
        if USE_AUI:
            ToolBar = self.Panes["ToolBar"]
        else:
            ToolBar = self.ToolBar
        if ToolBar:
            ToolBar.ToggleTool(ID_PLCOPENEDITORTOOLBARSELECTION, False)
            ToolBar.ToggleTool(ID_PLCOPENEDITORTOOLBARSELECTION, True)
        
    def ResetToolToggle(self, id):
        if USE_AUI:
            tool = self.Panes["ToolBar"].FindById(id)
        else:
            tool = self.ToolBar.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_PLCOPENEDITORTOOLBARCOMMENT)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_COMMENT)
    
    def OnVariableTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITORTOOLBARVARIABLE)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_VARIABLE)
    
    def OnBlockTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITORTOOLBARBLOCK)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_BLOCK)
        
    def OnConnectionTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITORTOOLBARCONNECTION)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_CONNECTION)
    
    def OnPowerRailTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITORTOOLBARPOWERRAIL)
        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_PLCOPENEDITORTOOLBARCOIL)
        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_PLCOPENEDITORTOOLBARCONTACT)
        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_PLCOPENEDITORTOOLBARINITIALSTEP)
        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_PLCOPENEDITORTOOLBARSTEP)
        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_PLCOPENEDITORTOOLBARACTIONBLOCK)
        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_PLCOPENEDITORTOOLBARTRANSITION)
        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_PLCOPENEDITORTOOLBARDIVERGENCE)
        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_PLCOPENEDITORTOOLBARJUMP)
        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 OnAddDataTypeMenu(self, event):
        dialog = DataTypeDialog(self, _("Add a new data type"), _("Please enter data type name"), "", wx.OK|wx.CANCEL)
        dialog.SetDataTypeNames(self.Controler.GetProjectDataTypeNames())
        if dialog.ShowModal() == wx.ID_OK:
            tagname = self.Controler.ProjectAddDataType(dialog.GetValue())
            if tagname is not None:
                self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE)
                self.EditProjectElement(ITEM_DATATYPE, tagname)
        dialog.Destroy()

    def GenerateAddPouFunction(self, pou_type):
        def OnAddPouMenu(event):
            dialog = PouDialog(self, pou_type)
            dialog.SetPouNames(self.Controler.GetProjectPouNames())
            dialog.SetPouElementNames(self.Controler.GetProjectPouVariables())
            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, TYPESTREE, LIBRARYTREE)
                    self.EditProjectElement(ITEM_POU, tagname)
            dialog.Destroy()
        return OnAddPouMenu

    def GenerateAddTransitionFunction(self, pou_name):
        def OnAddTransitionMenu(event):
            dialog = PouTransitionDialog(self)
            dialog.SetPouNames(self.Controler.GetProjectPouNames())
            dialog.SetPouElementNames(self.Controler.GetProjectPouVariables(pou_name))
            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, TYPESTREE)
                    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.GetProjectPouVariables(pou_name))
            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, TYPESTREE)
                    self.EditProjectElement(ITEM_ACTION, tagname)
            dialog.Destroy()
        return OnAddActionMenu

    def OnAddConfigurationMenu(self, event):
        dialog = ConfigurationNameDialog(self, _("Please enter configuration name"), _("Add new configuration"))
        dialog.SetPouNames(self.Controler.GetProjectPouNames())
        dialog.SetPouElementNames(self.Controler.GetProjectPouVariables())
        if dialog.ShowModal() == wx.ID_OK:
            value = dialog.GetValue()
            tagname = self.Controler.ProjectAddConfiguration(value)
            if tagname is not None:
                self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE, INSTANCESTREE)
                self.EditProjectElement(ITEM_CONFIGURATION, tagname)
        dialog.Destroy()

    def GenerateAddResourceFunction(self, config_name):
        def OnAddResourceMenu(event):
            dialog = ResourceNameDialog(self, _("Please enter resource name"), _("Add new resource"))
            dialog.SetPouNames(self.Controler.GetProjectPouNames())
            dialog.SetPouElementNames(self.Controler.GetProjectPouVariables())
            if dialog.ShowModal() == wx.ID_OK:
                value = dialog.GetValue()
                tagname = self.Controler.ProjectAddConfigurationResource(config_name, value)
                if tagname is not None:
                    self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE, INSTANCESTREE)
                    self.EditProjectElement(ITEM_RESOURCE, tagname)
            dialog.Destroy()
        return OnAddResourceMenu

    def GenerateChangePouTypeFunction(self, name, new_type):
        def OnChangePouTypeMenu(event):
            selected = self.TypesTree.GetSelection()
            if self.TypesTree.GetPyData(selected) == ITEM_POU: 
                self.Controler.ProjectChangePouType(name, new_type)
                self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, TYPESTREE, LIBRARYTREE)
        return OnChangePouTypeMenu

    def OnCopyPou(self, event):
        selected = self.TypesTree.GetSelection()
        pou_name = self.TypesTree.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.TypesTree.GetSelection()

        pou_type = self.TypesTree.GetItemText(selected)
        pou_type = UNEDITABLE_NAMES_DICT[pou_type] # one of 'Functions', 'Function Blocks' or 'Programs'
        pou_type = {'Functions': 'function', 'Function Blocks': 'functionBlock', 'Programs': 'program'}[pou_type]

        pou_xml = self.GetCopyBuffer()

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

        if result is not None:
            message = wx.MessageDialog(self, result, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        else:
            self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, TYPESTREE, LIBRARYTREE)

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

    def OnRemoveDataTypeMenu(self, event):
        selected = self.TypesTree.GetSelection()
        if self.TypesTree.GetPyData(selected) == ITEM_DATATYPE:
            name = self.TypesTree.GetItemText(selected)
            if not self.Controler.DataTypeIsUsed(name):
                self.Controler.ProjectRemoveDataType(name)
                tagname = self.Controler.ComputeDataTypeName(name)
                idx = self.IsOpened(tagname)
                if idx is not None:
                    self.TabsOpened.DeletePage(idx)
                self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, TYPESTREE)
            else:
                self.ShowErrorMessage(_("\"%s\" is used by one or more POUs. It can't be removed!"))

    def OnRenamePouMenu(self, event):
        selected = self.TypesTree.GetSelection()
        if self.TypesTree.GetPyData(selected) == ITEM_POU: 
            wx.CallAfter(self.TypesTree.EditLabel, selected)

    def OnRemovePouMenu(self, event):
        selected = self.TypesTree.GetSelection()
        if self.TypesTree.GetPyData(selected) == ITEM_POU:
            name = self.TypesTree.GetItemText(selected)
            if not self.Controler.PouIsUsed(name):
                self.Controler.ProjectRemovePou(name)
                tagname = self.Controler.ComputePouName(name)
                idx = self.IsOpened(tagname)
                if idx is not None:
                    self.TabsOpened.DeletePage(idx)
                self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE)
            else:
                self.ShowErrorMessage(_("\"%s\" is used by one or more POUs. It can't be removed!"))

    def OnRemoveTransitionMenu(self, event):
        selected = self.TypesTree.GetSelection()
        if self.TypesTree.GetPyData(selected) == ITEM_TRANSITION: 
            transition = self.TypesTree.GetItemText(selected)
            item = self.TypesTree.GetItemParent(selected)
            item_type = self.TypesTree.GetPyData(item)
            while item_type != ITEM_POU:
                item = self.TypesTree.GetItemParent(item)
                item_type = self.TypesTree.GetPyData(item)
            pou_name = self.TypesTree.GetItemText(item)
            self.Controler.ProjectRemovePouTransition(pou_name, transition)
            tagname = self.Controler.ComputePouTransitionName(pou_name, transition)
            idx = self.IsOpened(tagname)
            if idx is not None:
                self.TabsOpened.DeletePage(idx)
            self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE)

    def OnRemoveActionMenu(self, event):
        selected = self.TypesTree.GetSelection()
        if self.TypesTree.GetPyData(selected) == ITEM_ACTION: 
            action = self.TypesTree.GetItemText(selected)
            item = self.TypesTree.GetItemParent(selected)
            item_type = self.TypesTree.GetPyData(item)
            while item_type != ITEM_POU:
                item = self.TypesTree.GetItemParent(item)
                item_type = self.TypesTree.GetPyData(item)
            pou_name = self.TypesTree.GetItemText(item)
            self.Controler.ProjectRemovePouAction(pou_name, action)
            tagname = self.Controler.ComputePouActionName(pou_name, action)
            idx = self.IsOpened(tagname)
            if idx is not None:
                self.TabsOpened.DeletePage(idx)
            self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE)

    def OnRemoveConfigurationMenu(self, event):
        selected = self.TypesTree.GetSelection()
        if self.TypesTree.GetPyData(selected) == ITEM_CONFIGURATION: 
            name = self.TypesTree.GetItemText(selected)
            self.Controler.ProjectRemoveConfiguration(name)
            tagname = self.Controler.ComputeConfigurationName(name)
            idx = self.IsOpened(tagname)
            if idx is not None:
                self.TabsOpened.DeletePage(idx)
            self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE, INSTANCESTREE)

    def OnRemoveResourceMenu(self, event):
        selected = self.TypesTree.GetSelection()
        if self.TypesTree.GetPyData(selected) == ITEM_RESOURCE:
            resource = self.TypesTree.GetItemText(selected)
            item = self.TypesTree.GetItemParent(selected)
            item_type = self.TypesTree.GetPyData(item)
            while item_type != ITEM_CONFIGURATION:
                item = self.TypesTree.GetItemParent(item)
                item_type = self.TypesTree.GetPyData(item)
            config_name = self.TypesTree.GetItemText(item)
            self.Controler.ProjectRemoveConfigurationResource(config_name, resource)
            tagname = self.Controler.ComputeConfigurationResourceName(config_name, selected)
            idx = self.IsOpened(tagname)
            if idx is not None:
                self.TabsOpened.DeletePage(idx)
            self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE, INSTANCESTREE)
    
    def OnPLCOpenEditorMenu(self, event):
        wx.MessageBox(_("No documentation available.\nComing soon."))
        
    def OnPLCOpenMenu(self, event):
        open_pdf(os.path.join(CWD, "plcopen", "TC6_XML_V101.pdf"))
    
    def OnAboutMenu(self, event):
        OpenHtmlFrame(self,_("About PLCOpenEditor"), os.path.join(CWD, "doc","about.html"), wx.Size(350, 350))


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

    def ShowHighlight(self, infos, start, end, highlight_type):
        self.SelectTypesTreeItem(infos[0])
        if infos[1] == "name":
            self.Highlights[infos[0]] = highlight_type
            self.RefreshTypesTree()
            self.TypesTree.Unselect()
        else:
            self.EditProjectElement(self.Controler.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.RefreshTypesTree()
        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)

#-------------------------------------------------------------------------------
#                            PLCOpenEditor Main Class
#-------------------------------------------------------------------------------

class PLCOpenEditor(IDEFrame):

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

    def _init_coll_FileMenu_Items(self, parent):
        AppendMenu(parent, help='', id=wx.ID_NEW,
              kind=wx.ITEM_NORMAL, text=_(u'New\tCTRL+N'))
        AppendMenu(parent, help='', id=wx.ID_OPEN,
              kind=wx.ITEM_NORMAL, text=_(u'Open\tCTRL+O'))
        AppendMenu(parent, help='', id=wx.ID_CLOSE,
              kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W'))
        AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL,
              kind=wx.ITEM_NORMAL, text=_(u'Close Project'))
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_SAVE,
              kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S'))
        AppendMenu(parent, help='', id=wx.ID_SAVEAS,
              kind=wx.ITEM_NORMAL, text=_(u'Save As...\tCTRL+SHIFT+S'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATE,
              kind=wx.ITEM_NORMAL, text=_(u'Generate Program\tCTRL+G'))
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
              kind=wx.ITEM_NORMAL, text=_(u'Page Setup'))
        AppendMenu(parent, help='', id=wx.ID_PREVIEW,
              kind=wx.ITEM_NORMAL, text=_(u'Preview'))
        AppendMenu(parent, help='', id=wx.ID_PRINT,
              kind=wx.ITEM_NORMAL, text=_(u'Print'))
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
              kind=wx.ITEM_NORMAL, text=_(u'Properties'))
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_EXIT,
              kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q'))
        
        self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
        self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
        self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
        self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
        self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
        self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
        self.Bind(wx.EVT_MENU, self.OnGenerateProgramMenu,
              id=ID_PLCOPENEDITORFILEMENUGENERATE)
        self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
        self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
        self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
        self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
        self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)

    def _init_coll_HelpMenu_Items(self, parent):
        AppendMenu(parent, help='', id=wx.ID_HELP, 
            kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor\tF1'))
        #AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS,
        #      kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2')
        #AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT,
        #      kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3')
        AppendMenu(parent, help='', id=wx.ID_ABOUT,
            kind=wx.ITEM_NORMAL, text=_(u'About'))
        self.Bind(wx.EVT_MENU, self.OnPLCOpenEditorMenu, id=wx.ID_HELP)
        #self.Bind(wx.EVT_MENU, self.OnPLCOpenMenu, id=wx.ID_HELP_CONTENTS)
        self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)

    ## Constructor of the PLCOpenEditor class.
    #  @param parent The parent window.
    #  @param controler The controler been used by PLCOpenEditor (default: None).
    #  @param fileOpen The filepath to open if no controler defined (default: None).
    #  @param debug The filepath to open if no controler defined (default: False).
    def __init__(self, parent, fileOpen = None):
        IDEFrame.__init__(self, parent)
        
        result = None
        
        # Open the filepath if defined
        if fileOpen is not None and os.path.isfile(fileOpen):
            # Create a new controller
            self.Controler = PLCControler()
            result = self.Controler.OpenXMLFile(fileOpen)
            if result is None:
                self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
        
        # Define PLCOpenEditor icon
        self.SetIcon(wx.Icon(os.path.join(CWD,"Images", "poe.ico"),wx.BITMAP_TYPE_ICO))

        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
        
        self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        
        if result is not None:
            self.ShowErrorMessage(result)

    def OnCloseFrame(self, event):
        if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")):
            if USE_AUI:
                self.AUIManager.UnInit()
            
            self.SaveFrameSize()
            
            event.Skip()
        else:
            event.Veto()

    def RefreshTitle(self):
        name = _("PLCOpenEditor")
        if self.Controler is not None:
            self.SetTitle("%s - %s"%(name, self.Controler.GetFilename()))
        else:
            self.SetTitle(name)

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

    def RefreshFileMenu(self):
        if self.Controler is not None:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
            else:
                graphic_viewer = False
            if self.TabsOpened.GetPageCount() > 0:
                self.FileMenu.Enable(wx.ID_CLOSE, True)
                if graphic_viewer:
                    self.FileMenu.Enable(wx.ID_PREVIEW, True)
                    self.FileMenu.Enable(wx.ID_PRINT, True)
                else:
                    self.FileMenu.Enable(wx.ID_PREVIEW, False)
                    self.FileMenu.Enable(wx.ID_PRINT, False)
            else:
                self.FileMenu.Enable(wx.ID_CLOSE, False)
                self.FileMenu.Enable(wx.ID_PREVIEW, False)
                self.FileMenu.Enable(wx.ID_PRINT, False)
            self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
            self.FileMenu.Enable(wx.ID_SAVE, True)
            self.FileMenu.Enable(wx.ID_PROPERTIES, True)
            self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
            self.FileMenu.Enable(wx.ID_SAVEAS, True)
            self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, True)
        else:
            self.FileMenu.Enable(wx.ID_CLOSE, False)
            self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
            self.FileMenu.Enable(wx.ID_PREVIEW, False)
            self.FileMenu.Enable(wx.ID_PRINT, False)
            self.FileMenu.Enable(wx.ID_SAVE, False)
            self.FileMenu.Enable(wx.ID_PROPERTIES, False)
            self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
            self.FileMenu.Enable(wx.ID_SAVEAS, False)
            self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, False)

    def OnNewProjectMenu(self, event):
        if self.Controler is not None and not self.CheckSaveBeforeClosing():
            return
        dialog = ProjectDialog(self)
        if dialog.ShowModal() == wx.ID_OK:
            properties = dialog.GetValues()
            self.ResetView()
            self.Controler = PLCControler()
            self.Controler.CreateNewProject(properties)
            self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE, INSTANCESTREE, 
                          LIBRARYTREE)

    def OnOpenProjectMenu(self, event):
        if self.Controler is not None and not self.CheckSaveBeforeClosing():
            return
        filepath = ""
        if self.Controler is not None:
            filepath = self.Controler.GetFilePath()
        if filepath != "":
            directory = os.path.dirname(filepath)
        else:
            directory = os.getcwd()
        
        result = None
        
        dialog = wx.FileDialog(self, _("Choose a file"), directory, "",  _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            filepath = dialog.GetPath()
            if os.path.isfile(filepath):
                self.ResetView()
                self.Controler = PLCControler()
                result = self.Controler.OpenXMLFile(filepath)
                if result is None:
                    self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
            self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
        dialog.Destroy()
        
        if result is not None:
            self.ShowErrorMessage(result)
    
    def OnCloseProjectMenu(self, event):
        if not self.CheckSaveBeforeClosing():
            return
        self.ResetView()
        self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)

    def OnSaveProjectMenu(self, event):
        self.SaveProject()

    def OnSaveProjectAsMenu(self, event):
        self.SaveProjectAs()

    def OnGenerateProgramMenu(self, event):
        dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), self.Controler.GetProgramFilePath(),  _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE|wx.CHANGE_DIR)
        if dialog.ShowModal() == wx.ID_OK:
            filepath = dialog.GetPath()
            message_text = ""
            header, icon = _("Done"), wx.ICON_INFORMATION
            if os.path.isdir(os.path.dirname(filepath)):
                program, errors, warnings = self.Controler.GenerateProgram(filepath)
                message_text += "".join([_("warning: %s\n") for warning in warnings])
                if len(errors) > 0:
                    message_text += "".join([_("error: %s\n") for error in errors])
                    message_text += _("Can't generate program to file %s!")%filepath
                    header, icon = _("Error"), wx.ICON_ERROR
                else:
                    message_text += _("Program was successfully generated!")
            else:
                message_text += _("\"%s\" is not a valid folder!")%os.path.dirname(filepath)
                header, icon = _("Error"), wx.ICON_ERROR
            message = wx.MessageDialog(self, message_text, header, wx.OK|icon)
            message.ShowModal()
            message.Destroy()
        dialog.Destroy()

    def SaveProject(self):
        result = self.Controler.SaveXMLFile()
        if not result:
            self.SaveProjectAs()
        else:
            self._Refresh(TITLE, FILEMENU, PAGETITLES)
        
    def SaveProjectAs(self):
        filepath = self.Controler.GetFilePath()
        if filepath != "":
            directory, filename = os.path.split(filepath)
        else:
            directory, filename = os.getcwd(), "%(projectName)s.xml"%self.Controler.GetProjectProperties()
        dialog = wx.FileDialog(self, _("Choose a file"), directory, filename,  _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.SAVE|wx.OVERWRITE_PROMPT)
        if dialog.ShowModal() == wx.ID_OK:
            filepath = dialog.GetPath()
            if os.path.isdir(os.path.dirname(filepath)):
                result = self.Controler.SaveXMLFile(filepath)
                if not result:
                    self.ShowErrorMessage(_("Can't save project to file %s!")%filepath)
            else:
                self.ShowErrorMessage(_("\"%s\" is not a valid folder!")%os.path.dirname(filepath))
            self._Refresh(TITLE, FILEMENU, PAGETITLES)
        dialog.Destroy()

#-------------------------------------------------------------------------------
#                            Create Project Dialog
#-------------------------------------------------------------------------------

[ID_SCALINGPANEL, ID_SCALINGPANELXSCALE, 
 ID_SCALINGPANELYSCALE, ID_SCALINGPANELSTATICTEXT1, 
 ID_SCALINGPANELSTATICTEXT2, 
] = [wx.NewId() for _init_ctrls in range(5)]

class ScalingPanel(wx.Panel):
    
    def _init_coll_ScalingPanelSizer_Items(self, parent):
        parent.AddWindow(self.staticText1, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT)
        parent.AddWindow(self.XScale, 0, border=10, flag=wx.GROW|wx.TOP|wx.RIGHT)
        parent.AddWindow(self.staticText2, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM|wx.LEFT)
        parent.AddWindow(self.YScale, 0, border=10, flag=wx.GROW|wx.BOTTOM|wx.RIGHT)

    def _init_coll_ScalingPanelSizer_Growables(self, parent):
        parent.AddGrowableCol(1)

    def _init_sizers(self):
        self.ScalingPanelSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
        
        self._init_coll_ScalingPanelSizer_Items(self.ScalingPanelSizer)
        self._init_coll_ScalingPanelSizer_Growables(self.ScalingPanelSizer)

        self.SetSizer(self.ScalingPanelSizer)

    def _init_ctrls(self, prnt):
        wx.Panel.__init__(self, id=ID_SCALINGPANEL,
              name='ScalingPanel', parent=prnt, pos=wx.Point(0, 0),
              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
        
        self.staticText1 = wx.StaticText(id=ID_SCALINGPANELSTATICTEXT1,
              label=_('Horizontal:'), name='staticText1', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
        
        self.XScale = wx.SpinCtrl(id=ID_SCALINGPANELXSCALE,
              name='XScale', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0, min=0, max=2**16)
        
        self.staticText2 = wx.StaticText(id=ID_SCALINGPANELSTATICTEXT2,
              label=_('Vertical:'), name='staticText2', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
        
        self.YScale = wx.SpinCtrl(id=ID_SCALINGPANELYSCALE,
              name='YScale', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0, min=0, max=2**16)
        
        self._init_sizers()
        
    def __init__(self, parent):
        self._init_ctrls(parent)

    def SetScaling(self, x, y):
        self.XScale.SetValue(x)
        self.YScale.SetValue(y)
        
    def GetScaling(self):
        return self.XScale.GetValue(), self.YScale.GetValue()

[ID_PROJECTDIALOG, ID_PROJECTDIALOGMAINNOTEBOOK, 
 ID_PROJECTDIALOGPROJECTPANEL, ID_PROJECTDIALOGAUTHORPANEL, 
 ID_PROJECTDIALOGGRAPHICSPANEL, ID_PROJECTDIALOGMISCELLANEOUSPANEL, 
 ID_PROJECTDIALOGPROJECTNAME, ID_PROJECTDIALOGPROJECTVERSION, 
 ID_PROJECTDIALOGPRODUCTNAME, ID_PROJECTDIALOGPRODUCTVERSION, 
 ID_PROJECTDIALOGPRODUCTRELEASE, ID_PROJECTDIALOGCOMPANYNAME, 
 ID_PROJECTDIALOGCOMPANYURL, ID_PROJECTDIALOGAUTHORNAME, 
 ID_PROJECTDIALOGORGANIZATION, ID_PROJECTDIALOGLANGUAGE, 
 ID_PROJECTDIALOGCONTENTDESCRIPTION, ID_PROJECTDIALOGSCALINGNOTEBOOK, 
 ID_PROJECTDIALOGPAGEWIDTH, ID_PROJECTDIALOGPAGEHEIGHT, 
 ID_PROJECTDIALOGSTATICTEXT1, ID_PROJECTDIALOGSTATICTEXT2, 
 ID_PROJECTDIALOGSTATICTEXT3, ID_PROJECTDIALOGSTATICTEXT4, 
 ID_PROJECTDIALOGSTATICTEXT5, ID_PROJECTDIALOGSTATICTEXT6, 
 ID_PROJECTDIALOGSTATICTEXT7, ID_PROJECTDIALOGSTATICTEXT8, 
 ID_PROJECTDIALOGSTATICTEXT9, ID_PROJECTDIALOGSTATICTEXT10, 
 ID_PROJECTDIALOGSTATICTEXT11, ID_PROJECTDIALOGSTATICTEXT12, 
 ID_PROJECTDIALOGSTATICTEXT13, ID_PROJECTDIALOGSTATICTEXT14, 
 ID_PROJECTDIALOGSTATICTEXT15, 
] = [wx.NewId() for _init_ctrls in range(35)]

class ProjectDialog(wx.Dialog):
    if wx.VERSION < (2, 6, 0):
        def Bind(self, event, function, id = None):
            if id is not None:
                event(self, id, function)
            else:
                event(self, function)
                
    def _init_coll_flexGridSizer1_Items(self, parent):
        parent.AddSizer(self.MainNotebook, 0, border=0, flag=wx.GROW)
        parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
        
    def _init_coll_flexGridSizer1_Growables(self, parent):
        parent.AddGrowableCol(0)
        parent.AddGrowableRow(0)
    
    def _init_coll_ProjectPanelSizer_Items(self, parent):
        parent.AddWindow(self.staticText1, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT)
        parent.AddWindow(self.ProjectName, 0, border=10, flag=wx.GROW|wx.TOP|wx.RIGHT)
        parent.AddWindow(self.staticText2, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT)
        parent.AddWindow(self.ProjectVersion, 0, border=10, flag=wx.GROW|wx.RIGHT)
        parent.AddWindow(self.staticText3, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT)
        parent.AddWindow(self.ProductName, 0, border=10, flag=wx.GROW|wx.RIGHT)
        parent.AddWindow(self.staticText4, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT)
        parent.AddWindow(self.ProductVersion, 0, border=10, flag=wx.GROW|wx.RIGHT)
        parent.AddWindow(self.staticText5, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM|wx.LEFT)
        parent.AddWindow(self.ProductRelease, 0, border=10, flag=wx.GROW|wx.BOTTOM|wx.RIGHT)
        
    def _init_coll_ProjectPanelSizer_Growables(self, parent):
        parent.AddGrowableCol(1)

    def _init_coll_AuthorPanelSizer_Items(self, parent):
        parent.AddWindow(self.staticText6, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT)
        parent.AddWindow(self.CompanyName, 0, border=10, flag=wx.GROW|wx.TOP|wx.RIGHT)
        parent.AddWindow(self.staticText7, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT)
        parent.AddWindow(self.CompanyURL, 0, border=10, flag=wx.GROW|wx.RIGHT)
        parent.AddWindow(self.staticText8, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT)
        parent.AddWindow(self.AuthorName, 0, border=10, flag=wx.GROW|wx.RIGHT)
        parent.AddWindow(self.staticText9, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM|wx.LEFT)
        parent.AddWindow(self.Organization, 0, border=10, flag=wx.GROW|wx.BOTTOM|wx.RIGHT)
    
    def _init_coll_AuthorPanelSizer_Growables(self, parent):
        parent.AddGrowableCol(1)
    
    def _init_coll_GraphicsPanelSizer_Items(self, parent):
        parent.AddWindow(self.staticText12, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT|wx.RIGHT)
        parent.AddSizer(self.GraphicsPageSizeSizer, 0, border=10, flag=wx.GROW|wx.LEFT|wx.RIGHT)
        parent.AddWindow(self.staticText15, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT)
        parent.AddWindow(self.ScalingNotebook, 0, border=10, flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT)
        
    def _init_coll_GraphicsPanelSizer_Growables(self, parent):
        parent.AddGrowableCol(0)
        parent.AddGrowableRow(3)
    
    def _init_coll_GraphicsPageSizeSizer_Items(self, parent):
        parent.AddWindow(self.staticText13, 0, border=12, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT)
        parent.AddWindow(self.PageWidth, 0, border=0, flag=wx.GROW)
        parent.AddWindow(self.staticText14, 0, border=12, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT)
        parent.AddWindow(self.PageHeight, 0, border=0, flag=wx.GROW)
    
    def _init_coll_GraphicsPageSizeSizer_Growables(self, parent):
        parent.AddGrowableCol(1)
    
    def _init_coll_MiscellaneousPanelSizer_Items(self, parent):
        parent.AddWindow(self.staticText10, 0, border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT)
        parent.AddWindow(self.Language, 0, border=10, flag=wx.GROW|wx.TOP|wx.RIGHT)
        parent.AddWindow(self.staticText11, 0, border=10, flag=wx.BOTTOM|wx.LEFT)
        parent.AddWindow(self.ContentDescription, 0, border=10, flag=wx.GROW|wx.BOTTOM|wx.RIGHT)
        
    def _init_coll_MiscellaneousPanelSizer_Growables(self, parent):
        parent.AddGrowableCol(1)
        parent.AddGrowableRow(1)
        
    def _init_sizers(self):
        self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
        self.ProjectPanelSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=5, vgap=15)
        self.AuthorPanelSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=4, vgap=15)
        self.GraphicsPanelSizer = wx.FlexGridSizer(cols=1, hgap=5, rows=4, vgap=5)
        self.GraphicsPageSizeSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
        self.MiscellaneousPanelSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=15)

        self._init_coll_flexGridSizer1_Items(self.flexGridSizer1)
        self._init_coll_flexGridSizer1_Growables(self.flexGridSizer1)
        self._init_coll_ProjectPanelSizer_Items(self.ProjectPanelSizer)
        self._init_coll_ProjectPanelSizer_Growables(self.ProjectPanelSizer)
        self._init_coll_AuthorPanelSizer_Items(self.AuthorPanelSizer)
        self._init_coll_AuthorPanelSizer_Growables(self.AuthorPanelSizer)
        self._init_coll_GraphicsPanelSizer_Items(self.GraphicsPanelSizer)
        self._init_coll_GraphicsPanelSizer_Growables(self.GraphicsPanelSizer)
        self._init_coll_GraphicsPageSizeSizer_Items(self.GraphicsPageSizeSizer)
        self._init_coll_GraphicsPageSizeSizer_Growables(self.GraphicsPageSizeSizer)
        self._init_coll_MiscellaneousPanelSizer_Items(self.MiscellaneousPanelSizer)
        self._init_coll_MiscellaneousPanelSizer_Growables(self.MiscellaneousPanelSizer)

        self.SetSizer(self.flexGridSizer1)
        self.ProjectPanel.SetSizer(self.ProjectPanelSizer)
        self.AuthorPanel.SetSizer(self.AuthorPanelSizer)
        self.GraphicsPanel.SetSizer(self.GraphicsPanelSizer)
        self.MiscellaneousPanel.SetSizer(self.MiscellaneousPanelSizer)

    def _init_ctrls(self, prnt):
        wx.Dialog.__init__(self, id=ID_PROJECTDIALOG,
              name='ProjectDialog', parent=prnt,
              size=wx.Size(500, 350), style=wx.DEFAULT_DIALOG_STYLE,
              title=_('Project properties'))
        self.SetClientSize(wx.Size(500, 350))

        self.MainNotebook = wx.Notebook(id=ID_PROJECTDIALOGMAINNOTEBOOK,
                  name='MainNotebook', parent=self, pos=wx.Point(0,
                  0), size=wx.Size(0, 0), style=0)

        # Project Panel elements

        self.ProjectPanel = wx.Panel(id=ID_PROJECTDIALOGPROJECTPANEL,
              name='ProjectPanel', parent=self.MainNotebook, pos=wx.Point(0, 0),
              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)

        self.staticText1 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT1,
              label=_('Project Name (required):'), name='staticText1', parent=self.ProjectPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.ProjectName = wx.TextCtrl(id=ID_PROJECTDIALOGPROJECTNAME,
              name='ProjectName', parent=self.ProjectPanel, pos=wx.Point(0, 0), 
              size=wx.Size(0, 24), style=0)

        self.staticText2 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT2,
              label=_('Project Version (optional):'), name='staticText2', parent=self.ProjectPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.ProjectVersion = wx.TextCtrl(id=ID_PROJECTDIALOGPROJECTVERSION,
              name='ProjectVersion', parent=self.ProjectPanel, pos=wx.Point(0, 0), 
              size=wx.Size(0, 24), style=0)

        self.staticText3 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT3,
              label=_('Product Name (required):'), name='staticText3', parent=self.ProjectPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.ProductName = wx.TextCtrl(id=ID_PROJECTDIALOGPRODUCTNAME,
              name='ProductName', parent=self.ProjectPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.staticText4 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT4,
              label=_('Product Version (required):'), name='staticText4', parent=self.ProjectPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.ProductVersion = wx.TextCtrl(id=ID_PROJECTDIALOGPRODUCTVERSION,
              name='ProductVersion', parent=self.ProjectPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.staticText5 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT5,
              label=_('Product Release (optional):'), name='staticText5', parent=self.ProjectPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.ProductRelease = wx.TextCtrl(id=ID_PROJECTDIALOGPRODUCTRELEASE,
              name='ProductRelease', parent=self.ProjectPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.MainNotebook.AddPage(self.ProjectPanel, _("Project"))
        
        # Author Panel elements

        self.AuthorPanel = wx.Panel(id=ID_PROJECTDIALOGAUTHORPANEL,
              name='AuthorPanel', parent=self.MainNotebook, pos=wx.Point(0, 0),
              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)

        self.staticText6 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT6,
              label=_('Company Name (required):'), name='staticText6', parent=self.AuthorPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.CompanyName = wx.TextCtrl(id=ID_PROJECTDIALOGCOMPANYNAME,
              name='CompanyName', parent=self.AuthorPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.staticText7 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT7,
              label=_('Company URL (optional):'), name='staticText7', parent=self.AuthorPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.CompanyURL = wx.TextCtrl(id=ID_PROJECTDIALOGCOMPANYURL,
              name='CompanyURL', parent=self.AuthorPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.staticText8 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT8,
              label=_('Author Name (optional):'), name='staticText8', parent=self.AuthorPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.AuthorName = wx.TextCtrl(id=ID_PROJECTDIALOGAUTHORNAME,
              name='AuthorName', parent=self.AuthorPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.staticText9 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT9,
              label=_('Organization (optional):'), name='staticText9', parent=self.AuthorPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.Organization = wx.TextCtrl(id=ID_PROJECTDIALOGORGANIZATION,
              name='Organization', parent=self.AuthorPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.MainNotebook.AddPage(self.AuthorPanel, _("Author"))

        # Graphics Panel elements

        self.GraphicsPanel = wx.Panel(id=ID_PROJECTDIALOGGRAPHICSPANEL,
              name='GraphicsPanel', parent=self.MainNotebook, pos=wx.Point(0, 0),
              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)

        self.staticText12 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT12,
              label=_('Page Size (optional):'), name='staticText12', parent=self.GraphicsPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.staticText13 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT13,
              label=_('Width:'), name='staticText13', parent=self.GraphicsPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
        
        self.PageWidth = wx.SpinCtrl(id=ID_PROJECTDIALOGPAGEWIDTH,
              name='PageWidth', parent=self.GraphicsPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0, min=0, max=2**16)

        self.staticText14 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT14,
              label=_('Height:'), name='staticText14', parent=self.GraphicsPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
        
        self.PageHeight = wx.SpinCtrl(id=ID_PROJECTDIALOGPAGEHEIGHT,
              name='PageHeight', parent=self.GraphicsPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0, min=0, max=2**16)
        
        self.staticText15 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT15,
              label=_('Grid Resolution:'), name='staticText15', parent=self.GraphicsPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
        
        self.ScalingNotebook = wx.Notebook(id=ID_PROJECTDIALOGSCALINGNOTEBOOK,
              name='ScalingNotebook', parent=self.GraphicsPanel, pos=wx.Point(0,
              0), size=wx.Size(0, 0), style=0)
        
        self.Scalings = {}
        for language, translation in [("FBD",_("FBD")), ("LD",_("LD")), ("SFC",_("SFC"))]:
            window = ScalingPanel(self.ScalingNotebook)
            self.Scalings[language] = window
            self.ScalingNotebook.AddPage(window, translation)
        
        self.MainNotebook.AddPage(self.GraphicsPanel, _("Graphics"))

        # Miscellaneous Panel elements

        self.MiscellaneousPanel = wx.Panel(id=ID_PROJECTDIALOGMISCELLANEOUSPANEL,
              name='MiscellaneousPanel', parent=self.MainNotebook, pos=wx.Point(0, 0),
              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)

        self.staticText10 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT10,
              label=_('Language (optional):'), name='staticText10', parent=self.MiscellaneousPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.Language = wx.ComboBox(id=ID_PROJECTDIALOGLANGUAGE,
              name='Language', parent=self.MiscellaneousPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 28), style=wx.CB_READONLY)

        self.staticText11 = wx.StaticText(id=ID_PROJECTDIALOGSTATICTEXT11,
              label=_('Content Description (optional):'), name='staticText11', parent=self.MiscellaneousPanel,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.ContentDescription = wx.TextCtrl(id=ID_PROJECTDIALOGCONTENTDESCRIPTION,
              name='ContentDescription', parent=self.MiscellaneousPanel, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=wx.TE_MULTILINE)

        self.MainNotebook.AddPage(self.MiscellaneousPanel, _("Miscellaneous"))

        self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
        self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
        
        self._init_sizers()

    def __init__(self, parent, enable_required=True):
        self._init_ctrls(parent)
        
        self.ProjectName.Enable(enable_required)
        self.ProductName.Enable(enable_required)
        self.ProductVersion.Enable(enable_required)
        self.CompanyName.Enable(enable_required)
            
        languages = ["", "en-US", "fr-FR", "zh-CN"]
        
        for language in languages:
            self.Language.Append(language)
        
    def OnOK(self, event):
        error = []
        if self.ProjectName.GetValue() == "":
            error.append("Project Name")
        if self.CompanyName.GetValue() == "":
            error.append("Company Name")
        if self.ProductName.GetValue() == "":
            error.append("Product Name")
        if self.ProductVersion.GetValue() == "":
            error.append("Product Version")
        if len(error) > 0:
            text = ""
            for i, item in enumerate(error):
                if i == 0:
                    text += item
                elif i == len(error) - 1:
                    text += " and %s"%item
                else:
                    text += ", %s"%item
            message = wx.MessageDialog(self, _("Form isn't complete. %s must be filled!")%text, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        else:
            self.EndModal(wx.ID_OK)

    def SetValues(self, values):
        for item, value in values.items():
            if item == "projectName":
                self.ProjectName.SetValue(value)
            elif item == "projectVersion":
                self.ProjectVersion.SetValue(value)
            elif item == "productName":
                self.ProductName.SetValue(value)
            elif item == "productVersion":
                self.ProductVersion.SetValue(value)
            elif item == "productRelease":
                self.ProductRelease.SetValue(value)
            elif item == "companyName":
                self.CompanyName.SetValue(value)
            elif item == "companyURL":
                self.CompanyURL.SetValue(value)
            elif item == "authorName":
                self.AuthorName.SetValue(value)
            elif item == "organization":
                self.Organization.SetValue(value)
            elif item == "language":
                self.Language.SetStringSelection(value)
            elif item == "contentDescription":
                self.ContentDescription.SetValue(value)
            elif item == "pageSize":
                self.PageWidth.SetValue(value[0])
                self.PageHeight.SetValue(value[1])
            elif item == "scaling":
                for language, (x, y) in value.items():
                    if language in self.Scalings:
                        self.Scalings[language].SetScaling(x, y)
    
    def GetValues(self):
        values = {}
        values["projectName"] = self.ProjectName.GetValue()
        if self.ProjectVersion.GetValue() != "":
            values["projectVersion"] = self.ProjectVersion.GetValue()
        values["productName"] = self.ProductName.GetValue()
        values["productVersion"] = self.ProductVersion.GetValue()
        if self.ProductRelease.GetValue() != "":
            values["productRelease"] = self.ProductRelease.GetValue()
        values["companyName"] = self.CompanyName.GetValue()
        if self.CompanyURL.GetValue() != "":
            values["companyURL"] = self.CompanyURL.GetValue()
        if self.AuthorName.GetValue() != "":
            values["authorName"] = self.AuthorName.GetValue()
        if self.Organization.GetValue() != "":
            values["organization"] = self.Organization.GetValue()
        if self.Language.GetStringSelection() != "":
            values["language"] = self.Language.GetStringSelection()
        if self.ProductRelease.GetValue() != "":
            values["contentDescription"] = self.ContentDescription.GetValue()
        values["pageSize"] = (self.PageWidth.GetValue(), self.PageHeight.GetValue())
        values["scaling"] = {}
        for language in ["FBD", "LD", "SFC"]:
            values["scaling"][language] = self.Scalings[language].GetScaling()
        return values

#-------------------------------------------------------------------------------
#                          Edit Step Name Dialog
#-------------------------------------------------------------------------------

class DataTypeDialog(wx.TextEntryDialog):

    if wx.VERSION < (2, 6, 0):
        def Bind(self, event, function, id = None):
            if id is not None:
                event(self, id, function)
            else:
                event(self, function)
    
    def __init__(self, parent, message, caption = _("Please enter text"), defaultValue = "", 
                       style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
        wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
        
        self.DataTypeNames = []
        if wx.VERSION >= (2, 8, 0):
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton().GetId())
        elif wx.VERSION >= (2, 6, 0):
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
        else:
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
    
    def OnOK(self, event):
        datatype_name = self.GetSizer().GetItem(1).GetWindow().GetValue()
        if datatype_name == "":
            message = wx.MessageDialog(self, _("You must type a name!"), _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif not TestIdentifier(datatype_name):
            message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%datatype_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif datatype_name.upper() in IEC_KEYWORDS:
            message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%datatype_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif datatype_name.upper() in self.DataTypeNames:
            message = wx.MessageDialog(self, _("\"%s\" data type already exists!")%datatype_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        else:
            self.EndModal(wx.ID_OK)

    def SetDataTypeNames(self, datatype_names):
        self.DataTypeNames = [datatype_name.upper() for datatype_name in datatype_names]

    def GetValue(self):
        return self.GetSizer().GetItem(1).GetWindow().GetValue()

#-------------------------------------------------------------------------------
#                            Create Pou Dialog
#-------------------------------------------------------------------------------

[ID_POUDIALOG, ID_POUDIALOGPOUNAME, 
 ID_POUDIALOGPOUTYPE, ID_POUDIALOGLANGUAGE, ID_POUDIALOGSTATICTEXT1,
 ID_POUDIALOGSTATICTEXT2, ID_POUDIALOGSTATICTEXT3, 
] = [wx.NewId() for _init_ctrls in range(7)]

def GetTransitionLanguages():
    _ = lambda x : x
    return [_("IL"), _("ST"), _("LD"), _("FBD")]
TRANSITION_LANGUAGES_DICT = dict([(_(language), language) for language in GetTransitionLanguages()])

def GetPouTypes():
    _ = lambda x : x
    return [_("function"), _("functionBlock"), _("program")]
POU_TYPES_DICT = dict([(_(pou_type), pou_type) for pou_type in GetPouTypes()])

def GetPouLanguages():
    _ = lambda x : x
    return [_("IL"), _("ST"), _("LD"), _("FBD"), _("SFC")]
POU_LANGUAGES_DICT = dict([(_(language), language) for language in GetPouLanguages()])

class PouDialog(wx.Dialog):
    if wx.VERSION < (2, 6, 0):
        def Bind(self, event, function, id = None):
            if id is not None:
                event(self, id, function)
            else:
                event(self, function)
    
    def _init_coll_flexGridSizer1_Items(self, parent):
        parent.AddSizer(self.MainSizer, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
        parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
        
    def _init_coll_flexGridSizer1_Growables(self, parent):
        parent.AddGrowableCol(0)
        parent.AddGrowableRow(0)
    
    def _init_coll_MainSizer_Items(self, parent):
        parent.AddWindow(self.staticText1, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
        parent.AddWindow(self.PouName, 0, border=0, flag=wx.GROW)
        parent.AddWindow(self.staticText2, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
        parent.AddWindow(self.PouType, 0, border=0, flag=wx.GROW)
        parent.AddWindow(self.staticText3, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
        parent.AddWindow(self.Language, 0, border=0, flag=wx.GROW)
        
    def _init_coll_MainSizer_Growables(self, parent):
        parent.AddGrowableCol(1)
        
    def _init_sizers(self):
        self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
        self.MainSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15)

        self._init_coll_flexGridSizer1_Items(self.flexGridSizer1)
        self._init_coll_flexGridSizer1_Growables(self.flexGridSizer1)
        self._init_coll_MainSizer_Items(self.MainSizer)
        self._init_coll_MainSizer_Growables(self.MainSizer)

        self.SetSizer(self.flexGridSizer1)
        
    def _init_ctrls(self, prnt):
        wx.Dialog.__init__(self, id=ID_POUDIALOG,
              name='PouDialog', parent=prnt,
              size=wx.Size(300, 200), style=wx.DEFAULT_DIALOG_STYLE,
              title=_('Create a new POU'))
        self.SetClientSize(wx.Size(300, 200))

        self.staticText1 = wx.StaticText(id=ID_POUDIALOGSTATICTEXT1,
              label=_('POU Name:'), name='staticText1', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.PouName = wx.TextCtrl(id=ID_POUDIALOGPOUNAME,
              name='POUName', parent=self, pos=wx.Point(0, 0), 
              size=wx.Size(0, 24), style=0)

        self.staticText2 = wx.StaticText(id=ID_POUDIALOGSTATICTEXT2,
              label=_('POU Type:'), name='staticText2', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.PouType = wx.ComboBox(id=ID_POUDIALOGPOUTYPE,
              name='POUType', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(0, 28), style=wx.CB_READONLY)
        self.Bind(wx.EVT_COMBOBOX, self.OnTypeChanged, id=ID_POUDIALOGPOUTYPE)

        self.staticText3 = wx.StaticText(id=ID_POUDIALOGSTATICTEXT3,
              label=_('Language:'), name='staticText3', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.Language = wx.ComboBox(id=ID_POUDIALOGLANGUAGE,
              name='Language', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(0, 28), style=wx.CB_READONLY)
        
        self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
        self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
            
        self._init_sizers()

    def __init__(self, parent, pou_type = None):
        self._init_ctrls(parent)
        
        for option in GetPouTypes():
            self.PouType.Append(_(option))
        if pou_type is not None:
            self.PouType.SetStringSelection(_(pou_type))
        self.RefreshLanguage()

        self.PouNames = []
        self.PouElementNames = []

    def OnOK(self, event):
        error = []
        pou_name = self.PouName.GetValue()
        if pou_name == "":
            error.append(_("POU Name"))
        if self.PouType.GetSelection() == -1:
            error.append(_("POU Type"))
        if self.Language.GetSelection() == -1:
            error.append(_("Language"))
        if len(error) > 0:
            text = ""
            for i, item in enumerate(error):
                if i == 0:
                    text += item
                elif i == len(error) - 1:
                    text += _(" and %s")%item
                else:
                    text += _(", %s")%item 
            message = wx.MessageDialog(self, _("Form isn't complete. %s must be filled!")%text, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif not TestIdentifier(pou_name):
            message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%pou_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif pou_name.upper() in IEC_KEYWORDS:
            message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%pou_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif pou_name.upper() in self.PouNames:
            message = wx.MessageDialog(self, _("\"%s\" pou already exists!")%pou_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif pou_name.upper() in self.PouElementNames:
            message = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%pou_name, _("Warning"), wx.YES_NO|wx.ICON_EXCLAMATION)
            result = message.ShowModal()
            message.Destroy()
            if result == wx.ID_YES:
                self.EndModal(wx.ID_OK)
        else:
            self.EndModal(wx.ID_OK)

    def RefreshLanguage(self):
        selection = POU_LANGUAGES_DICT.get(self.Language.GetStringSelection(), "")
        self.Language.Clear()
        for language in GetPouLanguages():
            if language != "SFC" or POU_TYPES_DICT[self.PouType.GetStringSelection()] != "function":
                self.Language.Append(language)
        if self.Language.FindString(_(selection)) != wx.NOT_FOUND:
            self.Language.SetStringSelection(_(selection))

    def OnTypeChanged(self, event):
        self.RefreshLanguage()
        event.Skip()

    def SetPouNames(self, pou_names):
        self.PouNames = [pou_name.upper() for pou_name in pou_names]

    def SetPouElementNames(self, element_names):
        self.PouElementNames = [element_name.upper() for element_name in element_names]

    def SetValues(self, values):
        for item, value in values.items():
            if item == "pouName":
                self.PouName.SetValue(value)
            elif item == "pouType":
                self.PouType.SetStringSelection(_(value))
            elif item == "language":
                self.Language.SetStringSelection(_(POU_LANGUAGES_DICT))
                
    def GetValues(self):
        values = {}
        values["pouName"] = self.PouName.GetValue()
        values["pouType"] = POU_TYPES_DICT[self.PouType.GetStringSelection()]
        values["language"] = POU_LANGUAGES_DICT[self.Language.GetStringSelection()]
        return values


#-------------------------------------------------------------------------------
#                          Create Pou Transition Dialog
#-------------------------------------------------------------------------------

[ID_POUTRANSITIONDIALOG, ID_POUTRANSITIONDIALOGTRANSITIONNAME, 
 ID_POUTRANSITIONDIALOGLANGUAGE, ID_POUTRANSITIONDIALOGSTATICTEXT1, 
 ID_POUTRANSITIONDIALOGSTATICTEXT2,
] = [wx.NewId() for _init_ctrls in range(5)]

def GetTransitionLanguages():
    _ = lambda x : x
    return [_("IL"), _("ST"), _("LD"), _("FBD")]
TRANSITION_LANGUAGES_DICT = dict([(_(language), language) for language in GetTransitionLanguages()])

class PouTransitionDialog(wx.Dialog):
    if wx.VERSION < (2, 6, 0):
        def Bind(self, event, function, id = None):
            if id is not None:
                event(self, id, function)
            else:
                event(self, function)
    
    def _init_coll_flexGridSizer1_Items(self, parent):
        parent.AddSizer(self.MainSizer, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
        parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
        
    def _init_coll_flexGridSizer1_Growables(self, parent):
        parent.AddGrowableCol(0)
        parent.AddGrowableRow(0)
    
    def _init_coll_MainSizer_Items(self, parent):
        parent.AddWindow(self.staticText1, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
        parent.AddWindow(self.TransitionName, 0, border=0, flag=wx.GROW)
        parent.AddWindow(self.staticText2, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
        parent.AddWindow(self.Language, 0, border=0, flag=wx.GROW)
        
    def _init_coll_MainSizer_Growables(self, parent):
        parent.AddGrowableCol(1)
        
    def _init_sizers(self):
        self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
        self.MainSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=15)

        self._init_coll_flexGridSizer1_Items(self.flexGridSizer1)
        self._init_coll_flexGridSizer1_Growables(self.flexGridSizer1)
        self._init_coll_MainSizer_Items(self.MainSizer)
        self._init_coll_MainSizer_Growables(self.MainSizer)

        self.SetSizer(self.flexGridSizer1)

    def _init_ctrls(self, prnt):
        wx.Dialog.__init__(self, id=ID_POUTRANSITIONDIALOG,
              name='PouTransitionDialog', parent=prnt,
              size=wx.Size(350, 200), style=wx.DEFAULT_DIALOG_STYLE,
              title=_('Create a new transition'))
        self.SetClientSize(wx.Size(350, 160))

        self.staticText1 = wx.StaticText(id=ID_POUTRANSITIONDIALOGSTATICTEXT1,
              label=_('Transition Name:'), name='staticText1', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.TransitionName = wx.TextCtrl(id=ID_POUTRANSITIONDIALOGTRANSITIONNAME,
              name='TransitionName', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.staticText2 = wx.StaticText(id=ID_POUTRANSITIONDIALOGSTATICTEXT2,
              label=_('Language:'), name='staticText2', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.Language = wx.ComboBox(id=ID_POUTRANSITIONDIALOGLANGUAGE,
              name='Language', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(0, 28), style=wx.CB_READONLY)
        
        self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
        self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
        
        self._init_sizers()

    def __init__(self, parent):
        self._init_ctrls(parent)
        
        for language in GetTransitionLanguages():
            self.Language.Append(_(language))
            
        self.PouNames = []
        self.PouElementNames = []
        
    def OnOK(self, event):
        error = []
        transition_name = self.TransitionName.GetValue()
        if self.TransitionName.GetValue() == "":
            error.append(_("Transition Name"))
        if self.Language.GetSelection() == -1:
            error.append(_("Language"))
        if len(error) > 0:
            text = ""
            for i, item in enumerate(error):
                if i == 0:
                    text += item
                elif i == len(error) - 1:
                    text += _(" and %s")%item
                else:
                    text += _(", %s")%item 
            message = wx.MessageDialog(self, _("Form isn't complete. %s must be filled!")%text, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif not TestIdentifier(transition_name):
            message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%transition_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif transition_name.upper() in IEC_KEYWORDS:
            message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%transition_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif transition_name.upper() in self.PouNames:
            message = wx.MessageDialog(self, _("A POU named \"%s\" already exists!")%transition_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif transition_name.upper() in self.PouElementNames:
            message = wx.MessageDialog(self, _("\"%s\" element for this pou already exists!")%transition_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        else:
            self.EndModal(wx.ID_OK)
    
    def SetPouNames(self, pou_names):
        self.PouNames = [pou_name.upper() for pou_name in pou_names]
    
    def SetPouElementNames(self, pou_names):
        self.PouElementNames = [pou_name.upper() for pou_name in pou_names]
    
    def SetValues(self, values):
        for item, value in values.items():
            if item == "transitionName":
                self.TransitionName.SetValue(value)
            elif item == "language":
                self.Language.SetSelection(_(value))
                
    def GetValues(self):
        values = {}
        values["transitionName"] = self.TransitionName.GetValue()
        values["language"] = TRANSITION_LANGUAGES_DICT[self.Language.GetStringSelection()]
        return values

#-------------------------------------------------------------------------------
#                          Create Pou Action Dialog
#-------------------------------------------------------------------------------

[ID_POUACTIONDIALOG, ID_POUACTIONDIALOGACTIONNAME, 
 ID_POUACTIONDIALOGLANGUAGE, ID_POUACTIONDIALOGSTATICTEXT1, 
 ID_POUACTIONDIALOGSTATICTEXT2, 
] = [wx.NewId() for _init_ctrls in range(5)]

def GetActionLanguages():
    _ = lambda x : x
    return [_("IL"), _("ST"), _("LD"), _("FBD")]
ACTION_LANGUAGES_DICT = dict([(_(language), language) for language in GetActionLanguages()])

class PouActionDialog(wx.Dialog):
    if wx.VERSION < (2, 6, 0):
        def Bind(self, event, function, id = None):
            if id is not None:
                event(self, id, function)
            else:
                event(self, function)
    
    def _init_coll_flexGridSizer1_Items(self, parent):
        parent.AddSizer(self.MainSizer, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
        parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
        
    def _init_coll_flexGridSizer1_Growables(self, parent):
        parent.AddGrowableCol(0)
        parent.AddGrowableRow(0)
    
    def _init_coll_MainSizer_Items(self, parent):
        parent.AddWindow(self.staticText1, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
        parent.AddWindow(self.ActionName, 0, border=0, flag=wx.GROW)
        parent.AddWindow(self.staticText2, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
        parent.AddWindow(self.Language, 0, border=0, flag=wx.GROW)
        
    def _init_coll_MainSizer_Growables(self, parent):
        parent.AddGrowableCol(1)
        
    def _init_sizers(self):
        self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
        self.MainSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=15)

        self._init_coll_flexGridSizer1_Items(self.flexGridSizer1)
        self._init_coll_flexGridSizer1_Growables(self.flexGridSizer1)
        self._init_coll_MainSizer_Items(self.MainSizer)
        self._init_coll_MainSizer_Growables(self.MainSizer)

        self.SetSizer(self.flexGridSizer1)

    def _init_ctrls(self, prnt):
        wx.Dialog.__init__(self, id=ID_POUACTIONDIALOG,
              name='PouActionDialog', parent=prnt,
              size=wx.Size(320, 200), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,
              title=_('Create a new action'))
        self.SetClientSize(wx.Size(320, 160))

        self.staticText1 = wx.StaticText(id=ID_POUACTIONDIALOGSTATICTEXT1,
              label=_('Action Name:'), name='staticText1', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.ActionName = wx.TextCtrl(id=ID_POUACTIONDIALOGACTIONNAME,
              name='ActionName', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(0, 24), style=0)

        self.staticText2 = wx.StaticText(id=ID_POUACTIONDIALOGSTATICTEXT2,
              label=_('Language:'), name='staticText2', parent=self,
              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)

        self.Language = wx.ComboBox(id=ID_POUACTIONDIALOGLANGUAGE,
              name='Language', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(0, 28), style=wx.CB_READONLY)
        
        self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
        self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
        
        self._init_sizers()

    def __init__(self, parent):
        self._init_ctrls(parent)
        
        for option in GetActionLanguages():
            self.Language.Append(_(option))
        
        self.PouNames = []
        self.PouElementNames = []
    
    def OnOK(self, event):
        error = []
        action_name = self.ActionName.GetValue()
        if action_name == "":
            error.append(_("Action Name"))
        if self.Language.GetSelection() == -1:
            error.append(_("Language"))
        if len(error) > 0:
            text = ""
            for i, item in enumerate(error):
                if i == 0:
                    text += item
                elif i == len(error) - 1:
                    text += _(" and %s")%item
                else:
                    text += _(", %s")%item 
            message = wx.MessageDialog(self, _("Form isn't complete. %s must be filled!")%text, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif not TestIdentifier(action_name):
            message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%action_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif action_name.upper() in IEC_KEYWORDS:
            message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%action_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif action_name.upper() in self.PouNames:
            message = wx.MessageDialog(self, _("A POU named \"%s\" already exists!")%action_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif action_name.upper() in self.PouElementNames:
            message = wx.MessageDialog(self, _("\"%s\" element for this pou already exists!")%action_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        else:
            self.EndModal(wx.ID_OK)
    
    def SetPouNames(self, pou_names):
        self.PouNames = [pou_name.upper() for pou_name in pou_names]
        
    def SetPouElementNames(self, element_names):
        self.PouElementNames = [element_name.upper() for element_name in element_names]
        
    def SetValues(self, values):
        for item, value in values.items():
            if item == "actionName":
                self.ActionName.SetValue(value)
            elif item == "language":
                self.Language.SetStringSelection(_(value))
                
    def GetValues(self):
        values = {}
        values["actionName"] = self.ActionName.GetValue()
        values["language"] = ACTION_LANGUAGES_DICT[self.Language.GetStringSelection()]
        return values

#-------------------------------------------------------------------------------
#                          Configuration Name Dialog
#-------------------------------------------------------------------------------

class ConfigurationNameDialog(wx.TextEntryDialog):

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

    def __init__(self, parent, message, caption = _("Please enter configuration name"), defaultValue = "", 
                       style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
        wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
        
        self.PouNames = []
        self.PouElementNames = []
        
        if wx.VERSION >= (2, 8, 0):
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton().GetId())
        elif wx.VERSION >= (2, 6, 0):
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
        else:
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
    
    def OnOK(self, event):
        config_name = self.GetSizer().GetItem(1).GetWindow().GetValue()
        if config_name == "":
            message = wx.MessageDialog(self, _("You must type a name!"), _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif not TestIdentifier(config_name):
            message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%config_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif config_name.upper() in IEC_KEYWORDS:
            message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%config_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif config_name.upper() in self.PouNames:
            message = wx.MessageDialog(self, _("A POU named \"%s\" already exists!")%config_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif config_name.upper() in self.PouElementNames:
            message = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%config_name, _("Warning"), wx.YES_NO|wx.ICON_EXCLAMATION)
            result = message.ShowModal()
            message.Destroy()
            if result == wx.ID_YES:
                self.EndModal(wx.ID_OK)
        else:
            self.EndModal(wx.ID_OK)

    def SetPouNames(self, pou_names):
        self.PouNames = [pou_name.upper() for pou_name in pou_names]
    
    def SetPouElementNames(self, pou_names):
        self.PouElementNames = [pou_name.upper() for pou_name in pou_names]
    
    def GetValue(self):
        return self.GetSizer().GetItem(1).GetWindow().GetValue()

#-------------------------------------------------------------------------------
#                          Resource Name Dialog
#-------------------------------------------------------------------------------

class ResourceNameDialog(wx.TextEntryDialog):

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

    def __init__(self, parent, message, caption = _("Please enter resource name"), defaultValue = "", 
                       style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
        wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
        
        self.PouNames = []
        self.PouElementNames = []
        
        if wx.VERSION >= (2, 8, 0):
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton().GetId())
        elif wx.VERSION >= (2, 6, 0):
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
        else:
            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())

    def OnOK(self, event):
        resource_name = self.GetSizer().GetItem(1).GetWindow().GetValue()
        if resource_name == "":
            message = wx.MessageDialog(self, _("You must type a name!"), _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif not TestIdentifier(resource_name):
            message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%resource_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif resource_name.upper() in IEC_KEYWORDS:
            message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%resource_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif resource_name.upper() in self.PouNames:
            message = wx.MessageDialog(self, _("A POU named \"%s\" already exists!")%resource_name, _("Error"), wx.OK|wx.ICON_ERROR)
            message.ShowModal()
            message.Destroy()
        elif resource_name.upper() in self.PouElementNames:
            message = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%resource_name, _("Warning"), wx.YES_NO|wx.ICON_EXCLAMATION)
            result = message.ShowModal()
            message.Destroy()
            if result == wx.ID_YES:
                self.EndModal(wx.ID_OK)
        else:
            self.EndModal(wx.ID_OK)

    def SetPouNames(self, pou_names):
        self.PouNames = [pou_name.upper() for pou_name in pou_names]
    
    def SetPouElementNames(self, pou_names):
        self.PouElementNames = [pou_name.upper() for pou_name in pou_names]
    
    def GetValue(self):
        return self.GetSizer().GetItem(1).GetWindow().GetValue()

#-------------------------------------------------------------------------------
#                            Debug Variables Panel
#-------------------------------------------------------------------------------

def GetDebugVariablesTableColnames():
    _ = lambda x : x
    return [_("Variable"), _("Value")]

class VariableTableItem(DebugDataConsumer):
    
    def __init__(self, parent, variable, value):
        DebugDataConsumer.__init__(self)
        self.Parent = parent
        self.Variable = variable
        self.Value = value
    
    def __del__(self):
        self.Parent = None
    
    def SetVariable(self, variable):
        if self.Parent and self.Variable != variable:
            self.Variable = variable
            self.Parent.RefreshGrid()
    
    def GetVariable(self):
        return self.Variable
    
    def SetForced(self, forced):
        if self.Forced != forced:
            self.Forced = forced
            self.Parent.HasNewData = True
    
    def SetValue(self, value):
        if self.Value != value:
            self.Value = value
            self.Parent.HasNewData = True
            
    def GetValue(self):
        return self.Value

class DebugVariableTable(CustomTable):
    
    def GetValue(self, row, col):
        if row < self.GetNumberRows():
            return self.GetValueByName(row, self.GetColLabelValue(col, False))
        return ""
    
    def SetValue(self, row, col, value):
        if col < len(self.colnames):
            self.SetValueByName(row, self.GetColLabelValue(col, False), value)
            
    def GetValueByName(self, row, colname):
        if row < self.GetNumberRows():
            if colname == "Variable":
                return self.data[row].GetVariable()
            elif colname == "Value":
                return self.data[row].GetValue()
        return ""

    def SetValueByName(self, row, colname, value):
        if row < self.GetNumberRows():
            if colname == "Variable":
                self.data[row].SetVariable(value)
            elif colname == "Value":
                self.data[row].SetValue(value)
    
    def IsForced(self, row):
        if row < self.GetNumberRows():
            return self.data[row].IsForced()
        return False
    
    def _updateColAttrs(self, grid):
        """
        wx.grid.Grid -> update the column attributes to add the
        appropriate renderer given the column name.

        Otherwise default to the default renderer.
        """
        
        for row in range(self.GetNumberRows()):
            for col in range(self.GetNumberCols()):
                if self.GetColLabelValue(col, False) == "Value":
                    if self.IsForced(row):
                        grid.SetCellTextColour(row, col, wx.BLUE)
                    else:
                        grid.SetCellTextColour(row, col, wx.BLACK)
                grid.SetReadOnly(row, col, True)
            self.ResizeRow(grid, row)
                
    def AppendItem(self, data):
        self.data.append(data)
    
    def InsertItem(self, idx, data):
        self.data.insert(idx, data)
    
    def RemoveItem(self, idx):
        self.data.pop(idx)
    
    def MoveItem(self, idx, new_idx):
        self.data.insert(new_idx, self.data.pop(idx))
        
    def GetItem(self, idx):
        return self.data[idx]


class DebugVariableDropTarget(wx.TextDropTarget):
    
    def __init__(self, parent):
        wx.TextDropTarget.__init__(self)
        self.ParentWindow = parent
    
    def OnDropText(self, x, y, data):
        x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
        row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
        if row == wx.NOT_FOUND:
            row = self.ParentWindow.Table.GetNumberRows()
        message = None
        try:
            values = eval(data)
        except:
            message = _("Invalid value \"%s\" for debug variable")%data
            values = None
        if not isinstance(values, TupleType):
            message = _("Invalid value \"%s\" for debug variable")%data
            values = None
        if values is not None and values[1] == "debug":
            self.ParentWindow.InsertValue(values[0], row)
        if message is not None:
            wx.CallAfter(self.ShowMessage, message)
            
    def ShowMessage(self, message):
        message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
        message.ShowModal()
        message.Destroy()

[ID_DEBUGVARIABLEPANEL, ID_DEBUGVARIABLEPANELVARIABLESGRID, 
 ID_DEBUGVARIABLEPANELUPBUTTON, ID_DEBUGVARIABLEPANELDOWNBUTTON, 
 ID_DEBUGVARIABLEPANELDELETEBUTTON,
] = [wx.NewId() for _init_ctrls in range(5)]

class DebugVariablePanel(wx.Panel, DebugViewer):
    
    if wx.VERSION < (2, 6, 0):
        def Bind(self, event, function, id = None):
            if id is not None:
                event(self, id, function)
            else:
                event(self, function)
    
    def _init_coll_MainSizer_Items(self, parent):
        parent.AddSizer(self.ButtonPanelSizer, 0, border=5, flag=wx.ALIGN_RIGHT|wx.ALL)
        parent.AddWindow(self.VariablesGrid, 0, border=0, flag=wx.GROW)
    
    def _init_coll_MainSizer_Growables(self, parent):
        parent.AddGrowableCol(0)
        parent.AddGrowableRow(1)
    
    def _init_coll_ButtonPanelSizer_Items(self, parent):
        parent.AddWindow(self.UpButton, 0, border=5, flag=wx.RIGHT)
        parent.AddWindow(self.DownButton, 0, border=5, flag=wx.RIGHT)
        parent.AddWindow(self.DeleteButton, 0, border=0, flag=0)
        
    def _init_sizers(self):
        self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0)
        self.ButtonPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
        
        self._init_coll_MainSizer_Items(self.MainSizer)
        self._init_coll_MainSizer_Growables(self.MainSizer)
        self._init_coll_ButtonPanelSizer_Items(self.ButtonPanelSizer)
        
        self.SetSizer(self.MainSizer)
        
    def _init_ctrls(self, prnt):
        wx.Panel.__init__(self, id=ID_DEBUGVARIABLEPANEL,
              name='DebugVariablePanel', parent=prnt, pos=wx.Point(0, 0),
              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)

        self.VariablesGrid = CustomGrid(id=ID_DEBUGVARIABLEPANELVARIABLESGRID,
              name='VariablesGrid', parent=self, pos=wx.Point(0, 0), 
              size=wx.Size(0, 150), style=wx.VSCROLL)
        self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self))
        if wx.VERSION >= (2, 6, 0):
            self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.OnVariablesGridCellRightClick)
        else:
            wx.grid.EVT_GRID_CELL_RIGHT_CLICK(self.VariablesGrid, self.OnVariablesGridCellRightClick)
        
        self.UpButton = wx.Button(id=ID_DEBUGVARIABLEPANELUPBUTTON, label='^',
              name='UpButton', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(32, 32), style=0)
        
        self.DownButton = wx.Button(id=ID_DEBUGVARIABLEPANELDOWNBUTTON, label='v',
              name='DownButton', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(32, 32), style=0)
        
        self.DeleteButton = wx.Button(id=ID_DEBUGVARIABLEPANELDELETEBUTTON, label=_('Delete'),
              name='DeleteButton', parent=self, pos=wx.Point(0, 0),
              size=wx.DefaultSize, style=0)
        
        self._init_sizers()
    
    def __init__(self, parent, producer):
        self._init_ctrls(parent)
        DebugViewer.__init__(self, producer, True)
        self.HasNewData = False
        
        self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
        self.VariablesGrid.SetTable(self.Table)
        self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
                                       "Up": self.UpButton,
                                       "Down": self.DownButton})
        
        def _AddVariable(new_row):
            return self.VariablesGrid.GetGridCursorRow()
        setattr(self.VariablesGrid, "_AddRow", _AddVariable)
        
        def _DeleteVariable(row):
            item = self.Table.GetItem(row)
            self.RemoveDataConsumer(item)
            self.Table.RemoveItem(row)
            self.RefreshGrid()
        setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
        
        def _MoveVariable(row, move):
            new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
            if new_row != row:
                self.Table.MoveItem(row, new_row)
                self.RefreshGrid()
            return new_row
        setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
        
        self.VariablesGrid.SetRowLabelSize(0)
        
        for col in range(self.Table.GetNumberCols()):
            attr = wx.grid.GridCellAttr()
            attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
            self.VariablesGrid.SetColAttr(col, attr)
            self.VariablesGrid.SetColSize(col, 100)
        
        self.Table.ResetView(self.VariablesGrid)
        self.VariablesGrid.RefreshButtons()
    
    def RefreshNewData(self):
        if self.HasNewData:
            self.HasNewData = False
            self.RefreshGrid()
        DebugViewer.RefreshNewData(self)
    
    def RefreshGrid(self):
        self.Freeze()
        self.Table.ResetView(self.VariablesGrid)
        self.VariablesGrid.RefreshButtons()
        self.Thaw()
    
    def ResetGrid(self):
        self.DeleteDataConsumers()
        self.Table.Empty()
        self.Freeze()
        self.Table.ResetView(self.VariablesGrid)
        self.VariablesGrid.RefreshButtons()
        self.Thaw()
    
    def GetForceVariableMenuFunction(self, iec_path, item):
        iec_type = self.GetDataType(iec_path)
        def ForceVariableFunction(event):
            if iec_type is not None:
                dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
                if dialog.ShowModal() == wx.ID_OK:
                    self.ForceDataValue(iec_path, dialog.GetValue())
        return ForceVariableFunction

    def GetReleaseVariableMenuFunction(self, iec_path):
        def ReleaseVariableFunction(event):
            self.ReleaseDataValue(iec_path)
        return ReleaseVariableFunction
    
    def OnVariablesGridCellRightClick(self, event):
        row, col = event.GetRow(), event.GetCol()
        if self.Table.GetColLabelValue(col, False) == "Value":
            iec_path = self.Table.GetValueByName(row, "Variable").upper()

            menu = wx.Menu(title='')
            new_id = wx.NewId()
            AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Force value"))
            self.Bind(wx.EVT_MENU, self.GetForceVariableMenuFunction(iec_path.upper(), self.Table.GetItem(row)), id=new_id)
            new_id = wx.NewId()
            AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Release value"))
            self.Bind(wx.EVT_MENU, self.GetReleaseVariableMenuFunction(iec_path.upper()), id=new_id)
            if self.Table.IsForced(row):
                menu.Enable(new_id, True)
            else:
                menu.Enable(new_id, False)
            self.PopupMenu(menu)
        event.Skip()
    
    def InsertValue(self, iec_path, idx = None):
        if idx is None:
            idx = self.Table.GetNumberRows()
        for item in self.Table.GetData():
            if iec_path == item.GetVariable():
                return
        item = VariableTableItem(self, iec_path, "")
        result = self.AddDataConsumer(iec_path.upper(), item)
        if result is not None:
            self.Table.InsertItem(idx, item)
            self.RefreshGrid()

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

UPPER_DIV = lambda x, y: (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.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()
        ppiScreenX, ppiScreenY = self.GetPPIScreen()
        pw, ph = self.GetPageSizePixels()
        dw, dh = dc.GetSizeTuple()
        Xscale = (float(dw) * float(ppiPrinterX)) / (float(pw) * 25.4)
        Yscale = (float(dh) * float(ppiPrinterY)) / (float(ph) * 25.4)
        
        fontsize = self.FontSize * Yscale
        text_margin = self.TextMargin * 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 = float(area_width) / float(self.PageSize[0])
        scaleY = float(area_height) / float(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

#-------------------------------------------------------------------------------
#                               Exception Handler
#-------------------------------------------------------------------------------

Max_Traceback_List_Size = 20

def Display_Exception_Dialog(e_type,e_value,e_tb):
    trcbck_lst = []
    for i,line in enumerate(traceback.extract_tb(e_tb)):
        trcbck = " " + str(i+1) + _(". ")
        if line[0].find(os.getcwd()) == -1:
            trcbck += _("file : ") + str(line[0]) + _(",   ")
        else:
            trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(",   ")
        trcbck += _("line : ") + str(line[1]) + _(",   ") + _("function : ") + str(line[2])
        trcbck_lst.append(trcbck)
        
    # Allow clicking....
    cap = wx.Window_GetCapture()
    if cap:
        cap.ReleaseMouse()

    dlg = wx.SingleChoiceDialog(None, 
        _("""
An error has occurred.

Click OK to save an error report.

Please be kind enough to send this file to:
edouard.tisserant@gmail.com

Error:
""") +
        str(e_type) + _(" : ") + str(e_value), 
        _("Error"),
        trcbck_lst)
    try:
        res = (dlg.ShowModal() == wx.ID_OK)
    finally:
        dlg.Destroy()

    return res

def Display_Error_Dialog(e_value):
    message = wx.MessageDialog(None, str(e_value), _("Error"), wx.OK|wx.ICON_ERROR)
    message.ShowModal()
    message.Destroy()

def get_last_traceback(tb):
    while tb.tb_next:
        tb = tb.tb_next
    return tb


def format_namespace(d, indent='    '):
    return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])


ignored_exceptions = [] # a problem with a line in a module is only reported once per session

def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
    
    def handle_exception(e_type, e_value, e_traceback):
        traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
        last_tb = get_last_traceback(e_traceback)
        ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
        if str(e_value).startswith("!!!"):
            Display_Error_Dialog(e_value)
        elif ex not in ignored_exceptions:
            result = Display_Exception_Dialog(e_type,e_value,e_traceback)
            if result:
                ignored_exceptions.append(ex)
                info = {
                    'app-title' : wx.GetApp().GetAppName(), # app_title
                    'app-version' : app_version,
                    'wx-version' : wx.VERSION_STRING,
                    'wx-platform' : wx.Platform,
                    'python-version' : platform.python_version(), #sys.version.split()[0],
                    'platform' : platform.platform(),
                    'e-type' : e_type,
                    'e-value' : e_value,
                    'date' : time.ctime(),
                    'cwd' : os.getcwd(),
                    }
                if e_traceback:
                    info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
                    last_tb = get_last_traceback(e_traceback)
                    exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
                    info['locals'] = format_namespace(exception_locals)
                    if 'self' in exception_locals:
                        info['self'] = format_namespace(exception_locals['self'].__dict__)
                
                output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w')
                lst = info.keys()
                lst.sort()
                for a in lst:
                    output.write(a+":\n"+str(info[a])+"\n\n")

    #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
    sys.excepthook = handle_exception

if __name__ == '__main__':
    wx.InitAllImageHandlers()
    
    # Install a exception handle for bug reports
    AddExceptHook(os.getcwd(),__version__)
    
    frame = PLCOpenEditor(None, fileOpen=fileOpen)

    frame.Show()
    app.MainLoop()