IDEFrame.py
author Laurent Bessard
Sun, 29 Sep 2013 20:17:05 +0200
changeset 1334 b0c2c4e1c1f1
parent 1296 5f8e02717560
child 1362 077bcba2d485
permissions -rw-r--r--
Added support for loading PLCOpen v1 files and modify their content to be compatible with PLCOpen v2

import os, sys
import cPickle
from types import TupleType

import wx, wx.grid
import wx.aui

try:
    import matplotlib
    matplotlib.use('WX')
    USE_MPL = True
except:
    USE_MPL = False

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

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

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

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

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

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



# 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
EditorToolBarItems = {
    "FBD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
              "move", _("Move the view")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
              "add_comment", _("Create a new comment")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
              "add_variable", _("Create a new variable")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
              "add_block", _("Create a new block")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", 
              "add_connection", _("Create a new connection"))],
    "LD"  : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
              "move", _("Move the view")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", 
              "add_comment", _("Create a new comment")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", 
              "add_powerrail", _("Create a new power rail")),
             (False, DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool", 
              "add_rung", _("Create a new rung")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool", 
              "add_coil", _("Create a new coil")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", 
              "add_contact", _("Create a new contact")),
             (False, DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool", 
              "add_branch", _("Create a new branch")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", 
              "add_variable", _("Create a new variable")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", 
              "add_block", _("Create a new block")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", 
              "add_connection", _("Create a new connection"))],
    "SFC" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
              "move", _("Move the view")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", 
              "add_comment", _("Create a new comment")),
             (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool", 
              "add_initial_step", _("Create a new initial step")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool", 
              "add_step", _("Create a new step")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool", 
              "add_transition", _("Create a new transition")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool", 
              "add_action", _("Create a new action block")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool", 
              "add_divergence", _("Create a new divergence")),
             (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool", 
              "add_jump", _("Create a new jump")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", 
              "add_variable", _("Create a new variable")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", 
              "add_block", _("Create a new block")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", 
              "add_connection", _("Create a new connection")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", 
              "add_powerrail", _("Create a new power rail")),
             (True, FREEDRAWING_MODE, 
              ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", 
              "add_contact", _("Create a new contact"))],
    "ST"  : [],
    "IL"  : [],
    "debug": [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
              ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
              "move", _("Move the view"))],
}

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

import base64

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

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

# 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, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, 
 POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES
] = range(10)

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

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

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

def SimplifyTabLayout(tabs, rect):
    for tab in tabs:
        if tab["pos"][0] == rect.x:
            others = [t for t in tabs if t != tab]
            others.sort(lambda x,y: cmp(x["pos"][0], y["pos"][0]))
            for other in others:
                if (other["pos"][1] == tab["pos"][1] and 
                    other["size"][1] == tab["size"][1] and
                    other["pos"][0] == tab["pos"][0] + tab["size"][0] + TAB_BORDER):
                    
                    tab["size"] = (tab["size"][0] + other["size"][0] + TAB_BORDER, tab["size"][1])
                    tab["pages"].extend(other["pages"])
                    tabs.remove(other)
                    
                    if tab["size"][0] == rect.width:
                        return True
                    
        elif tab["pos"][1] == rect.y:
            others = [t for t in tabs if t != tab]
            others.sort(lambda x,y: cmp(x["pos"][1], y["pos"][1]))
            for other in others:
                if (other["pos"][0] == tab["pos"][0] and 
                    other["size"][0] == tab["size"][0] and
                    other["pos"][1] == tab["pos"][1] + tab["size"][1] + TAB_BORDER):
                    
                    tab["size"] = (tab["size"][0], tab["size"][1] + other["size"][1] + TAB_BORDER)
                    tab["pages"].extend(other["pages"])
                    tabs.remove(other)
                    
                    if tab["size"][1] == rect.height:
                        return True
    return False
    
def ComputeTabsLayout(tabs, rect):
    if len(tabs) == 0:
        return tabs
    if len(tabs) == 1:
        return tabs[0]
    split = None
    for idx, tab in enumerate(tabs):
        if len(tab["pages"]) == 0:
            raise ValueError, "Not possible"
        if tab["size"][0] == rect.width:
            if tab["pos"][1] == rect.y:
                split = (wx.TOP, float(tab["size"][1]) / float(rect.height))
                split_rect = wx.Rect(rect.x, rect.y + tab["size"][1] + TAB_BORDER, 
                                     rect.width, rect.height - tab["size"][1] - TAB_BORDER)
            elif tab["pos"][1] == rect.height + 1 - tab["size"][1]:
                split = (wx.BOTTOM, 1.0 - float(tab["size"][1]) / float(rect.height))
                split_rect = wx.Rect(rect.x, rect.y, 
                                     rect.width, rect.height - tab["size"][1] - TAB_BORDER)
            break
        elif tab["size"][1] == rect.height:
            if tab["pos"][0] == rect.x:
                split = (wx.LEFT, float(tab["size"][0]) / float(rect.width))
                split_rect = wx.Rect(rect.x + tab["size"][0] + TAB_BORDER, rect.y, 
                                     rect.width - tab["size"][0] - TAB_BORDER, rect.height)
            elif tab["pos"][0] == rect.width + 1 - tab["size"][0]:
                split = (wx.RIGHT, 1.0 - float(tab["size"][0]) / float(rect.width))
                split_rect = wx.Rect(rect.x, rect.y, 
                                     rect.width - tab["size"][0] - TAB_BORDER, rect.height)
            break
    if split != None:
        split_tab = tabs.pop(idx)
        return {"split": split,
                "tab": split_tab, 
                "others": ComputeTabsLayout(tabs, split_rect)}
    else:
        if SimplifyTabLayout(tabs, rect):
            return ComputeTabsLayout(tabs, rect)
    return tabs

#-------------------------------------------------------------------------------
#                              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_AddMenu_Items(self, parent, add_config=True):
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE,
              kind=wx.ITEM_NORMAL, text=_(u'&Data Type'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTION,
              kind=wx.ITEM_NORMAL, text=_(u'&Function'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK,
              kind=wx.ITEM_NORMAL, text=_(u'Function &Block'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDPROGRAM,
              kind=wx.ITEM_NORMAL, text=_(u'&Program'))
        if add_config:
            AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
                  kind=wx.ITEM_NORMAL, text=_(u'&Configuration'))
    
    def _init_coll_EditMenu_Items(self, parent):
        AppendMenu(parent, help='', id=wx.ID_UNDO,
              kind=wx.ITEM_NORMAL, text=_(u'Undo') + '\tCTRL+Z')
        AppendMenu(parent, help='', id=wx.ID_REDO,
              kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y')
        #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=wx.ID_FIND,
              kind=wx.ITEM_NORMAL, text=_(u'Find') + '\tCTRL+F')
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDNEXT,
              kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K')
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
              kind=wx.ITEM_NORMAL, text=_(u'Find Previous') + '\tCTRL+SHIFT+K')
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT,
              kind=wx.ITEM_NORMAL, text=_(u'Search in Project') + '\tCTRL+SHIFT+F')
        parent.AppendSeparator()
        add_menu = wx.Menu(title='')
        self._init_coll_AddMenu_Items(add_menu)
        parent.AppendMenu(wx.ID_ADD, _(u"&Add Element"), add_menu)
        AppendMenu(parent, help='', id=wx.ID_SELECTALL,
              kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A')
        AppendMenu(parent, help='', id=wx.ID_DELETE,
              kind=wx.ITEM_NORMAL, text=_(u'&Delete'))
        self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO)
        self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO)
        #self.Bind(wx.EVT_MENU, self.OnEnableUndoRedoMenu, id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO)
        self.Bind(wx.EVT_MENU, self.OnCutMenu, id=wx.ID_CUT)
        self.Bind(wx.EVT_MENU, self.OnCopyMenu, id=wx.ID_COPY)
        self.Bind(wx.EVT_MENU, self.OnPasteMenu, id=wx.ID_PASTE)
        self.Bind(wx.EVT_MENU, self.OnFindMenu, id=wx.ID_FIND)
        self.Bind(wx.EVT_MENU, self.OnFindNextMenu, 
              id=ID_PLCOPENEDITOREDITMENUFINDNEXT)
        self.Bind(wx.EVT_MENU, self.OnFindPreviousMenu, 
              id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS)
        self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, 
              id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT)
        self.Bind(wx.EVT_MENU, self.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)
        
        self.AddToMenuToolBar([(wx.ID_UNDO, "undo", _(u'Undo'), None),
                               (wx.ID_REDO, "redo", _(u'Redo'), None),
                               None,
                               (wx.ID_CUT, "cut", _(u'Cut'), None),
                               (wx.ID_COPY, "copy", _(u'Copy'), None),
                               (wx.ID_PASTE, "paste", _(u'Paste'), None),
                               None,
                               (ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, "find", _(u'Search in Project'), None)])

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

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

        self.FileMenu = wx.Menu(title='')
        self.EditMenu = wx.Menu(title='')
        self.DisplayMenu = wx.Menu(title='')
        self.HelpMenu = wx.Menu(title='')
        
        self._init_coll_MenuBar_Menus(self.MenuBar)
        self._init_coll_FileMenu_Items(self.FileMenu)
        self._init_coll_EditMenu_Items(self.EditMenu)
        self._init_coll_DisplayMenu_Items(self.DisplayMenu)
        self._init_coll_HelpMenu_Items(self.HelpMenu)

    def _init_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.SetClientSize(wx.Size(1000, 600))
        self.Bind(wx.EVT_ACTIVATE, self.OnActivated)
        
        self.TabsImageList = wx.ImageList(31, 16)
        self.TabsImageListIndexes = {}
        
        #-----------------------------------------------------------------------
        #                          Creating main structure
        #-----------------------------------------------------------------------
        
        self.AUIManager = wx.aui.AuiManager(self)
        self.AUIManager.SetDockSizeConstraint(0.5, 0.5)
        self.Panes = {}
        
        self.LeftNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORLEFTNOTEBOOK,
              style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE|
                    wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)
        self.LeftNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, 
                self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(self.LeftNoteBook, 
              wx.aui.AuiPaneInfo().Name("ProjectPane").
              Left().Layer(1).
              BestSize(wx.Size(300, 500)).CloseButton(False))
        
        self.BottomNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORBOTTOMNOTEBOOK,
              style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE|
                    wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)
        self.BottomNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, 
                self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(self.BottomNoteBook, 
              wx.aui.AuiPaneInfo().Name("ResultPane").
              Bottom().Layer(0).
              BestSize(wx.Size(800, 300)).CloseButton(False))
        
        self.RightNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORRIGHTNOTEBOOK,
              style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE|
                    wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)
        self.RightNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, 
                self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(self.RightNoteBook, 
              wx.aui.AuiPaneInfo().Name("LibraryPane").
              Right().Layer(0).
              BestSize(wx.Size(250, 400)).CloseButton(False))
        
        self.TabsOpened = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORTABSOPENED, 
              style=wx.aui.AUI_NB_DEFAULT_STYLE|wx.aui.AUI_NB_WINDOWLIST_BUTTON)
        self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGING,
              self.OnPouSelectedChanging)
        self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED,
              self.OnPouSelectedChanged)
        self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE,
              self.OnPageClose)
        self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG,
              self.OnPageDragged)
        self.AUIManager.AddPane(self.TabsOpened, 
              wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane"))
        
        #-----------------------------------------------------------------------
        #                    Creating PLCopen Project Types Tree
        #-----------------------------------------------------------------------
        
        self.MainTabs = {}
        
        self.ProjectPanel = wx.SplitterWindow(id=ID_PLCOPENEDITORPROJECTPANEL,
                  name='ProjectPanel', parent=self.LeftNoteBook, point=wx.Point(0, 0),
                  size=wx.Size(0, 0), style=wx.SP_3D)
        
        self.ProjectTree = CustomTree(id=ID_PLCOPENEDITORPROJECTTREE,
                  name='ProjectTree', parent=self.ProjectPanel, 
                  pos=wx.Point(0, 0), size=wx.Size(0, 0),
                  style=wx.SUNKEN_BORDER,
                  agwStyle=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.TR_EDIT_LABELS)
        self.ProjectTree.SetBackgroundBitmap(GetBitmap("custom_tree_background"),
                                             wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM)
        add_menu = wx.Menu()
        self._init_coll_AddMenu_Items(add_menu)
        self.ProjectTree.SetAddMenu(add_menu)
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnProjectTreeRightUp,
              id=ID_PLCOPENEDITORPROJECTTREE)
        self.ProjectTree.Bind(wx.EVT_LEFT_UP, self.OnProjectTreeLeftUp)
        self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnProjectTreeItemChanging,
              id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnProjectTreeBeginDrag,
              id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnProjectTreeItemBeginEdit,
              id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnProjectTreeItemEndEdit,
              id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnProjectTreeItemActivated,
              id=ID_PLCOPENEDITORPROJECTTREE)
        self.ProjectTree.Bind(wx.EVT_MOTION, self.OnProjectTreeMotion)
        
        #-----------------------------------------------------------------------
        #        Creating PLCopen Project POU Instance Variables Panel
        #-----------------------------------------------------------------------
        
        self.PouInstanceVariablesPanel = PouInstanceVariablesPanel(self.ProjectPanel, self, self.Controler, self.EnableDebug)
        
        self.MainTabs["ProjectPanel"] = (self.ProjectPanel, _("Project"))
        self.LeftNoteBook.AddPage(*self.MainTabs["ProjectPanel"])
        
        self.ProjectPanel.SplitHorizontally(self.ProjectTree, self.PouInstanceVariablesPanel, 300)
        
        #-----------------------------------------------------------------------
        #                            Creating Tool Bar
        #-----------------------------------------------------------------------
        
        MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR, wx.DefaultPosition, wx.DefaultSize,
                wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
        MenuToolBar.SetToolBitmapSize(wx.Size(25, 25))
        MenuToolBar.Realize()
        self.Panes["MenuToolBar"] = MenuToolBar
        self.AUIManager.AddPane(MenuToolBar, wx.aui.AuiPaneInfo().
                  Name("MenuToolBar").Caption(_("Menu ToolBar")).
                  ToolbarPane().Top().
                  LeftDockable(False).RightDockable(False))
        
        EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR, wx.DefaultPosition, wx.DefaultSize,
                wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
        EditorToolBar.SetToolBitmapSize(wx.Size(25, 25))
        EditorToolBar.AddRadioTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, 
              GetBitmap("select"), wx.NullBitmap, _("Select an object"))
        EditorToolBar.Realize()
        self.Panes["EditorToolBar"] = EditorToolBar
        self.AUIManager.AddPane(EditorToolBar, wx.aui.AuiPaneInfo().
                  Name("EditorToolBar").Caption(_("Editor ToolBar")).
                  ToolbarPane().Top().Position(1).
                  LeftDockable(False).RightDockable(False))
            
        self.Bind(wx.EVT_MENU, self.OnSelectionTool, 
              id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION)
        
        #-----------------------------------------------------------------------
        #                            Creating Search Panel
        #-----------------------------------------------------------------------
        
        self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self)
        self.MainTabs["SearchResultPanel"] = (self.SearchResultPanel, _("Search"))
        self.BottomNoteBook.AddPage(*self.MainTabs["SearchResultPanel"])
        
        #-----------------------------------------------------------------------
        #                            Creating Library Panel
        #-----------------------------------------------------------------------
        
        self.LibraryPanel = LibraryPanel(self, True)
        self.MainTabs["LibraryPanel"] = (self.LibraryPanel, _("Library"))
        self.RightNoteBook.AddPage(*self.MainTabs["LibraryPanel"])
                
        self._init_utils()
        self.SetMenuBar(self.MenuBar)
                
        if self.EnableDebug:
            self.DebugVariablePanel = DebugVariablePanel(self.RightNoteBook, self.Controler, self)
            self.MainTabs["DebugVariablePanel"] = (self.DebugVariablePanel, _("Debugger"))
            self.RightNoteBook.AddPage(*self.MainTabs["DebugVariablePanel"])
        
        self.AUIManager.Update()
    
        self.FindDialog = FindInPouDialog(self)
        self.FindDialog.Hide()
    
    ## 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(GetBitmap(language))
            
        # Icons for other items
        for imgname, itemtype in [
            #editables
            ("PROJECT",        ITEM_PROJECT),
            #("POU",            ITEM_POU),
            #("VARIABLE",       ITEM_VARIABLE),
            ("TRANSITION",     ITEM_TRANSITION),
            ("ACTION",         ITEM_ACTION),
            ("CONFIGURATION",  ITEM_CONFIGURATION),
            ("RESOURCE",       ITEM_RESOURCE),
            ("DATATYPE",       ITEM_DATATYPE),
            # uneditables
            ("DATATYPES",      ITEM_DATATYPES),
            ("FUNCTION",       ITEM_FUNCTION),
            ("FUNCTIONBLOCK",  ITEM_FUNCTIONBLOCK),
            ("PROGRAM",        ITEM_PROGRAM),
            ("VAR_LOCAL",      ITEM_VAR_LOCAL),
            ("VAR_LOCAL",      ITEM_VAR_GLOBAL),
            ("VAR_LOCAL",      ITEM_VAR_EXTERNAL),
            ("VAR_LOCAL",      ITEM_VAR_TEMP),
            ("VAR_INPUT",      ITEM_VAR_INPUT),
            ("VAR_OUTPUT",     ITEM_VAR_OUTPUT),
            ("VAR_INOUT",      ITEM_VAR_INOUT),
            ("TRANSITIONS",    ITEM_TRANSITIONS),
            ("ACTIONS",        ITEM_ACTIONS),
            ("CONFIGURATIONS", ITEM_CONFIGURATIONS),
            ("RESOURCES",      ITEM_RESOURCES),
            ("PROPERTIES",     ITEM_PROPERTIES)]:
            self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))
        
        # Assign icon list to TreeCtrls
        self.ProjectTree.SetImageList(self.TreeImageList)
        self.PouInstanceVariablesPanel.SetTreeImageList(self.TreeImageList)
        
        self.CurrentEditorToolBar = []
        self.CurrentMenu = None
        self.SelectedItem = None
        self.LastToolTipItem = None
        self.SearchParams = None
        self.Highlights = {}
        self.DrawingMode = FREEDRAWING_MODE
        #self.DrawingMode = DRIVENDRAWING_MODE
        self.AuiTabCtrl = []
        
        # Save default perspective
        notebooks = {}
        for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
                                     (self.BottomNoteBook, "bottomnotebook"),
                                     (self.RightNoteBook, "rightnotebook")]:
            notebooks[entry_name] = self.SaveTabLayout(notebook)
        self.DefaultPerspective = {
            "perspective": self.AUIManager.SavePerspective(),
            "notebooks": notebooks,
        }
        
        
        # Initialize Printing configuring elements
        self.PrintData = wx.PrintData()
        self.PrintData.SetPaperId(wx.PAPER_A4)
        self.PrintData.SetPrintMode(wx.PRINT_MODE_PRINTER)
        self.PageSetupData = wx.PageSetupDialogData(self.PrintData)
        self.PageSetupData.SetMarginTopLeft(wx.Point(10, 15))
        self.PageSetupData.SetMarginBottomRight(wx.Point(10, 20))
        
        self.SetRefreshFunctions()
        self.SetDeleteFunctions()
    
    def __del__(self):
        self.FindDialog.Destroy()
    
    def Show(self):
        wx.Frame.Show(self)
        wx.CallAfter(self.RestoreLastState)
    
    def OnActivated(self, event):
        if event.GetActive():
            wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        event.Skip()

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

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

    def GetTabInfos(self, tab):
        for page_name, (page_ref, page_title) in self.MainTabs.iteritems():
            if page_ref == tab:
                return ("main", page_name)
        return None
    
    def SaveTabLayout(self, notebook):
        tabs = []
        for child in notebook.GetChildren():
            if isinstance(child, wx.aui.AuiTabCtrl):
                if child.GetPageCount() > 0:
                    pos = child.GetPosition()
                    tab = {"pos": (pos.x, pos.y), "pages": []}
                    tab_size = child.GetSize()
                    for page_idx in xrange(child.GetPageCount()):
                        page = child.GetWindowFromIdx(page_idx)
                        if not tab.has_key("size"):
                            tab["size"] = (tab_size[0], tab_size[1] + page.GetSize()[1])
                        tab_infos = self.GetTabInfos(page)
                        if tab_infos is not None:
                            tab["pages"].append((tab_infos, page_idx == child.GetActivePage()))
                    tabs.append(tab)
        tabs.sort(lambda x, y: cmp(x["pos"], y["pos"]))
        size = notebook.GetSize()
        return ComputeTabsLayout(tabs, wx.Rect(1, 1, size[0] - NOTEBOOK_BORDER, size[1] - NOTEBOOK_BORDER))
    
    def LoadTab(self, notebook, page_infos):
        if page_infos[0] == "main":
            infos = self.MainTabs.get(page_infos[1])
            if infos is not None:
                page_ref, page_title = infos
                notebook.AddPage(page_ref, page_title)
                return notebook.GetPageIndex(page_ref)
        elif page_infos[0] == "editor":
            tagname = page_infos[1]
            page_ref = self.EditProjectElement(self.Controler.GetElementType(tagname), tagname)
            if page_ref is not None:
                page_ref.RefreshView()
                return notebook.GetPageIndex(page_ref)
        elif page_infos[0] == "debug":
            instance_path = page_infos[1]
            instance_infos = self.Controler.GetInstanceInfos(instance_path, self.EnableDebug)
            if instance_infos is not None:
                return notebook.GetPageIndex(self.OpenDebugViewer(instance_infos["class"], instance_path, instance_infos["type"]))
        return None
            
    def LoadTabLayout(self, notebook, tabs, mode="all", first_index=None):
        if isinstance(tabs, ListType):
            if len(tabs) == 0:
                return
            raise ValueError, "Not supported"
        
        if tabs.has_key("split"):
            self.LoadTabLayout(notebook, tabs["others"])
            
            split_dir, split_ratio = tabs["split"]
            first_index = self.LoadTabLayout(notebook, tabs["tab"], mode="first")
            notebook.Split(first_index, split_dir)
            self.LoadTabLayout(notebook, tabs["tab"], mode="others", first_index=first_index)
            
        elif mode == "first":
            return self.LoadTab(notebook, tabs["pages"][0][0])
        else:
            selected = first_index
            if mode == "others":
                add_tabs = tabs["pages"][1:]
            else:
                add_tabs = tabs["pages"]
            for page_infos, page_selected in add_tabs:
                page_idx = self.LoadTab(notebook, page_infos)
                if page_selected:
                    selected = page_idx
            if selected is not None:
                wx.CallAfter(notebook.SetSelection, selected)
    
    def ResetPerspective(self):
        if self.DefaultPerspective is not None:
            self.AUIManager.LoadPerspective(self.DefaultPerspective["perspective"])
        
            for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]:
                for idx in xrange(notebook.GetPageCount()):
                    notebook.RemovePage(0)
                        
            notebooks = self.DefaultPerspective["notebooks"]
            for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
                                         (self.BottomNoteBook, "bottomnotebook"),
                                         (self.RightNoteBook, "rightnotebook")]:
                self.LoadTabLayout(notebook, notebooks.get(entry_name))
    
            self._Refresh(EDITORTOOLBAR)
    
    def RestoreLastState(self):
        frame_size = None
        if self.Config.HasEntry("framesize"):
            frame_size = cPickle.loads(str(self.Config.Read("framesize")))
        
        if frame_size is None:
            self.Maximize()
        else:
            self.SetClientSize(frame_size)
        
    def SaveLastState(self):
        if not self.IsMaximized():
            self.Config.Write("framesize", cPickle.dumps(self.GetClientSize()))
        elif self.Config.HasEntry("framesize"):
            self.Config.DeleteEntry("framesize")
        
        self.Config.Flush()

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

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

    ## Call PLCOpenEditor refresh functions.
    #  @param elements List of elements to refresh.
    def _Refresh(self, *elements):
        try:
            for element in elements:
                self.RefreshFunctions[element]()
        except wx.PyDeadObjectError:
            # ignore exceptions caused by refresh while quitting
            pass

    ## Callback function when AUINotebook Page closed with CloseButton
    #  @param event AUINotebook Event.
    def OnPageClose(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected > -1:
            window = self.TabsOpened.GetPage(selected)
            
            if window.CheckSaveBeforeClosing():
                
                # Refresh all window elements that have changed
                wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
                wx.CallAfter(self.RefreshTabCtrlEvent)
                wx.CallAfter(self.CloseFindInPouDialog)
                event.Skip()
            else:
                event.Veto()
        

    def GetCopyBuffer(self, primary_selection=False):
        data = None
        if primary_selection and wx.Platform == '__WXMSW__':
            return data
        else:
            wx.TheClipboard.UsePrimarySelection(primary_selection)
        if wx.TheClipboard.Open():
            dataobj = wx.TextDataObject()
            if wx.TheClipboard.GetData(dataobj):
                data = dataobj.GetText()
            wx.TheClipboard.Close()
        return data
        
    def SetCopyBuffer(self, text, primary_selection=False):
        if primary_selection and wx.Platform == '__WXMSW__':
            return
        else:
            wx.TheClipboard.UsePrimarySelection(primary_selection)
        if wx.TheClipboard.Open():
            data = wx.TextDataObject()
            data.SetText(text)
            wx.TheClipboard.SetData(data)
            wx.TheClipboard.Flush()
            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 EditProjectSettings(self):
        old_values = self.Controler.GetProjectProperties()
        dialog = ProjectDialog(self)
        dialog.SetValues(old_values)
        if dialog.ShowModal() == wx.ID_OK:
            new_values = dialog.GetValues()
            new_values["creationDateTime"] = old_values["creationDateTime"]
            if new_values != old_values:
                self.Controler.SetProjectProperties(None, new_values)
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, 
                              PROJECTTREE, POUINSTANCEVARIABLESPANEL, SCALING)
        dialog.Destroy()

#-------------------------------------------------------------------------------
#                            Notebook Unified Functions
#-------------------------------------------------------------------------------
    
    ## 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):
        for idx in xrange(self.TabsOpened.GetPageCount()):
            self.TabsOpened.DeletePage(0)
        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):
        return self.TabsOpened.SetPageBitmap(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
        
        for idx in xrange(self.TabsOpened.GetPageCount()):
            window = self.TabsOpened.GetPage(idx)
            if not window.CheckSaveBeforeClosing():
                return False
        
        return True

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

    def RefreshFileMenu(self):
        pass

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

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

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

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

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

                preview_frame.Initialize()
                
                preview_canvas = preview.GetCanvas()
                preview_canvas.SetMinSize(preview_canvas.GetVirtualSize())
                preview_frame.Fit()
                
                preview_frame.Show(True)

    def OnPrintMenu(self, event):
        selected = self.TabsOpened.GetSelection()        
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            dialog_data = wx.PrintDialogData(self.PrintData)
            dialog_data.SetToPage(1)
            properties = self.Controler.GetProjectProperties(window.IsDebugging())
            page_size = map(int, properties["pageSize"])
            margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight())
            printer = wx.Printer(dialog_data)
            printout = GraphicPrintout(window, page_size, margins)
            
            if not printer.Print(self, printout, True) and printer.GetLastError() != wx.PRINTER_CANCELLED:
                self.ShowErrorMessage(_("There was a problem printing.\nPerhaps your current printer is not set correctly?"))
            printout.Destroy()

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

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

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

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

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

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

    def OnSelectAllMenu(self, event):
        control = self.FindFocus()
        if control is not None and control.GetName() == "Viewer":
            control.Parent.SelectAll()
        elif isinstance(control, wx.stc.StyledTextCtrl):
            control.SelectAll()
        elif isinstance(control, wx.TextCtrl):
            control.SetSelection(0, control.GetLastPosition())
        elif isinstance(control, wx.ComboBox):
            control.SetMark(0, control.GetLastPosition() + 1)
    
    def SetDeleteFunctions(self):
        self.DeleteFunctions = {
            ITEM_DATATYPE: GetDeleteElementFunction(
                    PLCControler.ProjectRemoveDataType, 
                    check_function=self.CheckDataTypeIsUsedBeforeDeletion),
            ITEM_POU: GetDeleteElementFunction(
                    PLCControler.ProjectRemovePou, 
                    check_function=self.CheckPouIsUsedBeforeDeletion),
            ITEM_TRANSITION: GetDeleteElementFunction(
                    PLCControler.ProjectRemovePouTransition, ITEM_POU),
            ITEM_ACTION: GetDeleteElementFunction(
                    PLCControler.ProjectRemovePouAction, ITEM_POU),
            ITEM_CONFIGURATION: GetDeleteElementFunction(
                    PLCControler.ProjectRemoveConfiguration),
            ITEM_RESOURCE: GetDeleteElementFunction(
                    PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION)
        }
    
    def OnDeleteMenu(self, event):
        window = self.FindFocus()
        if window == self.ProjectTree or window is None:
            selected = self.ProjectTree.GetSelection()
            if selected is not None and selected.IsOk():
                function = self.DeleteFunctions.get(self.ProjectTree.GetPyData(selected)["type"], None)
                if function is not None:
                    function(self, selected)
                    self.CloseTabsWithoutModel()
                    self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, 
                                  POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
        elif isinstance(window, (Viewer, TextViewer)):
            event = wx.KeyEvent(wx.EVT_CHAR._getEvtType())
            event.m_keyCode = wx.WXK_DELETE
            window.ProcessEvent(event)

    def OnFindMenu(self, event):
        if not self.FindDialog.IsShown():
            self.FindDialog.Show()
    
    def CloseFindInPouDialog(self):
        selected = self.TabsOpened.GetSelection()
        if selected == -1 and self.FindDialog.IsShown():
            self.FindDialog.Hide()
    
    def OnFindNextMenu(self, event):
        self.FindInPou(1)
    
    def OnFindPreviousMenu(self, event):
        self.FindInPou(-1)
    
    def FindInPou(self, direction, search_params=None):
        if search_params is not None:
            self.SearchParams = search_params
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.Find(direction, self.SearchParams)
    
    def OnSearchInProjectMenu(self, event):
        dialog = SearchInProjectDialog(self)
        if dialog.ShowModal() == wx.ID_OK:
            criteria = dialog.GetCriteria()
            result = self.Controler.SearchInProject(criteria)
            self.ClearSearchResults()
            self.SearchResultPanel.SetSearchResults(criteria, result)
            self.SelectTab(self.SearchResultPanel)
            
#-------------------------------------------------------------------------------
#                             Display Menu Functions
#-------------------------------------------------------------------------------

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

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

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

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

#-------------------------------------------------------------------------------
#                      Project Editor Panels Management Functions
#-------------------------------------------------------------------------------
    
    def OnPageDragged(self, event):
        wx.CallAfter(self.RefreshTabCtrlEvent)
        event.Skip()
    
    def OnAllowNotebookDnD(self, event):
        event.Allow()
    
    def RefreshTabCtrlEvent(self):
        auitabctrl = []
        for child in self.TabsOpened.GetChildren():
            if isinstance(child, wx.aui.AuiTabCtrl):
                auitabctrl.append(child)
                if child not in self.AuiTabCtrl:
                    child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child))
        self.AuiTabCtrl = auitabctrl
        if self.TabsOpened.GetPageCount() == 0:
            pane = self.AUIManager.GetPane(self.TabsOpened)
            if pane.IsMaximized():
                self.AUIManager.RestorePane(pane)
            self.AUIManager.Update()
    
    def EnsureTabVisible(self, tab):
        notebook = tab.GetParent()
        notebook.SetSelection(notebook.GetPageIndex(tab))
    
    def OnPouSelectedChanging(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected >= 0:
            window = self.TabsOpened.GetPage(selected)
            if not window.IsDebugging():
                window.ResetBuffer()
        event.Skip()
    
    def OnPouSelectedChanged(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected >= 0:
            window = self.TabsOpened.GetPage(selected)
            tagname = window.GetTagName()
            if not window.IsDebugging():
                self.SelectProjectTreeItem(tagname)
                self.PouInstanceVariablesPanel.SetPouType(tagname)
                window.RefreshView()
                self.EnsureTabVisible(self.LibraryPanel)
            else:
                instance_path = window.GetInstancePath()
                if tagname == "":
                    instance_path = instance_path.rsplit(".", 1)[0]
                    tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug)
                self.EnsureTabVisible(self.DebugVariablePanel)
                wx.CallAfter(self.PouInstanceVariablesPanel.SetPouType, tagname, instance_path)
        wx.CallAfter(self._Refresh, FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
        event.Skip()
        
    def RefreshEditor(self):
        selected = self.TabsOpened.GetSelection()
        if selected >= 0:
            window = self.TabsOpened.GetPage(selected)
            tagname = window.GetTagName()
            if not window.IsDebugging():
                self.SelectProjectTreeItem(tagname)
                self.PouInstanceVariablesPanel.SetPouType(tagname)
            else:
                instance_path = window.GetInstancePath()
                if tagname == "":
                    instance_path = instance_path.rsplit(".", 1)[0]
                    tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug)
                self.PouInstanceVariablesPanel.SetPouType(tagname, instance_path)
            for child in self.TabsOpened.GetChildren():
                if isinstance(child, wx.aui.AuiTabCtrl):
                    active_page = child.GetActivePage()
                    if active_page >= 0:
                        window = child.GetWindowFromIdx(active_page)
                        window.RefreshView()
            self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
    
    def RefreshEditorNames(self, old_tagname, new_tagname):
        for i in xrange(self.TabsOpened.GetPageCount()):
            editor = self.TabsOpened.GetPage(i)
            if editor.GetTagName() == old_tagname:
                editor.SetTagName(new_tagname)
    
    def IsOpened(self, tagname):
        for idx in xrange(self.TabsOpened.GetPageCount()):
            if self.TabsOpened.GetPage(idx).IsViewing(tagname):
                return idx
        return None

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

    def GetTabsOpenedDClickFunction(self, tabctrl):
        def OnTabsOpenedDClick(event):
            pos = event.GetPosition()
            if tabctrl.TabHitTest(pos.x, pos.y, None):
                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 RefreshProjectTree(self):
        # Extract current selected item tagname
        selected = self.ProjectTree.GetSelection()
        if selected is not None and selected.IsOk():
            item_infos = self.ProjectTree.GetPyData(selected)
            tagname = item_infos.get("tagname", None)
        else:
            tagname = None
        
        # Refresh treectrl items according to project infos
        infos = self.Controler.GetProjectInfos()
        root = self.ProjectTree.GetRootItem()
        if root is None or not root.IsOk():
            root = self.ProjectTree.AddRoot(infos["name"])
        self.GenerateProjectTreeBranch(root, infos)
        self.ProjectTree.Expand(root)
        
        # Select new item corresponding to previous selected item
        if tagname is not None:
            self.SelectProjectTreeItem(tagname)

    def GenerateProjectTreeBranch(self, root, infos, item_alone=False):
        to_delete = []
        item_name = infos["name"]
        if infos["type"] in ITEMS_UNEDITABLE:
            if len(infos["values"]) == 1:
                return self.GenerateProjectTreeBranch(root, infos["values"][0], True)
            item_name = _(item_name)
        self.ProjectTree.SetItemText(root, item_name)
        self.ProjectTree.SetPyData(root, infos)
        highlight_colours = self.Highlights.get(infos.get("tagname", None), (wx.WHITE, wx.BLACK))
        self.ProjectTree.SetItemBackgroundColour(root, highlight_colours[0])
        self.ProjectTree.SetItemTextColour(root, highlight_colours[1])
        self.ProjectTree.SetItemExtraImage(root, None)
        if infos["type"] == ITEM_POU:
            self.ProjectTree.SetItemImage(root, 
                self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])])
            if item_alone:
                self.ProjectTree.SetItemExtraImage(root, self.Controler.GetPouType(infos["name"]))
        elif infos.has_key("icon") and infos["icon"] is not None:
            icon_name = infos["icon"]
            if not self.TreeImageDict.has_key(icon_name):
                self.TreeImageDict[icon_name] = self.TreeImageList.Add(GetBitmap(icon_name))
            self.ProjectTree.SetItemImage(root, self.TreeImageDict[icon_name])
        elif self.TreeImageDict.has_key(infos["type"]):
            self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]])      
        
        item, root_cookie = self.ProjectTree.GetFirstChild(root)
        for values in infos["values"]:
            if values["type"] not in ITEMS_UNEDITABLE or len(values["values"]) > 0:
                if item is None or not item.IsOk():
                    item = self.ProjectTree.AppendItem(root, "")
                    item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
                self.GenerateProjectTreeBranch(item, values)
                item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
        while item is not None and item.IsOk():
            to_delete.append(item)
            item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
        for item in to_delete:
            self.ProjectTree.Delete(item)

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

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

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

    def ResetSelectedItem(self):
        self.SelectedItem = None

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

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

    def OnProjectTreeItemEndEdit(self, event):
        message = None
        abort = False
        new_name = event.GetLabel()
        if new_name != "":
            if not TestIdentifier(new_name):
                message = _("\"%s\" is not a valid identifier!")%new_name
            elif new_name.upper() in IEC_KEYWORDS:
                message = _("\"%s\" is a keyword. It can't be used!")%new_name
            else:
                item = event.GetItem()
                old_name = self.ProjectTree.GetItemText(item)
                item_infos = self.ProjectTree.GetPyData(item)
                if item_infos["type"] == ITEM_PROJECT:
                    self.Controler.SetProjectProperties(name = new_name)
                elif item_infos["type"] == ITEM_DATATYPE:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectDataTypeNames() if name != old_name]:
                        message = _("\"%s\" data type already exists!")%new_name
                        abort = True
                    if not abort:
                        self.Controler.ChangeDataTypeName(old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputeDataTypeName(old_name), 
                                                self.Controler.ComputeDataTypeName(new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_POU:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames() if name != old_name]:
                        message = _("\"%s\" pou already exists!")%new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        self.Controler.ChangePouName(old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputePouName(old_name), 
                                                self.Controler.ComputePouName(new_name))
                        self.RefreshLibraryPanel()
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_TRANSITION:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        message = _("A POU named \"%s\" already exists!")%new_name
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]:
                        message = _("A variable with \"%s\" as name already exists in this pou!")%new_name
                    else:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangePouTransitionName(words[1], old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputePouTransitionName(words[1], old_name), 
                                                self.Controler.ComputePouTransitionName(words[1], new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_ACTION:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        message = _("A POU named \"%s\" already exists!")%new_name
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]:
                        message = _("A variable with \"%s\" as name already exists in this pou!")%new_name
                    else:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangePouActionName(words[1], old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputePouActionName(words[1], old_name), 
                                                self.Controler.ComputePouActionName(words[1], new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_CONFIGURATION:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames() if name != old_name]:
                        message = _("\"%s\" config already exists!")%new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        self.Controler.ChangeConfigurationName(old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputeConfigurationName(old_name), 
                                                self.Controler.ComputeConfigurationName(new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_RESOURCE:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames()]:
                        message = _("\"%s\" config already exists!")%new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangeConfigurationResourceName(words[1], old_name, new_name)
                        self.RefreshEditorNames(self.Controler.ComputeConfigurationResourceName(words[1], old_name), 
                                                self.Controler.ComputeConfigurationResourceName(words[1], new_name))
                        self.RefreshPageTitles()
            if message or abort:
                if message:
                    self.ShowErrorMessage(message)
                item = event.GetItem()
                wx.CallAfter(self.ProjectTree.EditLabel, item)
                event.Veto()
            else:
                wx.CallAfter(self.RefreshProjectTree)
                self.RefreshEditor()
                self._Refresh(TITLE, FILEMENU, EDITMENU)
                event.Skip()
    
    def OnProjectTreeItemActivated(self, event):
        selected = event.GetItem()
        name = self.ProjectTree.GetItemText(selected)
        item_infos = self.ProjectTree.GetPyData(selected)
        if item_infos["type"] == ITEM_PROJECT:
            self.EditProjectSettings()
        else:
            if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU,
                                    ITEM_CONFIGURATION, ITEM_RESOURCE,
                                    ITEM_TRANSITION, ITEM_ACTION]:
                self.EditProjectElement(item_infos["type"], item_infos["tagname"])
            event.Skip()
    
    def ProjectTreeItemSelect(self, select_item):
        if select_item is not None and select_item.IsOk():
            name = self.ProjectTree.GetItemText(select_item)
            item_infos = self.ProjectTree.GetPyData(select_item)
            if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU,
                                      ITEM_CONFIGURATION, ITEM_RESOURCE,
                                      ITEM_TRANSITION, ITEM_ACTION]:
                self.EditProjectElement(item_infos["type"], item_infos["tagname"], True)
                self.PouInstanceVariablesPanel.SetPouType(item_infos["tagname"])
        
    def OnProjectTreeLeftUp(self, event):
        if self.SelectedItem is not None:
            self.ProjectTree.SelectItem(self.SelectedItem)
            self.ProjectTreeItemSelect(self.SelectedItem)
            self.ResetSelectedItem()
        event.Skip()
    
    def OnProjectTreeMotion(self, event):
        if not event.Dragging():
            pt = wx.Point(event.GetX(), event.GetY())
            item, flags = self.ProjectTree.HitTest(pt)
            if item is not None and item.IsOk() and flags & wx.TREE_HITTEST_ONITEMLABEL:
                item_infos = self.ProjectTree.GetPyData(item)
                if item != self.LastToolTipItem and self.LastToolTipItem is not None:
                    self.ProjectTree.SetToolTip(None)
                    self.LastToolTipItem = None
                if (self.LastToolTipItem != item and 
                    item_infos["type"] in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]):
                    bodytype = self.Controler.GetEditedElementBodyType(
                            item_infos["tagname"])
                    if item_infos["type"] == ITEM_POU:
                        block_type = {
                            "program": _("Program"),
                            "functionBlock": _("Function Block"),
                            "function": _("Function")
                        }[self.Controler.GetPouType(item_infos["name"])]
                    elif item_infos["type"] == ITEM_TRANSITION:
                        block_type = "Transition"
                    else:
                        block_type = "Action"
                    self.LastToolTipItem = item
                    wx.CallAfter(self.ProjectTree.SetToolTipString, 
                        "%s : %s : %s" % (
                            block_type, bodytype, item_infos["name"]))
            elif self.LastToolTipItem is not None:
                self.ProjectTree.SetToolTip(None)
                self.LastToolTipItem = None
        event.Skip()
    
    def OnProjectTreeItemChanging(self, event):
        if self.ProjectTree.GetPyData(event.GetItem())["type"] not in ITEMS_UNEDITABLE and self.SelectedItem is None:
            self.SelectedItem = event.GetItem()
            event.Veto()
        else:
            event.Skip()
    
    def EditProjectElement(self, element, tagname, onlyopened = False):
        openedidx = self.IsOpened(tagname)
        if openedidx is not None:
            old_selected = self.TabsOpened.GetSelection()
            if old_selected != openedidx:
                if old_selected >= 0:
                    self.TabsOpened.GetPage(old_selected).ResetBuffer()
                self.TabsOpened.SetSelection(openedidx)
            self._Refresh(FILEMENU, EDITMENU, EDITORTOOLBAR, PAGETITLES)
        elif not onlyopened:
            if isinstance(element, EditorPanel):
                new_window = element
                self.AddPage(element, "")
            elif self.Controler.GetEditedElement(tagname) is not None:
                new_window = None
                if element == ITEM_CONFIGURATION:
                    new_window = ConfigurationEditor(self.TabsOpened, tagname, self, self.Controler)
                    new_window.SetIcon(GetBitmap("CONFIGURATION"))
                    self.AddPage(new_window, "")
                elif element == ITEM_RESOURCE:
                    new_window = ResourceEditor(self.TabsOpened, tagname, self, self.Controler)
                    new_window.SetIcon(GetBitmap("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 = GetBitmap(pou_type, bodytype)
                    elif element == ITEM_TRANSITION:
                        icon = GetBitmap("TRANSITION", bodytype)
                    elif element == ITEM_ACTION:
                        icon = GetBitmap("ACTION", bodytype)
                    new_window.SetIcon(icon)
                    self.AddPage(new_window, "")
                elif element == ITEM_DATATYPE:
                    new_window = DataTypeEditor(self.TabsOpened, tagname, self, self.Controler)
                    new_window.SetIcon(GetBitmap("DATATYPE"))
                    self.AddPage(new_window, "")
            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()
            return new_window
    
    def OnProjectTreeRightUp(self, event):
        item = event.GetItem()
        self.ProjectTree.SelectItem(item)
        self.ProjectTreeItemSelect(item)
        name = self.ProjectTree.GetItemText(item)
        item_infos = self.ProjectTree.GetPyData(item)
        
        menu = None
        if item_infos["type"] in ITEMS_UNEDITABLE + [ITEM_PROJECT]:
            if item_infos["type"] == ITEM_PROJECT:
                name = "Project"
            else:
                name = UNEDITABLE_NAMES_DICT[name]
            
            if name == "Data Types":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add DataType"))
                self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, id=new_id)
            
            elif name in ["Functions", "Function Blocks", "Programs", "Project"]:
                menu = wx.Menu(title='')
                
                if name != "Project":
                    new_id = wx.NewId()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add POU"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction({"Functions" : "function", "Function Blocks" : "functionBlock", "Programs" : "program"}[name]), id=new_id)

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

            elif name == "Configurations":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Configuration"))
                self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu, id=new_id)
            
            elif name == "Transitions":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Transition"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetPyData(parent)["type"]
                while parent_type != ITEM_POU:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetPyData(parent)["type"]
                self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(self.ProjectTree.GetItemText(parent)), id=new_id)
            
            elif name == "Actions":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetPyData(parent)["type"]
                while parent_type != ITEM_POU:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetPyData(parent)["type"]
                self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(self.ProjectTree.GetItemText(parent)), id=new_id)
            
            elif name == "Resources":
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetPyData(parent)["type"]
                while parent_type not in [ITEM_CONFIGURATION, ITEM_PROJECT]:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetPyData(parent)["type"]
                parent_name = None
                if parent_type == ITEM_PROJECT:
                    config_names = self.Controler.GetProjectConfigNames()
                    if len(config_names) > 0:
                        parent_name = config_names[0]
                else:
                    parent_name = self.ProjectTree.GetItemText(parent)
                if parent_name is not None:
                    self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(parent_name), id=new_id)
            
        else:
            if item_infos["type"] == ITEM_POU:
                menu = wx.Menu(title='')
                if self.Controler.GetPouBodyType(name) == "SFC":
                    new_id = wx.NewId()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Transition"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(name), id=new_id)
                    new_id = wx.NewId()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(name), id=new_id)
                    menu.AppendSeparator()
    
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Copy POU"))
                self.Bind(wx.EVT_MENU, self.OnCopyPou, id=new_id)
    
                pou_type = self.Controler.GetPouType(name)
                if pou_type in ["function", "functionBlock"]:
                    change_menu = wx.Menu(title='')
                    if pou_type == "function":
                        new_id = wx.NewId()
                        AppendMenu(change_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Function Block"))
                        self.Bind(wx.EVT_MENU, self.GenerateChangePouTypeFunction(name, "functionBlock"), id=new_id)
                    new_id = wx.NewId()
                    AppendMenu(change_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Program"))
                    self.Bind(wx.EVT_MENU, self.GenerateChangePouTypeFunction(name, "program"), id=new_id)
                    menu.AppendMenu(wx.NewId(), _("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)
            
            elif item_infos["type"] == ITEM_CONFIGURATION:
                menu = wx.Menu(title='')
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource"))
                self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(name), id=new_id)
            
            elif item_infos["type"] in [ITEM_DATATYPE, ITEM_TRANSITION, ITEM_ACTION, ITEM_RESOURCE]:
                menu = wx.Menu(title='')
                
            if menu is not None:
                new_id = wx.NewId()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete"))
                self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id)
        
        if menu is not None:
            self.PopupMenu(menu)
            menu.Destroy()
        
        self.ResetSelectedItem()
        
        event.Skip()


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

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

    def OpenDebugViewer(self, instance_category, instance_path, instance_type):
        openedidx = self.IsOpened(instance_path)
        new_window = None
        if openedidx is not None:
            old_selected = self.TabsOpened.GetSelection()
            if old_selected != openedidx:
                if old_selected >= 0:
                    self.TabsOpened.GetPage(old_selected).ResetBuffer()
                self.TabsOpened.SetSelection(openedidx)
        
        elif instance_category in ITEMS_VARIABLE:
            if self.Controler.IsNumType(instance_type, True):
                if USE_MPL:
                    self.AddDebugVariable(instance_path, True)
                else:
                    new_window = GraphicViewer(self.TabsOpened, self, self.Controler, instance_path)
                    icon = GetBitmap("GRAPH")
        
        else:
            bodytype = self.Controler.GetEditedElementBodyType(instance_type, True)
            if bodytype == "FBD":
                new_window = Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            elif bodytype == "LD":
                new_window = LD_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            elif bodytype == "SFC":
                new_window = SFC_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            else:
                new_window = TextViewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.SetTextSyntax(bodytype)
                if bodytype == "IL":
                    new_window.SetKeywords(IL_KEYWORDS)
                else:
                    new_window.SetKeywords(ST_KEYWORDS)
            
            if new_window is not None:
                if instance_category in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]:
                    pou_type = self.Controler.GetEditedElementType(instance_type, True)[1].upper()
                    icon = GetBitmap(pou_type, bodytype)
                elif instance_category == ITEM_TRANSITION:
                    icon = GetBitmap("TRANSITION", bodytype)
                elif instance_category == ITEM_ACTION:
                    icon = GetBitmap("ACTION", bodytype)
        
        if new_window is not None:
            new_window.SetIcon(icon)
            self.AddPage(new_window, "")
            new_window.RefreshView()
            new_window.SetFocus()
            self.RefreshPageTitles()
        return new_window

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

    def CloseObsoleteDebugTabs(self):
        if self.EnableDebug:
            idxs = range(self.TabsOpened.GetPageCount())
            idxs.reverse()
            for idx in idxs:
                editor = self.TabsOpened.GetPage(idx)
                if isinstance(editor, (Viewer, GraphicViewer)) and editor.IsDebugging():
                    instance_infos = self.Controler.GetInstanceInfos(editor.GetInstancePath(), self.EnableDebug)
                    if instance_infos is None:
                        self.TabsOpened.DeletePage(idx)
                    elif isinstance(editor, GraphicViewer):
                        editor.ResetView(True)
                    else:
                        editor.SubscribeAllDataConsumers()
                elif editor.IsDebugging():
                    editor.SubscribeAllDataConsumers()
            self.DebugVariablePanel.SubscribeAllDataConsumers()
    
    def AddDebugVariable(self, iec_path, force=False, graph=False):
        if self.EnableDebug:
            self.DebugVariablePanel.InsertValue(iec_path, force=force, graph=graph)
            self.EnsureTabVisible(self.DebugVariablePanel)
            
#-------------------------------------------------------------------------------
#                         Library Panel Management Function
#-------------------------------------------------------------------------------

    def RefreshLibraryPanel(self):
        self.LibraryPanel.RefreshTree()
        
#-------------------------------------------------------------------------------
#                          ToolBars Management Functions
#-------------------------------------------------------------------------------

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

    def ResetEditorToolBar(self):
        EditorToolBar = self.Panes["EditorToolBar"]
        
        for item in self.CurrentEditorToolBar:
            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 EditorToolBar:
                EditorToolBar.DeleteTool(item)
        
        if EditorToolBar:
            EditorToolBar.Realize()
            self.AUIManager.GetPane("EditorToolBar").BestSize(EditorToolBar.GetBestSize())
            self.AUIManager.GetPane("EditorToolBar").Hide()
            self.AUIManager.Update()

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


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

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

    def OnSelectionTool(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_SELECTION)
    
    def OnMotionTool(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_MOTION)
    
    def OnCommentTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOMMENT)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_COMMENT)
    
    def OnVariableTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARVARIABLE)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_VARIABLE)
    
    def OnBlockTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARBLOCK)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_BLOCK)
        
    def OnConnectionTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONNECTION)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_CONNECTION)
    
    def OnPowerRailTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_POWERRAIL)
    
    def OnRungTool(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).AddLadderRung()
        event.Skip()
    
    def OnCoilTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOIL)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_COIL)
        event.Skip()
    
    def OnContactTool(self, event):
        if self.DrawingMode == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONTACT)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.DrawingMode == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_CONTACT)
            else:
                self.TabsOpened.GetPage(selected).AddLadderContact()
    
    def OnBranchTool(self, event): 
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).AddLadderBranch()
    
    def OnInitialStepTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_INITIALSTEP)
    
    def OnStepTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARSTEP)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_STEP)
            else:
                self.TabsOpened.GetPage(selected).AddStep()
    
    def OnActionBlockTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_ACTION)
            else:
                self.TabsOpened.GetPage(selected).AddStepAction()
    
    def OnTransitionTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARTRANSITION)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            self.TabsOpened.GetPage(selected).SetMode(MODE_TRANSITION)
    
    def OnDivergenceTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_DIVERGENCE)
            else:
                self.TabsOpened.GetPage(selected).AddDivergence()
    
    def OnJumpTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARJUMP)
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                self.TabsOpened.GetPage(selected).SetMode(MODE_JUMP)
            else:
                self.TabsOpened.GetPage(selected).AddJump()
    

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

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

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

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

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

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

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

    def OnCopyPou(self, event):
        selected = self.ProjectTree.GetSelection()
        pou_name = self.ProjectTree.GetItemText(selected)
        
        pou_xml = self.Controler.GetPouXml(pou_name)
        if pou_xml is not None:
            self.SetCopyBuffer(pou_xml)
            self._Refresh(EDITMENU)

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

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

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

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

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

    def CheckDataTypeIsUsedBeforeDeletion(self, name):
        return self.CheckElementIsUsedBeforeDeletion(
            self.Controler.DataTypeIsUsed,
            _("Remove Datatype"), name)
    
    def CheckPouIsUsedBeforeDeletion(self, name):
        return self.CheckElementIsUsedBeforeDeletion(
            self.Controler.PouIsUsed,
            _("Remove Pou"), name)
    
    def OnRemoveDataTypeMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_DATATYPE:
            name = self.ProjectTree.GetItemText(selected)
            if self.CheckDataTypeIsUsedBeforeDeletion(name):
                self.Controler.ProjectRemoveDataType(name)
                tagname = self.Controler.ComputeDataTypeName(name)
                idx = self.IsOpened(tagname)
                if idx is not None:
                    self.TabsOpened.DeletePage(idx)
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE)
            
    def OnRenamePouMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: 
            wx.CallAfter(self.ProjectTree.EditLabel, selected)

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

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

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

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

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

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

    def ShowHighlight(self, infos, start, end, highlight_type):
        self.SelectProjectTreeItem(infos[0])
        if infos[1] == "name":
            self.Highlights[infos[0]] = highlight_type
            self.RefreshProjectTree()
            self.ProjectTree.Unselect()
        else:
            self.EditProjectElement(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.RefreshProjectTree()
        for i in xrange(self.TabsOpened.GetPageCount()):
            viewer = self.TabsOpened.GetPage(i)
            viewer.ClearHighlights(highlight_type)

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

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

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

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