capocchi/DEVSimPy

View on GitHub
Editor.py

Summary

Maintainability
F
1 wk
Test Coverage
# -*- coding: utf-8 -*-

## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
# Editor.py ---
#                     --------------------------------
#                            Copyright (c) 2020
#                    L. CAPOCCHI (capocchi@univ-corse.fr) & T. Ville
#                SPE Lab - SISU Group - University of Corsica
#                     --------------------------------
# Version 1.0                                        last modified: 03/15/2020
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
#
# GENERAL NOTES AND REMARKS:
#
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##

## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
#
# GLOBAL VARIABLES AND FUNCTIONS
#
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##

import wx
import os
import sys
import keyword
import zipfile
import threading
import re
import codecs
import tabnanny
import builtins
import types
import traceback
import collections

import inspect
if not hasattr(inspect, 'getargspec'):
    inspect.getargspec = inspect.getfullargspec
    
from tempfile import gettempdir
from wx import stc

from Decorators import redirectStdout
from Utilities import path_to_module, printOnStatusBar

import ReloadModule
import ZipManager

import gettext
_ = gettext.gettext

# Turn on verbose mode
tabnanny.verbose = 1

if wx.Platform == '__WXMSW__':
    faces = dict(times='Times New Roman', mono='Courier New', helv='Arial', other='Comic Sans MS', size=10, size2=8)
elif wx.Platform == '__WXMAC__':
    faces = dict(times='Times New Roman', mono='Monaco', helv='Arial', other='Comic Sans MS', size=12, size2=10)
else:
    faces = dict(times='Times', mono='Courier', helv='Helvetica', other='new century schoolbook', size=12, size2=10)

wx.SystemSettings_GetColour = wx.SystemSettings.GetColour if wx.VERSION_STRING >= '4.0' else wx.SystemSettings_GetColour


#################################################################
###
###        GENERAL FUNCTIONS
###
#################################################################

### NOTE: Editor.py :: isError                 => check if file is well-formed and if requirements are corrects
def isError(scriptlet):
    """
    """
    try:
        code = compile(scriptlet, '<string>', 'exec')
        exec(code)
    except Exception as info:
        return info
    else:
        return False

### NOTE: Editor.py :: getObjectFromString    => todo
def getObjectFromString(scriptlet):
    """
    """

    assert scriptlet != ''

    # Compile the scriptlet.
    try:
        code = compile(scriptlet, '<string>', 'exec')
    except Exception as info:
        ### Add line number to the error trace
        for frame in traceback.extract_tb(sys.exc_info()[2]):
            fname,lineno,fn,text = frame
        L = list(info.args)
        L.append("line %i"%lineno)
        info.args = tuple(L)
        return info

    else:
        # Create the new 'temp' module.
        temp = types.ModuleType('temp')
        sys.modules["temp"] = temp

        ### there is syntaxe error ?
        try:
            exec(code, temp.__dict__)

        except Exception as info:
            ### Add line number to the error trace
            for frame in traceback.extract_tb(sys.exc_info()[2]):
                fname,lineno,fn,text = frame
            L = list(info.args)
            L.append("line %i"%lineno)
            info.args = tuple(L)

            return info

        else:
            classes = inspect.getmembers(temp, callable)
            for name, value in classes:
                if value.__module__ == "temp":
                    # Create the instance.
                    try:
                        return eval("temp.%s" % name)()

                    except Exception as info:
                        ### Add line number to the error trace
                        for frame in traceback.extract_tb(sys.exc_info()[2]):
                            fname,lineno,fn,text = frame
                        L = list(info.args)
                        L.append("line %i"%lineno)
                        info.args = tuple(L)

                        return info


### NOTE: Editor.py :: GetEditor             => Return the appropriate Editor
def GetEditor(parent, id, title="", obj=None, **kwargs):
    """ Factory Editor
    @param: parent
    @param: id
    @param: title
    @param: obj
    @param: file_type
    """

    if "file_type" in list(kwargs.keys()):
        file_type = kwargs["file_type"]

        if file_type == "test":
            editor = TestEditor(parent, id, title)
        elif file_type == "block":
            editor = BlockEditor(parent, id, title, obj)
        else:
            editor = GeneralEditor(parent, id, title)
    else:
        editor = GeneralEditor(parent, id, title)

    return editor

#################################################################
###
###        GENERAL CLASSES
###
#################################################################

class TestSearchCtrl(wx.SearchCtrl):
    maxSearches = 5

    def __init__(self, parent, id=-1, value="",
                 pos=wx.DefaultPosition, size=wx.DefaultSize, style=0,
                 doSearch=None):
        style |= wx.TE_PROCESS_ENTER
        wx.SearchCtrl.__init__(self, parent, id, value, pos, size, style)
        self.Bind(wx.EVT_TEXT_ENTER, self.OnTextEntered)
        self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.OnTextEntered)
        self.Bind(wx.EVT_MENU_RANGE, self.OnMenuItem, id=1, id2=self.maxSearches)
        self.doSearch = doSearch
        self.searches = []

    def OnTextEntered(self, evt):
        text = self.GetValue()
        if self.doSearch(text):
            self.searches.append(text)
            if len(self.searches) > self.maxSearches:
                del self.searches[0]
            self.SetMenu(self.MakeMenu())
        self.SetValue("")

    def OnMenuItem(self, evt):
        text = self.searches[evt.GetId()-1]
        self.doSearch(text)

    def MakeMenu(self):
        menu = wx.Menu()
        item = menu.Append(-1, "Recent Searches")
        item.Enable(False)
        for idx, txt in enumerate(self.searches):
            if txt != "":
                menu.Append(1+idx, txt)
        return menu

### NOTE: PythonSTC << stc.StyledTextCtrl :: todo
class PythonSTC(stc.StyledTextCtrl):
    """
    """

    fold_symbols = 2

    ### NOTE: PythonSTC:: constructor => __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0)
    def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
        """
        """
        stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
        
        # Do we want to automatically pop up command completion options?
        self.autoComplete = True
        self.autoCompleteIncludeMagic = True
        self.autoCompleteIncludeSingle = True
        self.autoCompleteIncludeDouble = True
        self.autoCompleteCaseInsensitive = True
        self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
        self.autoCompleteAutoHide = False
        self.AutoCompSetAutoHide(self.autoCompleteAutoHide)
        self.AutoCompStops(' .,;:([)]}\'"\\<>%^&+-=*/|`')
        
        self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
        self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)

        self.SetLexer(stc.STC_LEX_PYTHON)
        self.SetKeyWords(0, " ".join(keyword.kwlist))

        self.SetProperty("fold", "1")
        self.SetProperty("tab.timmy.whinge.level", "1")
        self.SetMargins(0, 0)

        self.SetViewWhiteSpace(False)
        self.SetBufferedDraw(False)
        self.SetViewEOL(True)
        self.SetEOLMode(stc.STC_EOL_CRLF)
        
        if wx.VERSION_STRING < '4.0': self.SetUseAntiAliasing(True)

        self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
        self.SetEdgeColumn(78)

        # Setup a margin to hold fold markers
        self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
        self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
        self.SetMarginSensitive(2, True)
        self.SetMarginWidth(2, 12)

        if self.fold_symbols == 0:
            # Arrow pointing right for contracted folders, arrow pointing down for expanded
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")

        elif self.fold_symbols == 1:
            # Plus for contracted folders, minus for expanded
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")

        elif self.fold_symbols == 2:
            # Like a flattened tree control using circular headers and curved joins
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040")

        elif self.fold_symbols == 3:
            # Like a flattened tree control using square headers
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
            self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080")

        self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
        self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)

        # Make some styles,  The lexer defines what each style is used for, we
        # just have to define what each style looks like.  This set is adapted from
        # Scintilla sample property files.

        # Global default styles for all languages
        self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
        #self.StyleSetSpec(STC_CODE_ERROR, 'fore:#FF0000,back:#FFFF00,size:%(size)d' % faces)
        #self.StyleSetSpec(STC_CODE_SEARCH_RESULT, 'fore:#FFFFFF,back:#FFA500,size:%(size)d' % faces)

        self.StyleClearAll()  # Reset all to be like the default

        # Global default styles for all languages
        self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
        self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
        self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
        self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
        self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")

        # Python styles
        # Default
        self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
        # Comments
        self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
        # Number
        self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
        # String
        self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
        # Single quoted string
        self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
        # Keyword
        self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
        # Triple quotes
        self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
        # Triple double quotes
        self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
        # Class name definition
        self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
        # Function or method name definition
        self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
        # Operators
        self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
        # Identifiers
        self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
        # Comment-blocks
        self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
        # End of line where string is not closed
        self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)

        self.SetCaretForeground("BLUE")

    # NOTE: PythonSTC :: __str__        => String representation of the class
    @classmethod
    def __str__(cls):
        attrs = [('fold_symbols', 'integer')]
        class_name = "PythonSTC"
        parent = "stc.StyledTextCtrl"
        methods = [
            ('__init__', 'self, parent, ID, pos, size, style'),
            ('FoldAll', 'self'),
            ('Expand', 'self, line, doExpand, force, visLevels, level'),
            ('OnUpdateUI', 'self, event'),
            ('OnMarginClick', 'self, event')
        ]
        return "\n--------------------------------------------------\
        \n\tClass :\t\t%s\n\n\tInherit from :\t%s\n\n\tAttributes :\t%s\n\n\tMethods :\t%s\n" % (
            class_name, parent, '\n\t\t\t'.join([attr + "\t:: " + typ for attr, typ in attrs]),
            "\n\t\t\t".join([method + "\tparams :: " + params for method, params in methods])
        )

    ### NOTE: PythonSTC :: OnUpdateUI             => Event for update user interface
    def OnUpdateUI(self, evt):

        # If the auto-complete window is up let it do its thing.
        if self.AutoCompActive() or self.CallTipActive():
            return

        # check for matching braces
        braceAtCaret = -1
        braceOpposite = -1
        charBefore = None
        caretPos = self.GetCurrentPos()

        if caretPos > 0:
            charBefore = self.GetCharAt(caretPos - 1)
            styleBefore = self.GetStyleAt(caretPos - 1)

        # check before
        if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
            braceAtCaret = caretPos - 1

        # check after
        if braceAtCaret < 0:
            charAfter = self.GetCharAt(caretPos)
            styleAfter = self.GetStyleAt(caretPos)

            if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
                braceAtCaret = caretPos

        if braceAtCaret >= 0:
            braceOpposite = self.BraceMatch(braceAtCaret)

        if braceAtCaret != -1 and braceOpposite == -1:
            self.BraceBadLight(braceAtCaret)
        else:
            self.BraceHighlight(braceAtCaret, braceOpposite)

    ### NOTE: PythonSTC :: OnMarginClick         => Event for click on margin
    def OnMarginClick(self, evt):
        # fold and unfold as needed
        if evt.GetMargin() == 2:
            if evt.GetShift() and evt.GetControl():
                self.FoldAll()
            else:
                lineClicked = self.LineFromPosition(evt.GetPosition())

                if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
                    if evt.GetShift():
                        self.SetFoldExpanded(lineClicked, True)
                        self.Expand(lineClicked, True, True, 1)
                    elif evt.GetControl():
                        if self.GetFoldExpanded(lineClicked):
                            self.SetFoldExpanded(lineClicked, False)
                            self.Expand(lineClicked, False, True, 0)
                        else:
                            self.SetFoldExpanded(lineClicked, True)
                            self.Expand(lineClicked, True, True, 100)
                    else:
                        self.ToggleFold(lineClicked)

    ### NOTE: PythonSTC :: FoldAll             => Fold entire code
    def FoldAll(self):
        lineCount = self.GetLineCount()
        expanding = True

        # find out if we are folding or unfolding
        for lineNum in range(lineCount):
            if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
                expanding = not self.GetFoldExpanded(lineNum)
                break

        lineNum = 0

        while lineNum < lineCount:
            level = self.GetFoldLevel(lineNum)
            if level & stc.STC_FOLDLEVELHEADERFLAG and (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:

                if expanding:
                    self.SetFoldExpanded(lineNum, True)
                    lineNum = self.Expand(lineNum, True)
                    lineNum -= 1
                else:
                    lastChild = self.GetLastChild(lineNum, -1)
                    self.SetFoldExpanded(lineNum, False)

                    if lastChild > lineNum:
                        self.HideLines(lineNum + 1, lastChild)

            lineNum += 1

    def Paste(self):
#        success = False
        #do = wx.TextDataObject()
#        if wx.TheClipboard.Open():
#            success = wx.TheClipboard.GetData(do)
#            wx.TheClipboard.Close()

#        if success:
#            if not self.execplugin('on_paste', self, do.GetText()):
#                wx.stc.StyledTextCtrl.Paste(self)
        wx.stc.StyledTextCtrl.Paste(self)

    def Copy(self):
        wx.stc.StyledTextCtrl.Copy(self)

    ### NOTE: PythonSTC :: Expand                 => Expand selected line
    def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
        lastChild = self.GetLastChild(line, level)
        line += 1

        while line <= lastChild:
            if force:
                if visLevels > 0:
                    self.ShowLines(line, line)
                else:
                    self.HideLines(line, line)
            else:
                if doExpand:
                    self.ShowLines(line, line)

            if level == -1:
                level = self.GetFoldLevel(line)

            if level & stc.STC_FOLDLEVELHEADERFLAG:
                if force:
                    if visLevels > 1:
                        self.SetFoldExpanded(line, True)
                    else:
                        self.SetFoldExpanded(line, False)

                    line = self.Expand(line, doExpand, force, visLevels - 1)

                else:
                    if doExpand and self.GetFoldExpanded(line):
                        line = self.Expand(line, True, force, visLevels - 1)
                    else:
                        line = self.Expand(line, False, force, visLevels - 1)
            else:
                line += 1

        return line


###-----------------------------------------------------------------------------
### NOTE: CodeEditor << PythonSTC :: todo
class CodeEditor(PythonSTC):
    #### NOTE: CodeEditor :: constructor         => __init__(self, parent)
    def __init__(self, parent):
        """ Constructor
        """
        PythonSTC.__init__(self, parent, wx.NewIdRef(), style=wx.BORDER_NONE)
        self.SetUpEditor()
        self.last_name_saved = ""

    # NOTE: CodeEditor :: __str__        => String representation of the class
    @classmethod
    def __str__(cls):
        attrs = [('last_name_saved', 'str')]
        class_name = "CodeEditor"
        parent = "PythonSTC"
        methods = [
            ('__init__', 'self, parent'),
            ('GetFilename', 'self'),
            ('SetFilename', 'self, filename'),
            ('GetValue', 'self'),
            ('SetValue', 'self, value'),
            ('IsModified', 'self'),
            ('Clear', 'self'),
            ('SetInsertionPoint', 'self, pos'),
            ('ShowPosition', 'self, pos'),
            ('GetLastPosition', 'self'),
            ('GetPositionFromLine', 'self, line'),
            ('GetRange', 'self, start, end'),
            ('GetSelection', 'self'),
            ('SetSelection', 'self, start, end'),
            ('SelectLine', 'self, line'),
            ('SetUpEditor', 'self'),
            ('RegisterModifiedEvent', 'self, eventHandler')
        ]
        return "\n--------------------------------------------------\
        \n\tClass :\t\t%s\n\n\tInherit from :\t%s\n\n\tAttributes :\t%s\n\n\tMethods :\t%s\n" % (
            class_name, parent, '\n\t\t\t'.join([attr + "\t:: " + typ for attr, typ in attrs]),
            "\n\t\t\t".join([method + "\tparams :: " + params for method, params in methods])
        )

    ### NOTE: CodeEditor :: GetFilename         => Get the last name saved
    def GetFilename(self):
        return self.last_name_saved

    ### NOTE: CodeEditor :: SetFilename         => Set the last name saved
    def SetFilename(self, filename):
        self.last_name_saved = filename

    ### NOTE: CodeEditor :: SetValue             => Set the text to print in the editor
    ### Some methods to make it compatible with how the wxTextCtrl is used
    def SetValue(self, value):
        self.SetText(value)
        self.EmptyUndoBuffer()
        self.SetSavePoint()

    ### NOTE: CodeEditor :: GetValue             => Get the text printed in the editor
    def GetValue(self):
        return self.GetText()

    ### NOTE: CodeEditor :: IsModified         => Flag to determine if the text is modified or not
    def IsModified(self):
        return self.GetModify()

    ### NOTE: CodeEditor :: Clear                 => Clear the text
    def Clear(self):
        self.ClearAll()

    ### NOTE: CodeEditor :: SetInsertionPoint    => Set an anchor for insertion
    def SetInsertionPoint(self, pos):
        self.SetCurrentPos(pos)
        self.SetAnchor(pos)

    ### NOTE: CodeEditor :: ShowPosition         => Go to the line of selected position
    def ShowPosition(self, pos):
        line = self.LineFromPosition(pos)
#        self.EnsureVisible(line)
        self.GotoLine(line)

    ### NOTE: CodeEditor :: GetLastPosition     => todo
    def GetLastPosition(self):
        return self.GetLength()

    ### NOTE: CodeEditor :: GetPositionFromLine => todo
    def GetPositionFromLine(self, line):
        return self.PositionFromLine(line)

    ### NOTE: CodeEditor :: GetRange             => Get the text range
    def GetRange(self, start, end):
        return self.GetTextRange(start, end)

    ### NOTE: CodeEditor :: GetSelection         => Get the selected text
    def GetSelection(self):
        return self.GetAnchor(), self.GetCurrentPos()

    ### NOTE: CodeEditor :: SetSelection         => Set the selected text
    def SetSelection(self, start, end):
        self.SetSelectionStart(start)
        self.SetSelectionEnd(end)

    ### NOTE: CodeEditor :: SelectLine         => Select the line
    def SelectLine(self, line):
        start = self.PositionFromLine(line)
        end = self.GetLineEndPosition(line)
        self.SetSelection(start, end)

    ### NOTE: CodeEditor :: SetUpEditor         => Configure lexer and color
    def SetUpEditor(self):
        """
        This method carries out the work of setting up the demo editor.
        It's seperate so as not to clutter up the init code.
        """
        import keyword

        self.SetLexer(stc.STC_LEX_PYTHON)
        self.SetKeyWords(0, " ".join(keyword.kwlist))

        ### Enable folding
        self.SetProperty("fold", "1")

        ### Highlight tab/space mixing (shouldn't be any)
        self.SetProperty("tab.timmy.whinge.level", "1")

        ### Set left and right margins
        self.SetMargins(2, 2)

        ### Set up the numbers in the margin for margin #1
        self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
        ### Reasonable value for, say, 4-5 digits using a mono font (40 pix)
        self.SetMarginWidth(1, 40)

        ### Indentation and tab stuff
        self.SetIndent(4)               # Proscribed indent size for wx
        self.SetIndentationGuides(True) # Show indent guides
        self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
        self.SetTabIndents(True)        # Tab key indents
        self.SetTabWidth(4)             # Proscribed tab size for wx
        self.SetUseTabs(True)          # Use spaces rather than tabs, or
        # TabTimmy will complain!
        ### White space
        self.SetViewWhiteSpace(False)   # Don't view white space

        ### EOL: Since we are loading/saving ourselves, and the
        ### strings will always have \n's in them, set the STC to
        ### edit them that way.
        self.SetEOLMode(wx.stc.STC_EOL_LF)
        self.SetViewEOL(False)

        ### No right-edge mode indicator
        self.SetEdgeMode(stc.STC_EDGE_NONE)

        ### Setup a margin to hold fold markers
        self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
        self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
        self.SetMarginSensitive(2, True)
        self.SetMarginWidth(2, 12)

        ### and now set up the fold markers
        self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "black")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "black")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "black")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "black")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "black")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "black")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "black")

        ### Global default style
        if wx.Platform == '__WXMSW__':
            self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Courier New,size:9')
        elif wx.Platform == '__WXMAC__':
        ### TODO: if this looks fine on Linux too, remove the Mac-specific case
        ### and use this whenever OS != MSW.
            self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Monaco')
        else:
            self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Courier,size:9')

        # Clear styles and revert to default.
        self.StyleClearAll()

        # Following style specs only indicate differences from default.
        # The rest remains unchanged.

        # Line numbers in margin
        self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER, 'fore:#000000,back:#99A9C2')
        # Highlighted brace
        self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT, 'fore:#00009D,back:#FFFF00')
        # Unmatched brace
        self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD, 'fore:#00009D,back:#FF0000')
        # Indentation guide
        self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")

        # Python styles
        self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
        # Comments
        self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
        self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
        # Numbers
        self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
        # Strings and characters
        self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080')
        self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080')
        # Keywords
        self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
        # Triple quotes
        self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
        self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
        # Class names
        self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
        # Function names
        self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
        # Operators
        self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
        # Identifiers. I leave this as not bold because everything seems
        # to be an identifier if it doesn't match the above criterae
        self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')

        # Caret color
        self.SetCaretForeground("BLUE")
        # Selection background
        self.SetSelBackground(1, '#66CCFF')

        self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) )
        self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))

    ### NOTE: CodeEditor :: RegisterModifiedEvent => todo
    def RegisterModifiedEvent(self, eventHandler):
        """
        """
        self.Bind(wx.stc.EVT_STC_CHANGE, eventHandler)


### EditionFile-----------------------------------------------------
### NOTE: EditionFile << CodeEditor :: Expect EditionFile objects to clearly separate file and notebook attributes
class EditionFile(CodeEditor):
    """
    """

    #
    def __init__(self, parent, path, code):
        """ Constructor
        """
        CodeEditor.__init__(self, parent)

        # variables
        self.modify = False
        self.error_flag = False
        self.SetFilename(path)
        self.SetValue(code)

    # NOTE: EditionFile :: __str__        => String representation of the class
    @classmethod
    def __str__(cls):
        attrs = [('modify', 'boolean'), ('error_flag', 'boolean')]
        class_name = "EditionFile"
        parent = "CodeEditor"
        methods = [
            ('__init__', 'self, parent'),
            ('ContainError', 'self')
        ]
        return "\n--------------------------------------------------\
        \n\tClass :\t\t%s\n\n\tInherit from :\t%s\n\n\tAttributes :\t%s\n\n\tMethods :\t%s\n" % (
            class_name, parent, '\n\t\t\t'.join([attr + "\t:: " + typ for attr, typ in attrs]),
            "\n\t\t\t".join([method + "\tparams :: " + params for method, params in methods])
        )

    # NOTE: EditionFile :: ContainError         => Getter of the error flag
    def ContainError(self):
        """
        """
        return self.error_flag

### ----------------------------------------------------------------


### EditionNotebook-------------------------------------------------
### NOTE: EditionNotebook << wx.Notebook :: Notebook for multiple file edition
class EditionNotebook(wx.Notebook):
    """
    """

    ### NOTE: EditionNotebook :: constructor     => __init__(self, *args, **kwargs)
    def __init__(self, *args, **kwargs):
        """
        Notebook class that allows overriding and adding methods.

        @param parent: parent windows
        @param id: id
        @param pos: windows position
        @param size: windows size
        @param style: windows style
        @param name: windows name
        """

        wx.Notebook.__init__(self, *args, **kwargs)

        # local copy
        self.parent = args[0]
        self.pages = []            # keeps track of pages

        # variables
        self.force_saving = False

        #icon under tab
        imgList = wx.ImageList(16, 16)
        for img in [os.path.join(ICON_PATH_16_16, 'featureFile.png')]:
            imgList.Add(wx.Bitmap(img))
        self.AssignImageList(imgList)

        ### binding
        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.__PageChanged)

        self.Show()

    # NOTE: EditionNotebook :: __str__        => String representation of the class
    @classmethod
    def __str__(cls):
        attrs = [('parent', 'UNDEFINED'), ('pages', 'list<EditionFile>'), ('force_saving', 'boolean')]
        class_name = "EditionNotebook"
        parent = "wx.Notebook"
        methods = [
            ('__init__', 'self, parent, ID, pos, size, style'),
            ('GetPages', 'self'),
            ('AddEditPage', 'self, title, path'),
            ('GetPageByName', 'self, name'),
            ('DoOpenFile', 'self'),
            ('DoSaveFile', 'self, base_name, code'),
            ('__PageChanged', 'self, event'),
            ('OnClosePage', 'self, event'),
            ('OnKeyDown', 'self, event'),
            ('OnCut', 'self, event'),
            ('OnCopy', 'self, event'),
            ('OnPaste', 'self, event'),
            ('OnReIndent', 'self, event'),
            ('OnDelete', 'self, event'),
            ('OnSelectAll', 'self, event'),
            ('<static> WriteFile', 'fileName, code, encode'),
            ('<static> CheckIndent', 'filename')
        ]
        return "\n--------------------------------------------------\
        \n\tClass :\t\t%s\n\n\tInherit from :\t%s\n\n\tAttributes :\t%s\n\n\tMethods :\t%s\n" % (
            class_name, parent, '\n\t\t\t'.join([attr + "\t:: " + typ for attr, typ in attrs]),
            "\n\t\t\t".join([method + "\tparams :: " + params for method, params in methods])
        )

    ### NOTE: EditionNotebook :: GetPages         => Get the list of created pages
    def GetPages(self):
        """
        """
        return self.pages

    ### NOTE: EditionNotebook :: AddEditPage     => Create a new page
    def AddEditPage(self, title="", path=""):
        """
        Adds a new page for editing to the notebook and keeps track of it.

        @type title: string
        @param title: Title for a new page
        """

        fileCode = ""
        
        ### FIXME: try to consider zipfile in zipfile
        L = re.findall("(.*\.(amd|cmd))\%s(.*)" % os.sep, path)

        if L:
            model_path, ext, name = L.pop(0)
            if zipfile.is_zipfile(model_path):
                importer = zipfile.ZipFile(model_path, "r")
                fileInfo = importer.getinfo(name)
                fileCode = importer.read(fileInfo)
                importer.close()
        else:
            if os.path.exists(path):
                with open(path, 'r') as f:
                        fileCode = f.read()
            else:
                ### fileCode is path (user work with IOString code, not file object)
                fileCode = path

        ### new page
        newPage = EditionFile(self, path, fileCode)
        newPage.SetFocus()

        ### bind the page
        newPage.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        newPage.Bind(wx.EVT_CHAR, self.parent.OnChar)

        ### add page
        self.pages.append(newPage)
        self.AddPage(newPage, title, imageId=0)

    ### NOTE: EditionNotebook :: GetPageByName => Return the page with the required name
    def GetPageByName(self, name=''):
        """
        """
        for i in range(len(self.pages)):
            if name == self.GetPageText(i):
                return self.GetPage(i)
        return None

    ### NOTE: EditionNotebook :: __PageChanged => Event when page changed
    def __PageChanged(self, evt):
        """
        """

        try:
            canvas = self.GetPage(self.GetSelection())

            ### allows to activate redo and undo for each page
            self.parent.tb.EnableTool(wx.ID_UNDO, len(canvas.stockUndo) != 0)
            self.parent.tb.EnableTool(wx.ID_REDO, len(canvas.stockRedo) != 0)

            canvas.deselect()
            canvas.Refresh()

        except Exception:
            pass
        evt.Skip()

    ### NOTE: EditionNotebook :: OnClosePage     => Event when the close button is clicked
    def OnClosePage(self, evt, id):
        """ Close current page.

            @type evt: event
            @param  evt: Event Object, None by default
        """

        if self.GetPageCount() > 0:
            self.pages.remove(self.GetPage(id))
            return self.DeletePage(id)

        return True

    ### NOTE: EditionNotebook :: OnKeyDown     => Event when key is pressed
    def OnKeyDown(self, event):
        """
        """
        keycode = event.GetKeyCode()
        controlDown = event.CmdDown()
        currentPage = self.GetCurrentPage()

        if keycode == wx.WXK_UP or keycode == wx.WXK_DOWN:
            event.Skip()
        elif keycode == 32 and controlDown:
            pos = currentPage.GetCurrentPos()

            kw = keyword.kwlist[:]
            #kw.append("this_is_a_much_much_much_much_much_much_much_longer_value")

            kw.sort()  # Python sorts are case sensitive
            currentPage.AutoCompSetIgnoreCase(False)  # so this needs to match

            # Images are specified with a appended "?type"
            for i in range(len(kw)):
                if kw[i] in keyword.kwlist:
                    kw[i] = kw[i] + "?1"

            currentPage.AutoCompShow(0, " ".join(kw))

        elif keycode == 68 and controlDown:
            cur_line = currentPage.GetCurrentLine()
            shiftDown = event.ShiftDown()
            if shiftDown:
                indent = currentPage.GetLineIndentPosition(cur_line)
                currentPage.Home()
                currentPage.DelWordRight()
                currentPage.SetCurrentPos(indent)
            else:
                currentPage.InsertText(currentPage.PositionFromLine(cur_line), "#")
        # elif keycode not in (32,13,9,8,27,316,314):
        #     pos = currentPage.GetCurrentPos()
        #     line = currentPage.GetLine(pos)
        #     start = currentPage.WordStartPosition(pos, True)
        #     end = currentPage.WordEndPosition(pos, True)
        #     current_word = currentPage.GetTextRange(start, end)

        #     kw = keyword.kwlist[:]
        #     kw.append('self')

        #     filtered_kw = [word for word in kw if word.startswith(current_word)]

        #     filtered_kw.sort()  # Python sorts are case sensitive

        #     if filtered_kw:
        #         currentPage.AutoCompSetIgnoreCase(False)  # so this needs to match
        #         currentPage.AutoCompShow(0, " ".join(filtered_kw))
        #     else:
        #         # If no suggestions are available, hide the autocomplete list
        #         currentPage.AutoCompCancel()
        #     event.Skip()
        else:
            event.Skip()


    ### NOTE: EditionNotebook :: DoOpenFile     => Opening file method
    def DoOpenFile(self):
        """
        """
        currentPage = self.GetCurrentPage()
        wcd = 'All files (*)|*|Editor files (*.py)|*.py'
        dir = HOME_PATH
        open_dlg = wx.FileDialog(self, message=_('Choose a file'), defaultDir=dir, defaultFile='', wildcard=wcd,
                                 style=wx.OPEN | wx.CHANGE_DIR)
        if open_dlg.ShowModal() == wx.ID_OK:
            path = open_dlg.GetPath()

            try:
                with codecs.open(path, 'r', 'utf-8') as f:
                    text = f.read()

                if currentPage.GetLastPosition():
                    currentPage().Clear()
                currentPage().WriteText(text)
                currentPage().SetFilename(path)

                currentPage.modify = False

            except Exception as info:
                wx.MessageBox(_('Error opening file:\n%s\n')%str(info),\
                            "Open file function",\
                            wx.OK | wx.ICON_ERROR)

        open_dlg.Destroy()

    ### NOTE: EditionNotebook :: DoSaveFile     => Saving file method
    def DoSaveFile(self, code):
        """
        """

        currentPage = self.GetCurrentPage()
        
        abs_path = currentPage.GetFilename()    # /home/../toto.*
        fic_filename = os.path.basename(abs_path)    # fileName toto.*
        model_dir = os.path.dirname(abs_path)        # model toto.amd
        
        ### if zipfile
        if zipfile.is_zipfile(model_dir):

            model_name, model_ext = os.path.basename(model_dir).split('.')        # toto, .amd or .cmd
            fic_name, fic_ext = fic_filename.split('.')            # toto, *

            ### write code in base_name temporary file
            self.WriteFile(fic_filename, code)

            ### update archive
            zf = ZipManager.Zip(model_dir)
            zf.Update(replace_files=[fic_filename])

            ### Clean up the temporary file yourself
            try:
                os.remove(fic_filename)
            except Exception as info:
                sys.exc_info()
                sys.stderr.write(_('File has not been deleted: %s'%info))

            ### reload module only if zipped python file is not plugins
            ### update only for python file of model which have path like .../name.amd(.cmd)/name.ext
            if isinstance(self.parent, Base) and fic_name == model_name and fic_ext == 'py':
                self.parent.UpdateModule()

        ### if python file in zipfile but also in directory into zipfile
        elif zipfile.is_zipfile(os.path.dirname(model_dir)):

            r_file = os.path.join(os.path.basename(model_dir), fic_filename)
            model_dir = os.path.dirname(model_dir)

            model_name, model_ext = os.path.basename(model_dir).split('.')        # toto, .amd or .cmd
            fic_name, fic_ext = fic_filename.split('.')            # toto, *

            ### write code in base_name temporary file
            self.WriteFile(fic_filename, code)

            ### update archive
            zf = ZipManager.Zip(model_dir)

            zf.Update(replace_files=[r_file])

            # Clean up the temporary file yourself
            os.remove(fic_filename)

            ### reload module only if zipped python file is not plugins
            ### update only for python file of model which have path like .../name.amd(.cmd)/name.ext
            if isinstance(self.parent, Base) and fic_name == model_name and fic_ext == 'py':
                self.parent.UpdateModule()

        ### if python file not in zipfile
        else:
            #assert (os.path.isfile(abs_path))
            if os.path.isfile(abs_path):
                ### write code in last name saved file
                self.WriteFile(abs_path, code)

                if isinstance(self.parent, Base):
                    ### reload module
                    self.parent.UpdateModule()
            else:
                pass

        ### disable save icon in toolbar
        self.parent.toolbar.EnableTool(self.parent.save.GetId(), False)

        ### status bar notification
        self.parent.Notification(False, _('%s saved') % fic_filename.replace("*",""), '', '')

    ### NOTE: EditionNotebook :: @WriteFile     => Write with correct encode
    @staticmethod
    def WriteFile(fileName, code, encode='utf-8'):
        """ Static method which write modification to the fileName file
        """

        with codecs.open(fileName, 'w', encode) as f:
            f.write(code)

    ### NOTE: EditionNotebook :: OnCut         => Event on cut
    def OnCut(self, event):
        """ Cut the text
        """
        self.GetCurrentPage().Cut()

    ### NOTE: EditionNotebook :: OnCopy         => Event on copy
    def OnCopy(self, event):
        """ Copy the text
        """
        self.GetCurrentPage().Copy()

    ### NOTE: EditionNotebook :: OnPaste         => Event on paste
    def OnPaste(self, event):
        """ Paste the text
        """
        self.GetCurrentPage().Paste()

    ### NOTE: EditionNotebook :: CheckIndent     => Check the code indentation
    @staticmethod
    @redirectStdout
    def CheckIndent(fileName):
        """
        """
        tabnanny.check(fileName)

    ### NOTE: EditionNotebook :: OnReIndent     => Event on re-indent
    def OnReIndent(self, event):
        """ Reindent all the text
        """
        cp = self.GetCurrentPage()

        parent_path = os.path.dirname(cp.GetFilename())
        tempdir = os.path.realpath(gettempdir())

        ### if zipfile
        if zipfile.is_zipfile(parent_path):
            ### python name file
            name = os.path.basename(cp.GetFilename())

            ### extract python file from zip in tmp directory
            sourceZip = zipfile.ZipFile(parent_path, 'r')
            sourceZip.extract(name, tempdir)
            sourceZip.close()

            ### temporary python name file
            python_file = os.path.join(tempdir, name)
        else:
            ### python name file
            python_file = cp.GetFilename()

        ### reindent from file
        os.system("python %s" % os.path.join(HOME_PATH, "reindent.py") + " " + python_file)

        ### only with python 2.6
        with codecs.open(str(python_file), 'r', 'utf-8') as f:
            text = f.read()

        ### relaod text in the textCtrl of Editor
        cp.Clear()
        cp.SetValue(text)

        ### status bar notification
        self.parent.Notification(True, _('%s re-indented' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnCommentUnComment(self, event):
        """ Comment/Uncomment current line(s)
        """
        cp = self.GetCurrentPage()
        selected_txt = cp.GetSelectedText()
        raws = selected_txt.split('\n')

        ### select lines
        if len(raws) > 1:
            commented_txt = ""
            ### for each raw, we insert # in front of the first caractere of the string in raw
            for i,raw in enumerate(raws):
                ### find index position of the first caractere
                find = re.search(r'[A-Za-z#]', raw)
                ### if caracter finded (False in the case of raw without caratere...)
                if find:
                    ### position of the first caractere in the raw
                    pos = find.start()
                    ### \n not for the last line
                    end_line = '\n' if i < len(raws)-1 else ''
                    ### comment symbol in python
                    symbol = "#"

                    ### uncomment - comment symbol is finded
                    if raw[pos] == symbol:
                        raw = raw.replace(symbol,"")
                        symbol = ""
                    
                    commented_txt += "".join(f"{raw[:pos]}{symbol}{raw[pos:]}{end_line}")

            ### replace slelected text by the commented text
            cp.ReplaceSelection(commented_txt)
        ### cursor is in the line to comment/uncomment
        else:

            ### comment symbol in python
            symbol = "#"

            ### pointed line that contain the line to comment/unncomment
            pointed_txt,pos = cp.GetCurLine()
            cur_line = cp.GetCurrentLine()
            ### search #
            find = re.search(r'[#]', pointed_txt)
            ### uncomment - comment symbol is finded
            if find:
                ### remove # and \n
                pointed_txt = pointed_txt.replace(symbol,"").replace("\n",'')
                pos_from = cp.PositionFromLine(cur_line)
                pos_to = pos_from+len(pointed_txt)+1
                ### replace the commented line by the uncommented one
                cp.Replace(pos_from, pos_to, pointed_txt)
            ### comment 
            else:
                cp.InsertText(cp.PositionFromLine(cur_line), symbol)
        
        ### status bar notification
        self.parent.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    # def OnUnComment(self, event):
        # """ Uncomment current line(s)
        # """
        # cp = self.GetCurrentPage()
        # cur_line = cp.GetCurrentLine()
        # indent = cp.GetLineIndentPosition(cur_line)
        # cp.Home()
        # cp.DelWordRight()
        # cp.SetCurrentPos(indent)

    ### 
    def OnDelete(self, event):
        """ Delete selected text
        """
        cp = self.GetCurrentPage()
        frm, to = cp.GetSelection()
        cp.Remove(frm, to)

    ###
    def OnSelectAll(self, event):
        """ Select all the text
        """
        self.GetCurrentPage().SelectAll()

###------------------------------------------------------------
class Base(object):
    """ Editor Base class
    """

    ### 
    def __init__(self, parent, id, title):
        """ Constructor.
        """

        ### copy
        self.parent = parent

        # notebook
        self.read_only = False

        # find param
        self.pos = 0
        self.size = 0

    # def update(self, concret_subject=None):
    #     """ Update method that manages the embedded editor depending of the selected model in the canvas.
    #     """

    #     state = concret_subject.GetState()
    #     canvas = state['canvas']
    #     model = state['model']
        
    #     ### delete all tab on notebook
    #     while(self.nb.GetPageCount()):
    #         self.nb.DeletePage(0)

    #     ### add behavioral code
    #     self.AddEditPage(model.label, model.python_path)

    #     ### add test file
    #     if hasattr(model, 'GetTestFile'):
    #         L = model.GetTestFile()
    #         for i,s in enumerate(os.path.join(model.model_path, l) for l in L):
    #             self.AddEditPage(L[i], s)

    #     self.cb = model

    def CreateMenu(self):
        """ Create the menu
        """
        # setting up menubar
        menubar = wx.MenuBar()

        ### file sub menu---------------------------------------------------
        file = wx.Menu()

        self.save = wx.MenuItem(file, wx.NewIdRef(), _('&Save\tCtrl+S'), _('Save the file'))
        self.save_as = wx.MenuItem(file, wx.NewIdRef(), _('&Save As\tCtrl+S'), _('Save as an other file'))
        self.quit = wx.MenuItem(file, wx.NewIdRef(), _('&Quit\tCtrl+Q'), _('Quit the application'))

        self.save.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'save.png')))
        self.save_as.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'save_as.png')))
        self.quit.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'exit.png')))

        items = [self.save, self.save_as, self.quit]

        if wx.VERSION_STRING < '4.0':
            for item in items: file.AppendItem(item)
        else:
            for item in items: file.Append(item)

        ### -----------------------------------------------------------------

        ### edit sub menu----------------------------------------------------
        edit = wx.Menu()

        self.search = wx.MenuItem(edit, wx.NewIdRef(), _('&Search\tCtrl+F'), _('Search text'))

        self.cut = wx.MenuItem(edit, wx.NewIdRef(), _('&Cut\tCtrl+X'), _('Cut the selection'))
        self.copy = wx.MenuItem(edit, wx.NewIdRef(), _('&Copy\tCtrl+C'), _('Copy the selection'))
        self.paste = wx.MenuItem(edit, wx.NewIdRef(), _('&Paste\tCtrl+V'), _('Paste text from clipboard'))
        delete = wx.MenuItem(edit, wx.NewIdRef(), _('&Delete'), _('Delete the selected text'))
        select = wx.MenuItem(edit, wx.NewIdRef(), _('Select &All\tCtrl+A'), _('Select the entire text'))
        reindent = wx.MenuItem(edit, wx.NewIdRef(), _('Re-indent\tCtrl+R'), _('re-indent all code'))
        comment = wx.MenuItem(edit, wx.NewIdRef(), _('&Comment\tCtrl+D'), _('comment current ligne'))
        uncomment = wx.MenuItem(edit, wx.NewIdRef(), _('&Uncomment\tCtrl+D'), _('uncomment current ligne'))
        # uncomment = wx.MenuItem(edit, wx.NewIdRef(), _('&Uncomment\tCtrl+Shift+D'), _('uncomment current ligne'))

        self.cut.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'cut.png')))
        self.copy.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'copy.png')))
        self.paste.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'paste.png')))
        delete.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'delete.png')))
        reindent.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 're-indent.png')))
        comment.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'comment_add.png')))
        uncomment.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'comment_remove.png')))

        ### Shortcut
        accel_tbl = wx.AcceleratorTable([(wx.ACCEL_CTRL,  ord('S'), self.save.GetId()),
                                        (wx.ACCEL_CTRL,  ord('X'), self.cut.GetId()),
                                        (wx.ACCEL_CTRL,  ord('C'), self.copy.GetId()),
                                        (wx.ACCEL_CTRL,  ord('V'), self.paste.GetId()),
                                        (wx.ACCEL_CTRL,  ord('D'), comment.GetId()),
                                        (wx.ACCEL_CTRL| wx.ACCEL_SHIFT,  ord('D'), uncomment.GetId()),
                                        (wx.ACCEL_CTRL,  ord('F'), self.search.GetId())
                                        ])
        self.SetAcceleratorTable(accel_tbl)

        if wx.VERSION_STRING < '4.0':
            edit.AppendItem(self.cut)
            edit.AppendItem(self.copy)
            edit.AppendItem(self.paste)
            edit.AppendItem(reindent)
            edit.AppendItem(comment)
            edit.AppendItem(uncomment)
            edit.AppendItem(self.search)
            edit.AppendSeparator()
            edit.AppendItem(delete)
            edit.AppendSeparator()
            edit.AppendItem(select)
        else:
            edit.Append(self.cut)
            edit.Append(self.copy)
            edit.Append(self.paste)
            edit.Append(reindent)
            edit.Append(comment)
            edit.Append(uncomment)
            edit.Append(self.search)
            edit.AppendSeparator()
            edit.Append(delete)
            edit.AppendSeparator()
            edit.Append(select)
        ### -------------------------------------------------------------------

        ### view sub menu------------------------------------------------------
        view = wx.Menu()

        showStatusBar = wx.MenuItem(view, wx.NewIdRef(), _('&Statusbar'), _('Show statusBar'))
        if wx.VERSION_STRING < '4.0':
            view.AppendItem(showStatusBar)
        else:
            view.Append(showStatusBar)

        ### ------------------------------------------------------------------

        ### help sub menu-----------------------------------------------------
        help = wx.Menu()

        about = wx.MenuItem(help, wx.NewIdRef(), _('&About\tF1'), _('About editor'))
        about.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH, 'info.png')))
        if wx.VERSION_STRING < '4.0':
            help.AppendItem(about)
        else:
            help.Append(about)
        ### -------------------------------------------------------------------

        menubar.Append(file, _('&File'))
        menubar.Append(edit, _('&Edit'))
        menubar.Append(view, _('&View'))
        menubar.Append(help, _('&Help'))

        ### binding event
        self.Bind(wx.EVT_MENU, self.OnSaveFile, id=self.save.GetId())
        self.Bind(wx.EVT_MENU, self.OnSaveAsFile, id=self.save_as.GetId())
        self.Bind(wx.EVT_MENU, self.QuitApplication, id=self.quit.GetId())
        self.Bind(wx.EVT_MENU, self.nb.OnCut, id=self.cut.GetId())
        self.Bind(wx.EVT_MENU, self.nb.OnCopy, id=self.copy.GetId())
        self.Bind(wx.EVT_MENU, self.nb.OnPaste, id=self.paste.GetId())
        self.Bind(wx.EVT_MENU, self.nb.OnReIndent, id=reindent.GetId())
        self.Bind(wx.EVT_MENU, self.nb.OnCommentUnComment, id=comment.GetId())
        self.Bind(wx.EVT_MENU, self.OnSearch, id=self.search.GetId())
        self.Bind(wx.EVT_MENU, self.nb.OnCommentUnComment, id=uncomment.GetId())
        self.Bind(wx.EVT_MENU, self.nb.OnDelete, id=delete.GetId())
        self.Bind(wx.EVT_MENU, self.nb.OnSelectAll, id=select.GetId())
        self.Bind(wx.EVT_MENU, self.ToggleStatusBar, id=showStatusBar.GetId())
        self.Bind(wx.EVT_MENU, self.OnAbout, id=about.GetId())
        self.Bind(wx.EVT_FIND, self.OnFind)
        self.Bind(wx.EVT_FIND_NEXT, self.OnFind)

        return menubar

    def CreateTB(self):
        """ Create tool-bar.
        """

        tb = wx.ToolBar(self, wx.NewIdRef(), name='tb', style=wx.TB_HORIZONTAL | wx.NO_BORDER)
        tb.SetToolBitmapSize((16, 16))# this required for non-standard size buttons on MSW

        if not self.parent:
            
            if wx.VERSION_STRING < '4.0':
                self.Bind(wx.EVT_TOOL, self.OnSaveFile, tb.AddTool(self.save.GetId(),wx.Bitmap(os.path.join(ICON_PATH, 'save.png')), _('Save'), ''))
                tb.AddSeparator()
                self.Bind(wx.EVT_TOOL, self.nb.OnCut, tb.AddTool(self.cut.GetId(), wx.Bitmap(os.path.join(ICON_PATH,'cut.png')), _('Cut'), ''))
                self.Bind(wx.EVT_TOOL, self.nb.OnCopy, tb.AddTool(self.copy.GetId(), wx.Bitmap(os.path.join(ICON_PATH,'copy.png')), _('Copy'), ''))
                self.Bind(wx.EVT_TOOL, self.nb.OnPaste, tb.AddTool(self.paste.GetId(), wx.Bitmap(os.path.join(ICON_PATH,'paste.png')), _('Paste'), ''))
            else:
                self.Bind(wx.EVT_TOOL, self.OnSaveFile, tb.AddTool(self.save.GetId(), "", wx.Bitmap(os.path.join(ICON_PATH, 'save.png')),shortHelp=_('Save')))
                tb.AddSeparator()
                self.Bind(wx.EVT_TOOL, self.nb.OnCut, tb.AddTool(self.cut.GetId(), "", wx.Bitmap(os.path.join(ICON_PATH,'cut.png')),  shortHelp=_('Cut')))
                self.Bind(wx.EVT_TOOL, self.nb.OnCopy, tb.AddTool(self.copy.GetId(), "", wx.Bitmap(os.path.join(ICON_PATH,'copy.png')),  shortHelp=_('Copy')))
                self.Bind(wx.EVT_TOOL, self.nb.OnPaste, tb.AddTool(self.paste.GetId(), "", wx.Bitmap(os.path.join(ICON_PATH,'paste.png')),  shortHelp=_('Paste')))
            self.Bind(wx.EVT_TOOL, self.QuitApplication, id = self.quit.GetId())
        else:

            if wx.VERSION_STRING < '4.0':
                tb.AddTool(self.save.GetId(), wx.Bitmap(os.path.join(ICON_PATH, 'save.png')), shortHelpString=_('Save'), longHelpString=_('Save the file'))
                tb.AddTool(self.cut.GetId(), wx.Bitmap(os.path.join(ICON_PATH,'cut.png')), shortHelpString=_('Cut'), longHelpString=_('Cut the selection'))
                tb.AddTool(self.copy.GetId(), wx.Bitmap(os.path.join(ICON_PATH,'copy.png')), shortHelpString=_('Copy'), longHelpString=_('Copy the selection'))
                tb.AddTool(self.paste.GetId(), wx.Bitmap(os.path.join(ICON_PATH,'paste.png')), shortHelpString=_('Paste'), longHelpString=_('Paste text from clipboard'))
            else:
                tb.AddTool(self.save.GetId(), "",wx.Bitmap(os.path.join(ICON_PATH, 'save.png')), shortHelp=_('Save'))
                tb.AddTool(self.cut.GetId(), "",wx.Bitmap(os.path.join(ICON_PATH,'cut.png')), shortHelp=_('Cut'))
                tb.AddTool(self.copy.GetId(), "",wx.Bitmap(os.path.join(ICON_PATH,'copy.png')), shortHelp=_('Copy'))
                tb.AddTool(self.paste.GetId(), "",wx.Bitmap(os.path.join(ICON_PATH,'paste.png')), shortHelp=_('Paste'))

            self.Bind(wx.EVT_TOOL, self.OnSaveFile, id=self.save.GetId())
            self.Bind(wx.EVT_TOOL, self.nb.OnCut, id=self.cut.GetId())
            self.Bind(wx.EVT_TOOL, self.nb.OnCopy, id=self.copy.GetId())
            self.Bind(wx.EVT_TOOL, self.nb.OnPaste, id= self.paste.GetId())

        tb.Realize()

        return tb

    def DoSearch(self, text):
        """
        """
        nb = self.GetNoteBook()
        currentPage = nb.GetCurrentPage()
        self.txt = currentPage.GetValue()
        self.data = wx.FindReplaceData()   # initializes and holds search parameters
        self.DoFind(text)
        
        #sys.stdout.write("DoSearch: %s\n" % text)
        # return true to tell the search ctrl to remember the text
        return True

    def GetNoteBook(self):
        """ Return the NoteBook
        """
        return self.nb

    # NOTE: Editor :: __str__        => String representation of the class
    @classmethod
    def __str__(cls):
        attrs = [('read_only', 'boolean'), ('nb', 'EditionNotebook'), ('save', 'wx.MenuItem'),('toolbar', 'wx.Toolbar')]
        class_name = "Editor"
        parent = "wx.Frame, wx.Panel"
        methods = [
            ('__init__', 'self, parent, id, title'),
            ('AddEditPage', 'self, title, path'),
            ('SetReadOnly', 'self, bol'),
            ('MakeIcon', 'self, img'),
            ('ConfigSaving', 'self, base_name, dir_name, code'),
            ('CheckErrors', 'self, base_name, code, new_instance'),
            ('SavingErrors', 'self, new_instance'),
            ('Notification', 'self, modify, *args'),
            ('GetStatusBar', 'self'),
            ('ToggleStatusBar', 'self, event'),
            ('OnChar', 'self, event'),
            ('OnOpenFile', 'self, event'),
            ('OnSaveFile', 'self, event'),
            ('QuitApplication', 'self, event'),
            ('OnAbout', 'self, event')
        ]
        return "\n--------------------------------------------------\
        \n\tClass :\t\t%s\n\n\tInherit from :\t%s\n\n\tAttributes :\t%s\n\n\tMethods :\t%s\n" % (
            class_name, parent, '\n\t\t\t'.join([attr + "\t:: " + typ for attr, typ in attrs]),
            "\n\t\t\t".join([method + "\tparams :: " + params for method, params in methods])
        )

    # NOTE: Editor :: AddEditPage        => Add new page
    def AddEditPage(self, title='', path=''):
        self.nb.AddEditPage(title, path)

    ### NOTE: Editor :: SetReadOnly             => Set the editor read-only
    def SetReadOnly(self, bol):
        self.read_only = bol

    ### NOTE: Editor :: MakeIcon                 => Make icons for the various platforms
    def MakeIcon(self, img):
        """
        The various platforms have different requirements for the
        icon size...
        """
        if "wxMSW" in wx.PlatformInfo:
            img = img.Scale(16, 16)
        elif "wxGTK" in wx.PlatformInfo:
            img = img.Scale(22, 22)

        # wxMac can be any size upto 128x128, so leave the source img alone....
        return wx.IconFromBitmap(img.ConvertToBitmap()) if wx.VERSION_STRING < '4.0' else wx.Icon(img.ConvertToBitmap())

    ### NOTE: Editor :: OnOnpenFile             => Event OnOpenFile
    def OnOpenFile(self, event):
        """ Open File has been invoked
        """
        if self.nb.GetCurrentPage().isModified():
            dlg = wx.MessageDialog(self, _('Save changes?'), _('Code Editor'), wx.YES_NO | wx.YES_DEFAULT | wx.CANCEL |wx.ICON_QUESTION)
            val = dlg.ShowModal()
            if val == wx.ID_YES:
                self.OnSaveFile(event)
                self.DoOpenFile()
            elif val == wx.ID_CANCEL:
                dlg.Destroy()
            else:
                self.DoOpenFile()
        else:
            self.DoOpenFile()

    ### NOTE: Editor :: OnSaveFile            => Event when save menu has been clicked
    def OnSaveFile(self, event):
        """ Save menu has been clicked.
        """
        currentPage = self.nb.GetCurrentPage()
        fn = currentPage.GetFilename()

        if not self.read_only:

            assert fn != ''

            ### base and dir name of python file
            base_name = os.path.basename(fn)
            dir_name = os.path.dirname(fn)

            ### code text
            code = currentPage.GetValue()
            code = '\n'.join(code.splitlines()) + '\n'

            new_instance = self.ConfigSaving(base_name, dir_name, code)

            
            ### there is error in file ?
            currentPage.error_flag = isinstance(new_instance, Exception)

            self.CheckErrors(base_name, code, new_instance)

        else:
            ### status bar notification
            self.Notification(False, _('%s not saved' % fn), _('file in readonly'), '')

    def OnSearch(self, evt):
        """
        """
        nb = self.GetNoteBook()
        currentPage = nb.GetCurrentPage()
        self.txt = currentPage.GetValue()
        self.data = wx.FindReplaceData()   # initializes and holds search parameters
        self.dlg = wx.FindReplaceDialog(currentPage, self.data, 'Find')
        self.dlg.Show()
    
    def OnFind(self, evt):
        """
        """
        findstring = self.data.GetFindString().lower()
        self.DoFind(findstring)

    def DoFind(self, findstring:str)->None:
        """
        """
        nb = self.GetNoteBook()
        editor = nb.GetCurrentPage()
        end = editor.GetLastPosition()
        textstring = editor.GetRange(0, end).lower()
        backward = not (self.data.GetFlags() & wx.FR_DOWN)

        if backward:
            start = editor.GetSelection()[0]
            loc = textstring.rfind(findstring, 0, start)
        else:
            start = editor.GetSelection()[1]
            loc = textstring.find(findstring, start)
        if loc == -1 and start != 0:
            # string not found, start at beginning
            if backward:
                start = end
                loc = textstring.rfind(findstring, 0, start)
            else:
                start = 0
                loc = textstring.find(findstring, start)
        if loc == -1:
            dlg = wx.MessageDialog(self, 'Find String Not Found',
                            'Find String Not Found in Demo File',
                            wx.OK | wx.ICON_INFORMATION)
            dlg.ShowModal()
            dlg.Destroy()

        # if self.finddlg:
        #     if loc == -1:
        #         self.finddlg.SetFocus()
        #         return
        #     else:
        #         self.finddlg.Destroy()
        #         self.finddlg = None

        editor.ShowPosition(loc)
        editor.SetSelection(loc, loc + len(findstring))

    def OnSaveAsFile(self, event):
        """
        """

        currentPage = self.nb.GetCurrentPage()
        fn = currentPage.GetFilename()

        if not self.read_only:

            assert fn != ''

            dir_name = os.path.dirname(fn)

            msg = "Python files (*.py)|*.py|All files (*)|*"

            wcd = _(msg)
            home = dir_name or HOME_PATH
            save_dlg = wx.FileDialog(self, message=_('Save file as...'), defaultDir=home, defaultFile='', wildcard=wcd, style=wx.SAVE | wx.OVERWRITE_PROMPT)

        if save_dlg.ShowModal() == wx.ID_OK:

            path = os.path.normpath(save_dlg.GetPath())
            ext = os.path.splitext(path)[-1]
            file_name = save_dlg.GetFilename()

            ### code text
            code = currentPage.GetValue()
            code = '\n'.join(code.splitlines()) + '\n'

            ### write code in last name saved file
            self.nb.WriteFile(path, code)

    ### NOTE: Editor :: ConfigSaving             => Configure save vars
    def ConfigSaving(self, base_name, dir_name, code):
        """
        """

        new_instance = None
        ### if force saving when quitting Editor
        if self.nb.force_saving:
            self.nb.DoSaveFile(code)
        else:
            new_instance = getObjectFromString(code)

        return new_instance

    # NOTE: Editor :: CheckErrors             => Check errors in files before saving
    def CheckErrors(self, base_name, code, new_instance):
        """
        """
        if not self.nb.GetCurrentPage().ContainError():
            self.nb.DoSaveFile(code)

            ### DAM and UAM update which are implemented in string object
            if not base_name.endswith('.py'):
                ### canvas and diagram
                canvas = self.parent
                dia = canvas.GetDiagram()

                ### current level
                cl =  dia.current_level

                ### if DAM string is in code
                if 'DAM' in code:
                    canvas.SetDAM(cl, code)
                else:
                    canvas.SetUAM(cl, code)

        ### some errors in file
        else:
            print("In checkErrors: ",new_instance)
            self.SavingErrors(new_instance)

    # NOTE: Editor :: SavingErrors            => Errors treatment
    def SavingErrors(self, new_instance):
        """ perhaps re-indent ?
        """

        fn = self.nb.GetCurrentPage().GetFilename()

        output_checking = EditionNotebook.CheckIndent(fn)
        if "indent not equal" in output_checking:
            dial = wx.MessageDialog(self, _('Tab problem in %s.\n%s \
                \nYou can try to re-indent it with Edit-> Re-indent sub-menu.' % (fn, output_checking)),
                                    _('Code Editor'), wx.OK | wx.ICON_INFORMATION)
            dial.ShowModal()
        else:
            ### status bar notification
            msg = _('Saving Error')

            try:
                self.Notification(True, msg, str(new_instance.args[0]), str(new_instance.args[1]))
            except UnicodeDecodeError:
                self.Notification(True, msg, str(new_instanceargs[0]).decode('latin-1').encode("utf-8"), str(new_instanceargs[1]).decode('latin-1').encode("utf-8"))

    ### NOTE: Editor :: Notification             => Notify something on the statusbar
    def Notification(self, modify, *args):
        """ Notify event in status bar
        """
        self.nb.GetCurrentPage().modify = modify

        if hasattr(self, 'statusbar'):
            for i, s in enumerate(args):
                printOnStatusBar(self.statusbar, {i:s})

    ### NOTE: Editor :: StatusBar             => Create a status bar
    def GetStatusBar(self):
        """ Get the status bar
        """
        sb = self.CreateStatusBar()
        sb.SetFieldsCount(3)
        sb.SetStatusWidths([-2, -2, -5])
        return sb

    ### NOTE: Editor :: ToggleStatusBar         => Event for show or hide status bar
    def ToggleStatusBar(self, event):
        """
        """
        if self.statusbar.IsShown():
            self.statusbar.Hide()
        else:
            self.statusbar.Show()

    ### NOTE: Editor :: OnChar                 => Event when a char is typed
    def OnChar(self, event):
        """
        """

        ### enable save icon in toolbar
        self.toolbar.EnableTool(self.save.GetId(), True)
        ### status bar notification
        self.Notification(True, _('%s modified' % (os.path.basename(self.nb.GetCurrentPage().GetFilename()))), '', '')
        event.Skip()

    ### NOTE: Editor :: OnOpenFile             => Event OnOpenFile
    def OnOpenFile(self, event):
        """
        """
        if self.nb.GetCurrentPage().isModified():
            dlg = wx.MessageDialog(self, _('Save changes?'), _('Code Editor'),
                                   wx.YES_NO | wx.YES_DEFAULT | wx.CANCEL | wx.ICON_QUESTION)
            val = dlg.ShowModal()
            if val == wx.ID_YES:
                self.OnSaveFile(event)
                self.DoOpenFile()
            elif val == wx.ID_CANCEL:
                dlg.Destroy()
            else:
                self.DoOpenFile()
        else:
            self.DoOpenFile()

    ### NOTE: Editor :: OnSaveFile            => Event when save menu has been clicked
    def OnSaveFile(self, event):
        """ Save menu has been clicked.
        """
        currentPage = self.nb.GetCurrentPage()

        if not self.read_only:

            assert currentPage.GetFilename() != ''

            ### base and dir name of python file
            base_name = os.path.basename(currentPage.GetFilename())
            dir_name = os.path.dirname(currentPage.GetFilename())

            ### code text
            code = currentPage.GetValue()
            code = '\n'.join(code.splitlines()) + '\n'

            new_instance = self.ConfigSaving(base_name, dir_name, code)



            ### there is error in file ?
            currentPage.error_flag = isinstance(new_instance, Exception)

            self.CheckErrors(base_name, code, new_instance)

        else:
            ### status bar notification
            self.Notification(False, _('%s not saved' % (currentPage.GetFilename())), _('file in readonly'))

    ### NOTE: Editor :: QuitApplication         => Event on quit application
    def QuitApplication(self, event):
        """
        """
        # FIXME: Editor :: QuitApplication         => Corrupted file saving crash DEVSimPY

        cp = self.nb.GetCurrentPage()
        if cp.modify:
            ### if no error
            if not cp.ContainError():
                dlg = wx.MessageDialog(self, _('Save before Exit?'), _('Code Editor'), wx.YES_NO | wx.YES_DEFAULT | wx.CANCEL | wx.ICON_QUESTION)
            else:
                dlg = wx.MessageDialog(self, _('File contain errors.\nDo you want to force saving before exit knowing that the file can be corrupts?'), _('Code Editor'), wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL | wx.ICON_QUESTION)
            val = dlg.ShowModal()
            if val == wx.ID_YES:
                self.nb.force_saving = cp.ContainError()
                self.OnSaveFile(event)
                self.nb.force_saving = False
                if not cp.modify:
                    #wx.Exit()
                    dlg.Destroy()
            elif val == wx.ID_CANCEL:
                dlg.Destroy()
            else:
                self.Destroy()
        else:
            self.Destroy()

    ### NOTE: Editor :: OnAbout                 => Event when about button is clicked
    def OnAbout(self, event):
        dial = wx.MessageDialog(self, _('\tPython Code Editor\t\n \tDEVSimPy \t\n'), _('About editor'), wx.OK | wx.ICON_INFORMATION)
        dial.ShowModal()

### ----------------------------------------------------------------
class EditorPanel(Base, wx.Panel):
    """ Editor class which is a Panel.
    """
    
    def __init__(self, parent, id, title):
        """ Constructor.
        """
        
        self.parent = parent
        
        ### call constructor and set background color of panel
        wx.Panel.__init__(self, self.parent, id)
        self.SetBackgroundColour(wx.WHITE)

        ### Define sizer
        sizer = wx.BoxSizer(wx.VERTICAL)

        ### create notebook
        self.nb = EditionNotebook(self, wx.NewIdRef(), style=wx.CLIP_CHILDREN)
        ### create juste for the bind of action (save,...) of the toolbar - Warning it must stay here !
        self.menuBar = self.CreateMenu()
        ### create toolbar
        self.toolbar = self.CreateTB()
        ###recover the statusbar of mainW
        self.statusbar = parent.GetStatusBar()

        ### add toolbar and notebook
        sizer.Add(self.toolbar, 0, wx.ALL | wx.ALIGN_LEFT | wx.EXPAND)
        sizer.Add(self.nb, 1 ,wx.EXPAND)

        ### set sizer and layout of panel
        self.SetSizer(sizer)
        self.SetAutoLayout(True)
    
        Base.__init__(self, parent, id, title)
        
###------------------------------------------------------------
class EditorFrame(Base, wx.Frame):
    """ Editor class which is a Frame
    """

    ### 
    def __init__(self, parent, id, title):
        """ Constructor.
        """

        ### copy
        self.parent = parent
        
        ### call constructor
        wx.Frame.__init__(self, self.parent, id, title, size=(800, 500), style=wx.DEFAULT_FRAME_STYLE|wx.STAY_ON_TOP)

        ### Create notebook
        self.nb = EditionNotebook(self, wx.NewIdRef(), style=wx.CLIP_CHILDREN)

        ### create menu, toolbar and statusbar for the frame
        self.menuBar = self.CreateMenu()
        self.SetMenuBar(self.menuBar)
        self.toolbar = self.CreateTB()
        self.statusbar= self.GetStatusBar()

        ### set the tool bar
        self.SetToolBar(self.toolbar)

        ### binding
        self.Bind(wx.EVT_CLOSE, self.QuitApplication)

        self.Centre()

        ### just for windows
        e = wx.SizeEvent(self.GetSize())
        self.ProcessEvent(e)

        Base.__init__(self, parent, id, title)

class BlockBase(object):
    ### 
    def __init__(self, parent, id, title, block):
        """ Constructor.
        """
        
        self.cb = block
        self.parent = parent

        self.setChoices(block)

    def update(self, concret_subject=None):
        """ Update method that manages the embedded editor depending of the selected model in the canvas.
        """

        state = concret_subject.GetState()
        canvas = state['canvas']
        model = state['model']
        
        ### delete all tab on notebook
        while(self.nb.GetPageCount()):
            self.nb.DeletePage(0)

        ### add behavioral code
        self.AddEditPage(model.label, model.python_path)

        ### add test file
        if hasattr(model, 'GetTestFile'):
            L = model.GetTestFile()
            for i,s in enumerate(os.path.join(model.model_path, l) for l in L):
                self.AddEditPage(L[i], s)

        self.cb = model

        ### update the combo box items depending on the model
        self.setChoices(model)
        self.combo.Clear()
        self.combo.Set([_("Choose to insert in place")]+self.getChoices())
        self.combo.SetSelection(0)

    def setChoices(self,block):
        ### define the choices of combo list for text insert functionality depending on the type of block
        ### choices object is ordderd dict to associate handlers
        if block:
            if not block.isCMD():
                self._choices = collections.OrderedDict([(_('New peek'),self.OnPeek), (_('New all peek'),self.OnAllPeek), 
                    (_('New poke'),self.OnPoke), (_('New hold in state'),self.OnInsertHoldInState), (_('New passivate in state'),self.OnInsertPassivateInState), 
                    (_('New passivate state'),self.OnInsertPassivateState), (_('New Phase test'),self.OnInsertPhaseIs), (_('New debugger stdout'),self.OnInsertDebug), 
                    (_('Get state'),self.OnInsertGetState), (_('Get sigma'),self.OnInsertGetSigma), (_('Get message value'),self.OnInsertGetMsgValue), (_('Get message time'),self.OnInsertGetMsgTime)])
            else:
                if 'PyPDEVS' in builtins.__dict__['DEFAULT_DEVS_DIRNAME']:
                    self._choices = collections.OrderedDict([(_("New add sub model"),self.OnAddModel),(_("New remove sub model"),self.OnRemoveModel),(_("New port connection"),self.OnDisConnectPorts),(_("New port connection"),self.OnConnectPorts),(_('New debugger stdout'),self.OnInsertDebug)])
                else:
                    self._choices = collections.OrderedDict([(_('New debugger stdout'),self.OnInsertDebug)])
        else:
            self._choices = collections.OrderedDict()

    def getChoices(self):
        """ Return the list of choices for text insert function.
        """
        return list(self._choices.keys())

    def OnPeek(self, *args)->None:
        """ Insert the peek statement.
        """
        sins = list(map(str, list(range(self.cb.input if hasattr(self.cb, 'input') else 10))))
        dlg = wx.SingleChoiceDialog(self, _('Port number'), _('Which port?'), sins, wx.CHOICEDLG_STYLE)
        port = dlg.GetStringSelection() if dlg.ShowModal() == wx.ID_OK else None
        dlg.Destroy()

        if port is not None:
            cp = self.nb.GetCurrentPage()
            cp.AddText("self.peek(self.IPorts[%d], *args)" % int(port))
            self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')
    
    def OnPoke(self, *args)->None:
        """Insert the poke statement.
        """
        souts = list(map(str, list(range(self.cb.output if hasattr(self.cb, 'output') else 10))))
        dlg = wx.SingleChoiceDialog(self, _('Port number'), _('Which port?'), souts, wx.CHOICEDLG_STYLE)
        port = dlg.GetStringSelection() if dlg.ShowModal() == wx.ID_OK else None
        dlg.Destroy()

        if port is not None:
            cp = self.nb.GetCurrentPage()
            cp.AddText("return self.poke(self.OPorts[%d], Message(<>, self.timeNext))"%int(port))
            self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnAllPeek(self, *args):
        """ Insert the loop to peek all input ports.
        """

        txt = """
        for p in self.IPorts:
            msg = self.peek(p, *args)
            if msg:
                v = self.getMsgValue(msg)
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText(txt)
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnCombo(self, event):
        """ Combobox for the text insert function.
        """
        choice = event.GetString()
        handler = self._choices[choice]
        ### execute the handler
        handler(event)

    ###
    def OnInsertPeekPoke(self, event):
        """ Event when insert peek or insert poke button is clicked.
        """

        ### selected submenu
        menubar = self.GetMenuBar()
        label = menubar.GetLabel(event.GetId()).lower()
        sins = None

        if "peek" in label:
            sins = list(map(str, list(range(self.cb.input if hasattr(self.cb, 'input') else 10))))
        elif "poke" in label:
            sins = list(map(str, list(range(self.cb.output if hasattr(self.cb, 'output') else 10))))
        else:
            sys.stdout.write(_("function not implemented"))

        if sins:
            dlg = wx.SingleChoiceDialog(self, _('Port number'), _(' %s on which port?')%label, sins, wx.CHOICEDLG_STYLE)
            port = dlg.GetStringSelection() if dlg.ShowModal() == wx.ID_OK else None
            dlg.Destroy()

            if port is not None:
                cp = self.nb.GetCurrentPage()
                if "peek" in label:
                    cp.AddText("self.peek(self.IPorts[%d], *args)"%int(port))
                elif "poke" in label:
                    cp.AddText("return self.poke(self.OPorts[%d], Message(<>, self.timeNext))"%int(port))
                self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertInitPhase(self, event):
        """ Insert a sentence to get the init phase (status and sigma)
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.initPhase(<status>,<sigma>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertSetState(self, event):
        """ Insert a sentence to set the state (status and sigma)
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.setState({'status':'<phase>', 'sigma':<time>})")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertSetStatus(self, event):
        """ Insert a sentence to set the status of the state
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.setStatus('<phase>')")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertSetSigma(self, event):
        """ Insert a sentence to set the sigma of the state
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.setSigma(<time>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertGetState(self, event):
        """ Insert a sentence to get the state object.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.getState()")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertGetStatus(self, event):
        """ Insert a sentence to get the status.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.getStatus()")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertGetSigma(self, event):
        """ Insert a sentence to get the sigma value.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.getSigma()")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertGetPortId(self, event):
        """ Insert a sentence to get the port ID.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.getPortId(<port>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertGetMsgValue(self, event):
        """ Insert a sentence to get the message value.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.getMsgValue(<msg>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')
    
    def OnInsertGetMsgTime(self, event):
        """ Insert a sentence to get the message time.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.getMsgTime(<msg>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    def OnInsertGetElapsed(self, event):
        """ Insert a sentence to get the elapsed time.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.getElapsed()")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def OnInsertHoldInState(self, event):
        """ Insert a sentence to change the state.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.holdIn('<phase>',<sigma>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def OnInsertPhaseIs(self, event):
        """ Insert a sentence to test the phase.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.phaseIs('<phase>')")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')
        
    ###
    def OnInsertPassivateInState(self, event):
        """ Insert a sentence to change the state.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.passivateIn('<phase>')")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def OnInsertPassivateState(self, event):
        """ Insert a sentence to change the state.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.passivate()")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def OnInsertDebug(self, event):
        """ Insert a sentence to invoke the debugger function that trace message into the log of model.
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.debugger('<message>')")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def OnAddModel(self, event):
        """ Insert a sentence to add sub model to coupled model
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.addSubModel(<model>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def OnRemoveModel(self, event):
        """ Insert a sentence to remove model from coupled model
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.removeSubModel(<model>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def OnConnectPorts(self, event):
        """ Insert a sentence to connect the port p1 to the port p2
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.connectPorts(<p1>,<p2>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def OnDisConnectPorts(self, event):
        """ Insert a sentence to disconnect the port p1 form the port p2
        """
        cp = self.nb.GetCurrentPage()
        cp.AddText("self.disconnectPorts(<p1>,<p2>)")
        self.Notification(True, _('%s modified' % (os.path.basename(cp.GetFilename()))), '', '')

    ###
    def ConfigSaving(self, base_name, dir_name, code):
        """ Saving method.
        """

        new_instance = None

        ### if force saving when quitting Editor
        if self.nb.force_saving:
            self.nb.DoSaveFile(code)
        else:
            ### get new instance from text loaded in Editor
            if not base_name.endswith('plugins.py'):
                new_instance = getObjectFromString(code)

            ### typical plugins python file
            else:
                new_instance = isError(code) or None

        return new_instance

    ###
    def UpdateArgs(self, new_args):
        """ Update the args or constructor class from new_args.
        """

        ### update args (behavioral attributes) before saving
        if isinstance(new_args, dict):

            ### add new attributes
            for key, val in list(new_args.items()):
                if key not in self.cb.args:
                    self.cb.args[key]=val

            ### del old attributes
            for key, val in list(self.cb.args.items()):
                if key not in new_args:
                    del self.cb.args[key]

        else:
            ### status bar notification
            self.Notification(False, _('args not updated'),'','')

    ###
    def CheckErrors(self, base_name, code, new_instance):
        """
        """

        if not self.nb.GetCurrentPage().ContainError():
            
            ### if some simulation is running
            on_simulation_flag = True in [_('Simulator') in thread.getName() and (thread.isAlive() if hasattr(thread,'isAlive') else thread.is_alive()) for thread in threading.enumerate()[1:]]

            new_class = new_instance.__class__

            if new_instance:
                import Components
                new_args = Components.GetArgs(new_class)
                self.UpdateArgs(new_args)

            ### user would change the behavior during a simulation without saving
            if on_simulation_flag and new_instance is not bool:

                assert self.cb.getDEVSModel() is not None, _("Close Simulation Diag!")

                self.cb.setDEVSClassModel(new_class)

                if base_name.split('.')[1] == 'py':
                    self.nb.DoSaveFile(code)
                else:
                    ### status bar notification
                    self.Notification(False, _('File not saved during simulation'), _('New class from %s')%(new_class))
            ### save file
            else:
                self.nb.DoSaveFile(code)

        ### some errors in file
        else:
            self.SavingErrors(new_instance)

    ###
    def UpdateModule(self):
        """ Reloading associated module and devs model.
        """

        ### re importation du module de la classe avec verification des erreurs éventuelles dans le code
        if hasattr(self.cb, 'model_path') and zipfile.is_zipfile(self.cb.model_path):
            module_name = self.cb.model_path
        # recuperation du module correspondant à la classe
        else:
            module_name = path_to_module(self.cb.python_path)

        info = ReloadModule.recompile(module_name)

        cp = self.nb.GetCurrentPage()
        cp.error_flag = isinstance(info, (Exception, str))

        if cp.error_flag:
            wx.MessageBox(_('Error saving file:\n%s')%str(info), \
                        "UpdateModule method", \
                        wx.OK | wx.ICON_ERROR)
        else:
            import Components

            classe = Components.GetClass(cp.GetFilename())

            if not isinstance(classe, Exception):

                ### for plugins.py file, i is not a class !
                if inspect.isclass(classe):

                    # get behavioral attribute from python file through constructor class
                    # args must have default value in the constructor
                    try:
                        constructor = inspect.getargspec(classe.__init__)
                        new_args = dict(list(zip(constructor[0][1:], constructor[-1]))) if constructor[-1] else {}
                    except ValueError:
                        constructor = inspect.signature(classe.__init__)
                        parameters = constructor.parameters
                        new_args = {}

                        for name, parameter in parameters.items():
                            if name != 'self':
                                if parameter.default != inspect.Parameter.empty:
                                    new_args[name] = parameter.default
                
                    self.UpdateArgs(new_args)

                    # code update if it was modified during the simulation (out of constructor code,
                    # because we don't re-instanciated the devs model but only change the class reference)
                    devs = self.cb.getDEVSModel()
                    if devs is not None:
                        self.cb.setDEVSClassModel(classe)
                        self.cb.setBlock(devs)
            else:
                wx.MessageBox(_('Error trying to give class: %s\n')%str(classe), \
                            "GetClass Function", \
                            wx.OK | wx.ICON_ERROR)

    ###
    @classmethod
    def __str__(cls):
        """
        """
        attrs = [('cb', 'Block')]
        class_name = "BlockEditor"
        parent = "Editor"
        methods = [
            ('__init__', 'self, parent, ID, title'),
            ('ConfigureGUI', 'self'),
            ('ConfigSaving', 'self, base_name, dir_name, code'),
            ('CheckErrors', 'self, base_name, code, new_instance'),
            ('UpdateModule', 'self'),
            ('OnInsertPeekPoke', 'self, event'),
            ('OnInsertHoldInState', 'self, event')
        ]
        return "\n--------------------------------------------------\
        \n\tClass :\t\t%s\n\n\tInherit from :\t%s\n\n\tAttributes :\t%s\n\n\tMethods :\t%s\n" % (
            class_name, parent, '\n\t\t\t'.join([attr + "\t:: " + typ for attr, typ in attrs]),
            "\n\t\t\t".join([method + "\tparams :: " + params for method, params in methods])
        )

### CodeBlock editor with special submenu---------------------------
class BlockEditorFrame(BlockBase, EditorFrame):
    """ Block Editor class which inherit of Editor class
    """

    ### 
    def __init__(self, parent, id, title, block):
        """ Constructor
        """

        EditorFrame.__init__(self, parent, id, title)
        BlockBase.__init__(self, parent, id, title, block)

        self.block = block

        #if not parent:
        self.SetIcon(self.MakeIcon(wx.Image(os.path.join(ICON_PATH_16_16, 'pythonFile.png'), wx.BITMAP_TYPE_PNG)))
        self.ConfigureGUI()
        
        #else:
        #    ### in panel
        #    self.ConfigureTB()

    ###
    def ConfigureGUI(self):
        """
        """
        ### insert sub menu-------------------------------------------------
        insert = wx.Menu()

        ### sub sub menus
        get_submenu = wx.Menu()
        set_submenu = wx.Menu()
        new_submenu = wx.Menu()

        if not self.block.isCMD():

            ### New items
            peek = wx.MenuItem(new_submenu, wx.NewIdRef(), _('Peek'), _('Generate new peek code'))
            poke = wx.MenuItem(new_submenu, wx.NewIdRef(), _('Poke'), _('Generate new poke code'))
            holdInState = wx.MenuItem(new_submenu, wx.NewIdRef(), _('Hold in state'), _('Generate new hold in state code self.holdIn(...)'))
            phaseIs = wx.MenuItem(new_submenu, wx.NewIdRef(), _('PhaseIs test'), _('Generate phase test code self.phaseIs(...)'))
            passivateInState = wx.MenuItem(new_submenu, wx.NewIdRef(), _('Passivate in state'), _('Generate new passivate in state code self.passivateIn(...)'))
            passivateState = wx.MenuItem(new_submenu, wx.NewIdRef(), _('Passivate state'), _('Generate new passivate state code self.passivate(...)'))

            ### getter items
            getPortId = wx.MenuItem(get_submenu, wx.NewIdRef(), _('Port Id'), _('Get the port ID from port instance (self.getPortId(port)->int)'))
            getMsgValue = wx.MenuItem(get_submenu, wx.NewIdRef(), _('Message value'), _('Get message value (self.getMsgValue(msg)->Object)'))
            getMsgTime = wx.MenuItem(get_submenu, wx.NewIdRef(), _('Message time'), _('Get message time (self.getMsgTime(msg)->Object)'))
            getSigma = wx.MenuItem(get_submenu, wx.NewIdRef(), _('Sigma value'), _('Get sigma value (self.getSigma()->float)'))
            getStatus = wx.MenuItem(get_submenu, wx.NewIdRef(), _('Status value'), _('Get status value (self.getStatus()->str)'))
            getState = wx.MenuItem(get_submenu, wx.NewIdRef(), _('State object'), _('Get state object (self.getState()->dict)'))
            getElapsed = wx.MenuItem(get_submenu, wx.NewIdRef(), _('Elapsed time'), _('Get the elapsed time (self.elapsed)'))

            ### setter items
            setInitPhase = wx.MenuItem(set_submenu, wx.NewIdRef(), _('Init phase'), _('Set the phase (self.getInitPhase())'))
            setStatus = wx.MenuItem(set_submenu, wx.NewIdRef(), _('Status value'), _("Set status value (self.setStatus('IDLE'))"))
            setState = wx.MenuItem(set_submenu, wx.NewIdRef(), _('State object'), _("Set state object (self.setState({'status':'IDLE', 'sigma':0.0}))"))
            setSigma = wx.MenuItem(set_submenu, wx.NewIdRef(), _('Sigma value'), _('Set sigma value (self.setSigma(0.0))'))

            peek.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH_16_16,'peek.png')))
            poke.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH_16_16,'poke.png')))
            # getPortId.SetBitmap()
            # getMsgValue.SetBitmap()
            # getMsgTime.SetBitmap()
            # setInitPhase.SetBitmap()
            # getSigma.SetBitmap()
            # getStatus.SetBitmap()
            # getState.SetBitmap()
            # holdInState.SetBitmap()
            # phaseIs.SetBitmap()
            # passivateInState.SetBitmap()
            # passivateState.SetBitmap()

            new_submenu.Append(peek)
            new_submenu.Append(poke)
            new_submenu.AppendSeparator()
            new_submenu.Append(holdInState)
            new_submenu.Append(phaseIs)
            new_submenu.Append(passivateInState)
            new_submenu.Append(passivateState)
            new_submenu.AppendSeparator()

            set_submenu.Append(setInitPhase)
            set_submenu.Append(setStatus)
            set_submenu.Append(setState)
            set_submenu.Append(setSigma)

            get_submenu.Append(getState)
            get_submenu.Append(getSigma)
            get_submenu.Append(getStatus)
            get_submenu.Append(getPortId)
            get_submenu.Append(getMsgValue)
            get_submenu.Append(getMsgTime)    
            get_submenu.Append(getElapsed)

        debug = wx.MenuItem(new_submenu, wx.NewIdRef(), _('Debugger'), _('Generate new debugger code (print into the log of model)'))
        debug.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH_16_16,'debugger.png')))
        new_submenu.Append(debug)

        menu = self.GetMenuBar().GetMenu(1)
        menu.Prepend(wx.NewIdRef(), _("Insert"), insert)
        insert.Append(wx.NewIdRef(), _("New"), new_submenu)
        insert.Append(wx.NewIdRef(), _("Set"), set_submenu)
        insert.Append(wx.NewIdRef(), _("Get"), get_submenu)

        ### -------------------------------------------------------------------

        ### insert new icon in toolbar (icon are not available in embeded editor (Show menu)
        tb = self.GetToolBar()
        
        tb.AddSeparator()
        #tb.InsertSeparator(tb.GetToolsCount())

        ### combo to insert tips text
        cbID = wx.NewIdRef()
        tb.AddControl(wx.ComboBox(tb, cbID, _("Choose to insert in place"), choices=self.getChoices(),size=(160,-1), style=wx.CB_DROPDOWN))
        
        ### search text box 
        tb.AddStretchableSpace()
        finddlg = TestSearchCtrl(tb, size=(150,-1), doSearch=self.DoSearch)
        tb.AddControl(finddlg)

        try:
            tb.Realize()
        except:
            print("Toolbar not displayed on mac...")
            pass

        if not self.block.isCMD():
            self.Bind(wx.EVT_MENU, self.OnInsertPeekPoke, id=peek.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertPeekPoke, id=poke.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertHoldInState, id=holdInState.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertPhaseIs, id=phaseIs.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertPassivateInState, id=passivateInState.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertPassivateState, id=passivateState.GetId())

            self.Bind(wx.EVT_MENU, self.OnInsertGetState, id=getState.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertGetStatus, id=getStatus.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertGetSigma, id=getSigma.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertGetPortId, id=getPortId.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertGetMsgValue, id=getMsgValue.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertGetMsgTime, id=getMsgTime.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertGetElapsed, id=getElapsed.GetId())
            
            self.Bind(wx.EVT_MENU, self.OnInsertInitPhase, id=setInitPhase.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertSetState, id=setState.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertSetSigma, id=setSigma.GetId())
            self.Bind(wx.EVT_MENU, self.OnInsertSetStatus, id=setStatus.GetId())

        self.Bind(wx.EVT_COMBOBOX, self.OnCombo, id=cbID)
        self.Bind(wx.EVT_MENU, self.OnInsertDebug, id=debug.GetId())
        
class BlockEditorPanel(BlockBase, EditorPanel):
    """ Block Editor class which inherit of Editor class
    """

    ### 
    def __init__(self, parent, id, title, block):
        """ Constructor
        """

        EditorPanel.__init__(self, parent, id, title)
        BlockBase.__init__(self, parent, id, title, block)
        
        #if not parent:
            #self.SetIcon(self.MakeIcon(wx.Image(os.path.join(ICON_PATH_16_16, 'pythonFile.png'), wx.BITMAP_TYPE_PNG)))
            #self.ConfigureGUI()
        #else:
        #    ### in panel
        self.ConfigureTB()

    def ConfigureTB(self):
        """
        """
        id = [wx.NewIdRef()]*4
        self.toolbar.InsertSeparator(self.toolbar.GetToolsCount())

        ### combo to insert tips text
        cbID = wx.NewIdRef()
        self.combo = wx.ComboBox(self.toolbar, cbID, _("Choose to insert in place"), choices=self.getChoices(),size=(160,-1), style=wx.CB_DROPDOWN)

        self.toolbar.AddControl(self.combo)
        
        ### search text box 
        self.toolbar.AddStretchableSpace()
        self.finddlg = TestSearchCtrl(self.toolbar, size=(150,-1), doSearch=self.DoSearch)
        self.toolbar.AddControl(self.finddlg)

        # if wx.VERSION_STRING < '4.0':
        #     self.toolbar.AddTool(id[0], wx.Bitmap(os.path.join(ICON_PATH_16_16,'peek.png')),shortHelpString=_('New peek'), longHelpString=_('Insert a code for a new peek'))
        #     self.toolbar.AddTool(id[1], wx.Bitmap(os.path.join(ICON_PATH_16_16,'poke.png')),shortHelpString=_('New poke'), longHelpString=_('Insert a code for a new poke'))
        #     self.toolbar.AddTool(id[2], wx.Bitmap(os.path.join(ICON_PATH_16_16,'new_state.png')),shortHelpString=_('New hold in state'), longHelpString=_('Insert a code for a new hold in state'))
        #     self.toolbar.AddTool(id[3], wx.Bitmap(os.path.join(ICON_PATH_16_16,'debugger.png')),shortHelpString=_('New debugger'), longHelpString=_('Insert a code for print information into the log of model'))
        # else:
        #     self.toolbar.AddTool(id[0], "",wx.Bitmap(os.path.join(ICON_PATH_16_16,'peek.png')),shortHelp=_('New peek'))
        #     self.toolbar.AddTool(id[1], "",wx.Bitmap(os.path.join(ICON_PATH_16_16,'poke.png')),shortHelp=_('New poke'))
        #     self.toolbar.AddTool(id[2], "",wx.Bitmap(os.path.join(ICON_PATH_16_16,'new_state.png')),shortHelp=_('New hold in state'))
        #     self.toolbar.AddTool(id[3], "",wx.Bitmap(os.path.join(ICON_PATH_16_16,'debugger.png')),shortHelp=_('New debugger'))
        
        self.toolbar.Realize()

        self.Bind(wx.EVT_MENU, self.OnInsertPeekPoke, id=id[0])
        self.Bind(wx.EVT_MENU, self.OnInsertPeekPoke, id=id[1])
        self.Bind(wx.EVT_MENU, self.OnInsertHoldInState, id=id[2])
        self.Bind(wx.EVT_MENU, self.OnInsertDebug, id=id[3])

        self.Bind(wx.EVT_COMBOBOX, self.OnCombo, id=cbID)

### factory function for BlockEditor
def BlockEditor(*args):
    parent = args[0]
    if not parent:
        return BlockEditorFrame(*args)
    else:
        return BlockEditorPanel(*args)
    
### Edition of any files with notebook------------------------------
# NOTE: TestEditor << Editor :: Specific editor for tests files
class TestEditor(EditorFrame):
    """ Test Editor class wich inherit of Editor class
    """

    # NOTE: TestEditor :: contructor         => __init__(self, parent, id, title, feature_path, steps_path)
    def __init__(self, parent, id, title):
        """Constructor.
        """

        EditorFrame.__init__(self, parent, id, title)

        if not parent:
            self.SetIcon(self.MakeIcon(wx.Image(os.path.join(ICON_PATH, 'iconDEVSimPy.png'), wx.BITMAP_TYPE_PNG)))
            self.ConfigureGUI()

    # NOTE: TestEditor :: __str__        => String representation of the class
    @classmethod
    def __str__(cls):
        """
        """
        attrs = []
        class_name = "TestEditor"
        parent = "Editor"
        methods = [
            ('__init__', 'self, parent, id, title, feature_path, steps_path'),
            ('ConfigureGUI', 'self'),
            ('OnFeatureSkeleton', 'self, event'),
            ('OnStepsSkeleton', 'self, event'),
            ('OnHeaderGeneration', 'self, event')
        ]
        return "\n--------------------------------------------------\
        \n\tClass :\t\t%s\n\n\tInherit from :\t%s\n\n\tAttributes :\t%s\n\n\tMethods :\t%s\n" % (
            class_name, parent, '\n\t\t\t'.join([attr + "\t:: " + typ for attr, typ in attrs]),
            "\n\t\t\t".join([method + "\tparams :: " + params for method, params in methods])
        )

    # NOTE: TestEditor :: ConfigureGUI         => Configure the interface for tests edition
    def ConfigureGUI(self):
        """
        """
        ### insert sub menu-------------------------------------------------
        insert = wx.Menu()

        feature = wx.MenuItem(insert, wx.NewIdRef(), _('Feature skeleton'), _('Generate feature skeleton'))
        steps = wx.MenuItem(insert, wx.NewIdRef(), _('Steps skeleton'), _('Generate steps skeleton'))
        env_header = wx.MenuItem(insert, wx.NewIdRef(), _('Environment header generation'), _('Generate environment header'))
        env_gen_def = wx.MenuItem(insert, wx.NewIdRef(), _('Environment methods generation'), _('Generate minimal methods for environment'))
        # env_spec_def = wx.MenuItem(insert, wx.NewIdRef(), _('Specific environment methods generation'), _('Generate minimal methods for specific environment'))

        if wx.VERSION_STRING < '4.0':
            insert.AppendItem(feature)
            insert.AppendItem(steps)
            insert.AppendItem(env_header)
            insert.AppendItem(env_gen_def)
            # insert.AppendItem(env_spec_def)
        else:
            insert.Append(feature)
            insert.Append(steps)
            insert.Append(env_header)
            insert.Append(env_gen_def)
            # insert.Append(env_spec_def)

        menu = self.GetMenuBar().GetMenu(1)
        if wx.VERSION_STRING < '4.0':
            menu.PrependMenu(wx.NewIdRef(), _("Insert"), insert)
        else:
            menu.Prepend(wx.NewIdRef(), _("Insert"), insert)
        ### ----------------------------------------------------------------

        ### Bind all new event----------------------------------------------
        self.Bind(wx.EVT_MENU, self.OnFeatureSkeleton, id=feature.GetId())
        self.Bind(wx.EVT_MENU, self.OnStepsSkeleton, id=steps.GetId())
        self.Bind(wx.EVT_MENU, self.OnHeaderGeneration, id=env_header.GetId())
        self.Bind(wx.EVT_MENU, self.OnEnvDefGeneration, id=env_gen_def.GetId())
        # self.Bind(wx.EVT_MENU, self.OnSpecEnvDefGeneration, id=env_spec_def.GetId())

    ### ----------------------------------------------------------------

    # NOTE: TestEditor :: OnFeatureSkeleton => Event when insert feature skeleton button is clicked
    def OnFeatureSkeleton(self, event):
        FEATURE_SKELETON = "Feature: # Feature description\n\tScenario: # Scenario description\n\t\tGiven # Context\n\t\tWhen # Event\n\t\tThen # Assertions"
        self.nb.GetCurrentPage().AddText(FEATURE_SKELETON)

    # NOTE: TestEditor :: OnStepsSkeleton     => Event when insert steps skeleton button is clicked
    def OnStepsSkeleton(self, event):
        STEP_FUNCTION = 'def step(context):\n\tpass\n'
        STEPS_SKELETON = "from behave import *\n\n@given('your given text')\n" + STEP_FUNCTION + "\n@when('your event text')\n" + STEP_FUNCTION + "\n@then('yout assertions text')\n" + STEP_FUNCTION
        self.nb.GetCurrentPage().AddText(STEPS_SKELETON)

    # NOTE: TestEditor :: OnHeaderGeneration        => note
    def OnHeaderGeneration(self, event):
        HEADER = """
import os\nimport builtins\nimport sys\nimport pickle\nfrom tempfile import gettempdir\nABS_PATH = '%s'\nsys.path.append(ABS_PATH)
builtins.__dict__['HOME_PATH'] = ABS_PATH\nbuiltins.__dict__['DOMAIN_PATH'] = os.path.join(ABS_PATH, 'Domain')
builtins.__dict__['GUI_FLAG'] = True\n\nsys.path.append(os.path.join(gettempdir(), "AtomicDEVS"))\n\nmodels = {}
""" % HOME_PATH
        self.nb.GetCurrentPage().AddText(HEADER)

    # NOTE: TestEditor :: OnGenEnvDefGeneration        => note
    def OnEnvDefGeneration(self, event):
        GEN_ENV_DEF = """import re\n
# NOTE: environment.py :: addModel        => note
def loadModel():\n\tglobal models\n\tfiles = [os.path.join(gettempdir(), f) for f in os.listdir(gettempdir()) if re.match('^AtomicModel_.*\.{1}serial$', f)]\n\tfor f in files:\n\t\tm = pickle.load(open(f, "rb"))
\t\tif not m.blockModel.label in models.keys():\n\t\t\tmodels[m.blockModel.label] = m\n
def before_all(context):\n\tglobal models\n\tloadModel()\n\tcontext.models = models\n
"""
        self.nb.GetCurrentPage().AddText(GEN_ENV_DEF)

    # NOTE: TestEditor :: OnEnvironmentSkeleton        => note
#     def OnSpecEnvDefGeneration(self, event):
#         SPEC_ENV_DEF = """
# # NOTE: environment.py :: addModel        => note
# def loadModel():\n\tglobal models\n\tm = pickle.load(open(os.path.join(gettempdir(), "AtomicModel_'your_model_label'.serial"), "rb"))\n\tif not m.blockModel.label in models.keys():\n\t\tmodels[m.blockModel.label] = m\n
# \ndef before_all(context):\n\tglobal models\n\tloadModel()\n\tcontext.models = models\n
# """
#         self.nb.GetCurrentPage().AddTextUTF8(SPEC_ENV_DEF)

### ----------------------------------------------------------------
### NOTE: GeneralEditor << Editor :: Editor for simple files like text files
class GeneralEditor(EditorFrame):
    """ General Editor class which inherit of Editor class
    """

    ### NOTE: GeneralEditor :: constructor     => __init__(self, parent, id, title)
    def __init__(self, parent, id, title):
        """ Constructor.
        """

        ### call constructor of parent
        EditorFrame.__init__(self, parent, id, title)

        self.title = title

        ### if not parent, we configure a frame
        if not parent:
            self.SetIcon(self.MakeIcon(wx.Image(os.path.join(ICON_PATH, 'iconDEVSimPy.png'), wx.BITMAP_TYPE_PNG)))
            self.ConfigureGUI()

    # NOTE: GeneralEditor :: __str__        => String representation of the class
    @classmethod
    def __str__(cls):
        """
        """
        attrs = []
        class_name = "GeneralEditor"
        parent = "Editor"
        methods = [
            ('__init__', 'self, parent, id, title'),
            ('ConfigureGUI', 'self'),
            ('OnAddPage', 'self, event'),
            ('OnClosePage', 'self, event')
        ]
        return "\n--------------------------------------------------\
        \n\tClass :\t\t%s\n\n\tInherit from :\t%s\n\n\tAttributes :\t%s\n\n\tMethods :\t%s\n" % (
            class_name, parent, '\n\t\t\t'.join([attr + "\t:: " + typ for attr, typ in attrs]),
            "\n\t\t\t".join([method + "\tparams :: " + params for method, params in methods])
        )

    ### NOTE: GeneralEditor :: ConfigureGUI     => Configure the interface by default
    def ConfigureGUI(self):
        """
        """

        ### AddPage button in toolbar---------------------------------------
        filemenu = self.GetMenuBar().GetMenu(0)
        add = wx.MenuItem(filemenu, wx.NewIdRef(), _('&Add'), _('Add new page'))
        close = wx.MenuItem(filemenu, wx.NewIdRef(), _('&Close'), _('Close current page'))
        ### ----------------------------------------------------------------
        ### Construct new toolbar-------------------------------------------
        self.toolbar.AddSeparator()
        ### ----------------------------------------------------------------
        ### Bind all new event----------------------------------------------
        self.Bind(wx.EVT_TOOL, self.OnAddPage, self.toolbar.AddSimpleTool(add.GetId(), wx.Bitmap(os.path.join(ICON_PATH, 'new.png')), _('Add'), ''))
        self.Bind(wx.EVT_TOOL, self.OnClosePage, self.toolbar.AddSimpleTool(close.GetId(), wx.Bitmap(os.path.join(ICON_PATH, 'close.png')), _('Close'), ''))
        ### ----------------------------------------------------------------
        self.toolbar.Realize()

    ### NOTE: GeneralEditor :: OnAddPage         => Event when Add page button is clicked
    def OnAddPage(self, event):
        """ Add page to the notebook
        """

        self.nb.AddEditPage(_("New File"), '')

    ### NOTE: GeneralEditor :: OnClosePage     => Event when close page button is clicked
    def OnClosePage(self, event):
        """ Close button has been invoked
        """

        if self.nb.GetPageCount() > 1:

            id = self.nb.GetSelection()
            page = self.nb.GetPage(id)

            if page.IsModified():
                dlg = wx.MessageDialog(self, _('%s\nSave changes to the current diagram?')%(self.title), _('Save'), wx.YES_NO | wx.YES_DEFAULT | wx.CANCEL |wx.ICON_QUESTION)
                val = dlg.ShowModal()
                if val == wx.ID_YES:
                    self.OnSaveFile(event)
                elif val == wx.ID_NO:
                    self.nb.OnClosePage(event, id)
                else:
                    dlg.Destroy()
                    return False

                dlg.Destroy()

            else:
                self.nb.OnClosePage(event, id)

        return True

### -----------------------------------------------------------------------------------------------
class TestApp(wx.App):
    """ Testing application
    """

    def OnInit(self):
        import gettext

        builtins.__dict__['HOME_PATH'] = os.getcwd()
        builtins.__dict__['ICON_PATH'] = os.path.join('icons')
        builtins.__dict__['ICON_PATH_16_16'] = os.path.join(ICON_PATH, '16x16')

        builtins.__dict__['_'] = gettext.gettext

        fn = os.path.join(os.path.realpath(gettempdir()), 'test.py')
        with open(fn, 'w') as f:
            f.write("Hello world !")

        frame1 = GetEditor(None, -1, 'Test1')
        frame1.AddEditPage("Hello world", fn)
        frame1.SetPosition((100, 100))
        frame1.Show()

        frame2 = GetEditor(None, -1, 'Test2', file_type='test')
        frame2.AddEditPage("Hello world", fn)
        frame2.AddEditPage("Hello world", fn)
        frame2.SetPosition((200, 200))
        frame2.Show()

        frame3 = GetEditor(None, -1, 'Test3', None, file_type='block')
        frame3.AddEditPage("Hello world", fn)
        frame3.SetPosition((300, 300))
        frame3.Show()

        return True

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


### -----------------------------------------------------------------------------------------------
def manager(args):
    os.system(['clear', 'cls'][os.name == 'nt'])
    if args.start:
        start()
    if args.info:
        info()


def info():
    sys.stdout.write(PythonSTC.__str__())
    sys.stdout.write(CodeEditor.__str__())
    sys.stdout.write(EditionFile.__str__())
    sys.stdout.write(EditionNotebook.__str__())
    sys.stdout.write(Editor.__str__())
    sys.stdout.write(BlockEditor.__str__())
    sys.stdout.write(TestEditor.__str__())
    sys.stdout.write(GeneralEditor.__str__())


def start():
    app = TestApp(0)
    app.MainLoop()


def main():
    parser = argparse.ArgumentParser(description='Text Editor for DEVSimPy')

    ### Class info---------------------------------------------------------------------------------
    parser.add_argument('-c', '--class-info', action="store_true", dest="info", help='Show __str__ for each class')

    ### Start App----------------------------------------------------------------------------------
    parser.add_argument('-s', '--start', action="store_true", dest="start", help='Start testing app')

    args = parser.parse_args()
    manager(args)


if __name__ == '__main__':
    import argparse

    main()