objdictgen/subindextable.py
author Robert Lehmann <robert.lehmann@sitec-systems.de>
Tue, 28 Jul 2015 16:36:55 +0200
changeset 793 72e9e1064432
parent 789 a82d867e7850
permissions -rw-r--r--
timers_unix: Fix termination problem of WaitReceiveTaskEnd

The function pthread_kill sends the Signal thread and to the own process.
If you use this construct than the application which calls uses the
canfestival api will terminate at the call of canClose. To avoid that
use pthread_cancel instead of pthread_kill. To use the pthread_cancel call
you need to set the cancel ability in the thread function. That means
you need to call pthread_setcancelstate and pthread_setcanceltype.
For the termination of the thread at any time it is important to set the
cancel type to PTHREAD_CANCEL_ASYNCHRONOUS.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#This file is part of CanFestival, a library implementing CanOpen Stack. 
#
#Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD
#
#See COPYING file for copyrights details.
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#Lesser General Public License for more details.
#
#You should have received a copy of the GNU Lesser General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import wx
import wx.grid

from types import *
from commondialogs import *

from node import OD_Subindex, OD_MultipleSubindexes, OD_IdenticalSubindexes, OD_IdenticalIndexes

ColSizes = [75, 250, 150, 125, 100, 60, 250]
ColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_CENTER, wx.ALIGN_RIGHT, wx.ALIGN_CENTER, wx.ALIGN_CENTER, wx.ALIGN_LEFT]

def GetAccessList(write=True):
    _ = lambda x : x
    if write:
        return [_("Read Only"), _("Write Only"), _("Read/Write")]
    return [_("Read Only"), _("Read/Write")]
AccessList = ",".join(map(_, GetAccessList()))
RAccessList = ",".join(map(_, GetAccessList(False)))
ACCESS_LIST_DICT = dict([(_(access), access) for access in GetAccessList()])

def GetBoolList():
    _ = lambda x : x
    return [_("True"), _("False")]
BoolList = ",".join(map(_, GetBoolList()))
BOOL_LIST_DICT = dict([(_(bool), bool) for bool in GetBoolList()])

def GetOptionList():
    _ = lambda x : x
    return [_("Yes"), _("No")]
OptionList = ",".join(map(_, GetOptionList()))
OPTION_LIST_DICT = dict([(_(option), option) for option in GetOptionList()])

[USER_TYPE, SDO_SERVER, SDO_CLIENT, 
 PDO_TRANSMIT, PDO_RECEIVE, MAP_VARIABLE] = range(6)

INDEXCHOICE_OPTIONS = {
    USER_TYPE: (_("User Type"), 0, "AddUserType"), 
    SDO_SERVER: (_("SDO Server"), 1, "AddSDOServerToCurrent"),
    SDO_CLIENT: (_("SDO Client"), 1, "AddSDOClientToCurrent"),
    PDO_RECEIVE: (_("PDO Receive"), 1, "AddPDOReceiveToCurrent"),
    PDO_TRANSMIT: (_("PDO Transmit"), 1, "AddPDOTransmitToCurrent"),
    MAP_VARIABLE: (_("Map Variable"), 0, "AddMapVariable")
}

INDEXCHOICE_OPTIONS_DICT = dict([(translation, option) for option, (translation, object, function) in INDEXCHOICE_OPTIONS.iteritems()])

INDEXCHOICE_SECTIONS = {
    0 : [USER_TYPE],
    2 : [SDO_SERVER, SDO_CLIENT],
    3 : [PDO_RECEIVE],
    4 : [PDO_RECEIVE],
    5 : [PDO_TRANSMIT],
    6 : [PDO_TRANSMIT],
    8 : [MAP_VARIABLE],
}

def GetSubindexTableColnames():
    _ = lambda x : x
    return [_("subindex"), _("name"), _("type"), _("value"), _("access"), _("save"), _("comment")]

DictionaryOrganisation = [
    {"minIndex" : 0x0001, "maxIndex" : 0x0FFF, "name" : "Data Type Definitions"},
    {"minIndex" : 0x1000, "maxIndex" : 0x1029, "name" : "Communication Parameters"},
    {"minIndex" : 0x1200, "maxIndex" : 0x12FF, "name" : "SDO Parameters"},
    {"minIndex" : 0x1400, "maxIndex" : 0x15FF, "name" : "Receive PDO Parameters"},
    {"minIndex" : 0x1600, "maxIndex" : 0x17FF, "name" : "Receive PDO Mapping"},
    {"minIndex" : 0x1800, "maxIndex" : 0x19FF, "name" : "Transmit PDO Parameters"},
    {"minIndex" : 0x1A00, "maxIndex" : 0x1BFF, "name" : "Transmit PDO Mapping"},
    {"minIndex" : 0x1C00, "maxIndex" : 0x1FFF, "name" : "Other Communication Parameters"},
    {"minIndex" : 0x2000, "maxIndex" : 0x5FFF, "name" : "Manufacturer Specific"},
    {"minIndex" : 0x6000, "maxIndex" : 0x9FFF, "name" : "Standardized Device Profile"},
    {"minIndex" : 0xA000, "maxIndex" : 0xBFFF, "name" : "Standardized Interface Profile"}]

IECTypeConversion = {
    "BOOLEAN": "BOOL",
    "INTEGER8": "SINT",
    "INTEGER16": "INT",
    "INTEGER32": "DINT",
    "UNSIGNED8": "USINT",
    "UNSIGNED16": "UINT",
    "UNSIGNED32": "UDINT",
    "REAL32": "REAL",
    "VISIBLE_STRING": "STRING",
    "OCTET_STRING": "STRING",
    "UNICODE_STRING": "WSTRING",
    "DOMAIN": "STRING",
    "INTEGER24": "DINT",
    "REAL64": "LREAL",
    "INTEGER40": "LINT",
    "INTEGER48": "LINT",
    "INTEGER56": "LINT",
    "INTEGER64": "LINT",
    "UNSIGNED24": "UDINT",
    "UNSIGNED40": "ULINT",
    "UNSIGNED48": "ULINT",
    "UNSIGNED56": "ULINT",
    "UNSIGNED64": "ULINT",
}
SizeConversion = {1 : "X", 8 : "B", 16 : "W", 24 : "D", 32 : "D", 40 : "L", 48 : "L", 56 : "L", 64 : "L"}

class SubindexTable(wx.grid.PyGridTableBase):
    
    """
    A custom wxGrid Table using user supplied data
    """
    def __init__(self, parent, data, editors, colnames):
        # The base class must be initialized *first*
        wx.grid.PyGridTableBase.__init__(self)
        self.data = data
        self.editors = editors
        self.CurrentIndex = 0
        self.colnames = colnames
        self.Parent = parent
        self.Editable = True
        # XXX
        # we need to store the row length and collength to
        # see if the table has changed size
        self._rows = self.GetNumberRows()
        self._cols = self.GetNumberCols()
    
    def Disable(self):
        self.Editable = False
        
    def Enable(self):
        self.Editable = True
    
    def GetNumberCols(self):
        return len(self.colnames)
        
    def GetNumberRows(self):
        return len(self.data)

    def GetColLabelValue(self, col, translate=True):
        if col < len(self.colnames):
            if translate:
                return _(self.colnames[col])
            return self.colnames[col]

    def GetRowLabelValues(self, row, translate=True):
        return row

    def GetValue(self, row, col, translate=True):
        if row < self.GetNumberRows():
            colname = self.GetColLabelValue(col, False)
            value = unicode(self.data[row].get(colname, ""))
            if translate and (colname == "access" or 
                              self.editors[row][colname] in ["bool", "option"] or
                              self.editors[row][colname] == "map" and value == "None"):
                value = _(value)
            return value
            
    def GetEditor(self, row, col):
        if row < self.GetNumberRows():
            return self.editors[row].get(self.GetColLabelValue(col, False), "")
    
    def GetValueByName(self, row, colname):
        return self.data[row].get(colname)

    def SetValue(self, row, col, value):
        if col < len(self.colnames):
            colname = self.GetColLabelValue(col, False)
            if colname == "access":
                value = ACCESS_LIST_DICT[value]
            elif self.editors[row][colname] == "bool":
                value = BOOL_LIST_DICT[value]
            elif self.editors[row][colname] == "option":
                value = OPTION_LIST_DICT[value]
            elif self.editors[row][colname] == "map" and value == _("None"):
                value = "None"
            self.data[row][colname] = value
        
    def ResetView(self, grid):
        """
        (wx.grid.Grid) -> Reset the grid view.   Call this to
        update the grid if rows and columns have been added or deleted
        """
        grid.BeginBatch()
        for current, new, delmsg, addmsg in [
            (self._rows, self.GetNumberRows(), wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
            (self._cols, self.GetNumberCols(), wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
        ]:
            if new < current:
                msg = wx.grid.GridTableMessage(self,delmsg,new,current-new)
                grid.ProcessTableMessage(msg)
            elif new > current:
                msg = wx.grid.GridTableMessage(self,addmsg,new-current)
                grid.ProcessTableMessage(msg)
                self.UpdateValues(grid)
        grid.EndBatch()

        self._rows = self.GetNumberRows()
        self._cols = self.GetNumberCols()
        # update the column rendering scheme
        self._updateColAttrs(grid)

        # update the scrollbars and the displayed part of the grid
        grid.AdjustScrollbars()
        grid.ForceRefresh()


    def UpdateValues(self, grid):
        """Update all displayed values"""
        # This sends an event to the grid table to update all of the values
        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
        grid.ProcessTableMessage(msg)

    def _updateColAttrs(self, grid):
        """
        wx.grid.Grid -> update the column attributes to add the
        appropriate renderer given the column name.

        Otherwise default to the default renderer.
        """
        
        for col in range(self.GetNumberCols()):
            attr = wx.grid.GridCellAttr()
            attr.SetAlignment(ColAlignements[col], wx.ALIGN_CENTRE)
            grid.SetColAttr(col, attr)
            grid.SetColMinimalWidth(col, ColSizes[col])
            grid.AutoSizeColumn(col, False)
        
        typelist = None
        maplist = None
        for row in range(self.GetNumberRows()):
            editors = self.editors[row]
            if wx.Platform == '__WXMSW__':
                grid.SetRowMinimalHeight(row, 20)
            else:
                grid.SetRowMinimalHeight(row, 28)
            grid.AutoSizeRow(row, False)
            for col in range(self.GetNumberCols()):
                editor = None
                renderer = None
                
                colname = self.GetColLabelValue(col, False)
                editortype = editors[colname]
                if editortype == "dcf":
                    editor = wx.grid.GridCellTextEditor()
                    renderer = wx.grid.GridCellStringRenderer()
                elif editortype and self.Editable:
                    grid.SetReadOnly(row, col, False)
                    if editortype == "string":
                        editor = wx.grid.GridCellTextEditor()
                        renderer = wx.grid.GridCellStringRenderer()
                        if colname == "value" and "length" in editors:
                            editor.SetParameters(editors["length"]) 
                    elif editortype == "number":
                        editor = wx.grid.GridCellNumberEditor()
                        renderer = wx.grid.GridCellNumberRenderer()
                        if colname == "value" and "min" in editors and "max" in editors:
                            editor.SetParameters("%s,%s"%(editors["min"],editors["max"]))
                    elif editortype == "float":
                        editor = wx.grid.GridCellTextEditor()
                        renderer = wx.grid.GridCellStringRenderer()
                    elif editortype == "bool":
                        editor = wx.grid.GridCellChoiceEditor()
                        editor.SetParameters(BoolList)
                    elif editortype == "access":
                        editor = wx.grid.GridCellChoiceEditor()
                        editor.SetParameters(AccessList)
                    elif editortype == "raccess":
                        editor = wx.grid.GridCellChoiceEditor()
                        editor.SetParameters(RAccessList)
                    elif editortype == "option":
                        editor = wx.grid.GridCellChoiceEditor()
                        editor.SetParameters(OptionList)
                    elif editortype == "type":
                        editor = wx.grid.GridCellChoiceEditor()
                        if typelist == None:
                            typelist = self.Parent.Manager.GetCurrentTypeList()
                        editor.SetParameters(typelist)
                    elif editortype == "map":
                        editor = wx.grid.GridCellChoiceEditor()
                        if maplist == None:
                            maplist = self.Parent.Manager.GetCurrentMapList()
                        editor.SetParameters(maplist)
                    elif editortype == "time":
                        editor = wx.grid.GridCellTextEditor()
                        renderer = wx.grid.GridCellStringRenderer()
                    elif editortype == "domain":
                        editor = wx.grid.GridCellTextEditor()
                        renderer = wx.grid.GridCellStringRenderer()
                else:
                    grid.SetReadOnly(row, col, True)
                    
                grid.SetCellEditor(row, col, editor)
                grid.SetCellRenderer(row, col, renderer)
                
                grid.SetCellBackgroundColour(row, col, wx.WHITE)
    
    def SetData(self, data):
        self.data = data
        
    def SetEditors(self, editors):
        self.editors = editors
    
    def GetCurrentIndex(self):
        return self.CurrentIndex
    
    def SetCurrentIndex(self, index):
        self.CurrentIndex = index
    
    def AppendRow(self, row_content):
        self.data.append(row_content)

    def Empty(self):
        self.data = []
        self.editors = []

[ID_EDITINGPANEL, ID_EDITINGPANELADDBUTTON, ID_EDITINGPANELINDEXCHOICE, 
 ID_EDITINGPANELINDEXLIST, ID_EDITINGPANELINDEXLISTPANEL, ID_EDITINGPANELPARTLIST, 
 ID_EDITINGPANELSECONDSPLITTER, ID_EDITINGPANELSUBINDEXGRID,
 ID_EDITINGPANELSUBINDEXGRIDPANEL, ID_EDITINGPANELCALLBACKCHECK,
] = [wx.NewId() for _init_ctrls in range(10)]

[ID_EDITINGPANELINDEXLISTMENUITEMS0, ID_EDITINGPANELINDEXLISTMENUITEMS1, 
 ID_EDITINGPANELINDEXLISTMENUITEMS2, 
] = [wx.NewId() for _init_coll_IndexListMenu_Items in range(3)]

[ID_EDITINGPANELMENU1ITEMS0, ID_EDITINGPANELMENU1ITEMS1, 
 ID_EDITINGPANELMENU1ITEMS3, ID_EDITINGPANELMENU1ITEMS4, 
] = [wx.NewId() for _init_coll_SubindexGridMenu_Items in range(4)]

class EditingPanel(wx.SplitterWindow):
    def _init_coll_AddToListSizer_Items(self, parent):
        parent.AddWindow(self.AddButton, 0, border=0, flag=0)
        parent.AddWindow(self.IndexChoice, 0, border=0, flag=wx.GROW)

    def _init_coll_SubindexGridSizer_Items(self, parent):
        parent.AddWindow(self.CallbackCheck, 0, border=0, flag=0)
        parent.AddWindow(self.SubindexGrid, 0, border=0, flag=wx.GROW)

    def _init_coll_IndexListSizer_Items(self, parent):
        parent.AddWindow(self.IndexList, 0, border=0, flag=wx.GROW)
        parent.AddSizer(self.AddToListSizer, 0, border=0, flag=wx.GROW)

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

    def _init_coll_SubindexGridSizer_Growables(self, parent):
        parent.AddGrowableCol(0)
        parent.AddGrowableRow(1)

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

    def _init_coll_SubindexGridMenu_Items(self, parent):
        parent.Append(help='', id=ID_EDITINGPANELMENU1ITEMS0,
              kind=wx.ITEM_NORMAL, text=_('Add subindexes'))
        parent.Append(help='', id=ID_EDITINGPANELMENU1ITEMS1,
              kind=wx.ITEM_NORMAL, text=_('Delete subindexes'))
        parent.AppendSeparator()
        parent.Append(help='', id=ID_EDITINGPANELMENU1ITEMS3,
              kind=wx.ITEM_NORMAL, text=_('Default value'))
        if not self.Editable:
            parent.Append(help='', id=ID_EDITINGPANELMENU1ITEMS4,
                  kind=wx.ITEM_NORMAL, text=_('Add to DCF'))
        self.Bind(wx.EVT_MENU, self.OnAddSubindexMenu,
              id=ID_EDITINGPANELMENU1ITEMS0)
        self.Bind(wx.EVT_MENU, self.OnDeleteSubindexMenu,
              id=ID_EDITINGPANELMENU1ITEMS1)
        self.Bind(wx.EVT_MENU, self.OnDefaultValueSubindexMenu,
              id=ID_EDITINGPANELMENU1ITEMS3)
        if not self.Editable:
            self.Bind(wx.EVT_MENU, self.OnAddToDCFSubindexMenu,
                  id=ID_EDITINGPANELMENU1ITEMS4)

    def _init_coll_IndexListMenu_Items(self, parent):
        parent.Append(help='', id=ID_EDITINGPANELINDEXLISTMENUITEMS0,
              kind=wx.ITEM_NORMAL, text=_('Rename'))
        parent.Append(help='', id=ID_EDITINGPANELINDEXLISTMENUITEMS2,
              kind=wx.ITEM_NORMAL, text=_('Modify'))
        parent.Append(help='', id=ID_EDITINGPANELINDEXLISTMENUITEMS1,
              kind=wx.ITEM_NORMAL, text=_('Delete'))
        self.Bind(wx.EVT_MENU, self.OnRenameIndexMenu,
              id=ID_EDITINGPANELINDEXLISTMENUITEMS0)
        self.Bind(wx.EVT_MENU, self.OnDeleteIndexMenu,
              id=ID_EDITINGPANELINDEXLISTMENUITEMS1)
        self.Bind(wx.EVT_MENU, self.OnModifyIndexMenu,
              id=ID_EDITINGPANELINDEXLISTMENUITEMS2)

    def _init_utils(self):
        self.IndexListMenu = wx.Menu(title='')
        self.SubindexGridMenu = wx.Menu(title='')

        self._init_coll_IndexListMenu_Items(self.IndexListMenu)
        self._init_coll_SubindexGridMenu_Items(self.SubindexGridMenu)

    def _init_sizers(self):
        self.IndexListSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
        self.SubindexGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
        self.AddToListSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)

        self._init_coll_IndexListSizer_Growables(self.IndexListSizer)
        self._init_coll_IndexListSizer_Items(self.IndexListSizer)
        self._init_coll_SubindexGridSizer_Growables(self.SubindexGridSizer)
        self._init_coll_SubindexGridSizer_Items(self.SubindexGridSizer)
        self._init_coll_AddToListSizer_Growables(self.AddToListSizer)
        self._init_coll_AddToListSizer_Items(self.AddToListSizer)

        self.SubindexGridPanel.SetSizer(self.SubindexGridSizer)
        self.IndexListPanel.SetSizer(self.IndexListSizer)
        
    def _init_ctrls(self, prnt):
        wx.SplitterWindow.__init__(self, id=ID_EDITINGPANEL,
              name='MainSplitter', parent=prnt, point=wx.Point(0, 0),
              size=wx.Size(-1, -1), style=wx.SP_3D)
        self._init_utils()
        
        self.PartList = wx.ListBox(choices=[], id=ID_EDITINGPANELPARTLIST,
              name='PartList', parent=self, pos=wx.Point(0, 0),
              size=wx.Size(-1, -1), style=0)
        self.PartList.Bind(wx.EVT_LISTBOX, self.OnPartListBoxClick,
              id=ID_EDITINGPANELPARTLIST)

        self.SecondSplitter = wx.SplitterWindow(id=ID_EDITINGPANELSECONDSPLITTER,
              name='SecondSplitter', parent=self, point=wx.Point(0, 0), 
              size=wx.Size(-1, -1), style=wx.SP_3D)
        self.SplitHorizontally(self.PartList, self.SecondSplitter, 110)
        self.SetMinimumPaneSize(1)
        
        self.SubindexGridPanel = wx.Panel(id=ID_EDITINGPANELSUBINDEXGRIDPANEL,
              name='SubindexGridPanel', parent=self.SecondSplitter, 
              pos=wx.Point(0, 0), size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL)

        self.IndexListPanel = wx.Panel(id=ID_EDITINGPANELINDEXLISTPANEL,
              name='IndexListPanel', parent=self.SecondSplitter, 
              pos=wx.Point(0, 0), size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL)
        self.SecondSplitter.SplitVertically(self.IndexListPanel, self.SubindexGridPanel, 280)
        self.SecondSplitter.SetMinimumPaneSize(1)
        
        self.SubindexGrid = wx.grid.Grid(id=ID_EDITINGPANELSUBINDEXGRID,
              name='SubindexGrid', parent=self.SubindexGridPanel, pos=wx.Point(0,
              0), size=wx.Size(-1, -1), style=0)
        self.SubindexGrid.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False,
              'Sans'))
        self.SubindexGrid.SetLabelFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL,
              False, 'Sans'))
        self.SubindexGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE,
              self.OnSubindexGridCellChange)
        self.SubindexGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK,
              self.OnSubindexGridRightClick)
        self.SubindexGrid.Bind(wx.grid.EVT_GRID_SELECT_CELL,
              self.OnSubindexGridSelectCell)
        self.SubindexGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
              self.OnSubindexGridCellLeftClick)
        self.SubindexGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, 
              self.OnSubindexGridEditorShown)

        self.CallbackCheck = wx.CheckBox(id=ID_EDITINGPANELCALLBACKCHECK,
              label=_('Have Callbacks'), name='CallbackCheck',
              parent=self.SubindexGridPanel, pos=wx.Point(0, 0), size=wx.Size(152,
              24), style=0)
        self.CallbackCheck.Bind(wx.EVT_CHECKBOX, self.OnCallbackCheck,
              id=ID_EDITINGPANELCALLBACKCHECK)

        self.IndexList = wx.ListBox(choices=[], id=ID_EDITINGPANELINDEXLIST,
              name='IndexList', parent=self.IndexListPanel, pos=wx.Point(0, 0),
              size=wx.Size(-1, -1), style=0)
        self.IndexList.Bind(wx.EVT_LISTBOX, self.OnIndexListClick,
              id=ID_EDITINGPANELINDEXLIST)
        self.IndexList.Bind(wx.EVT_RIGHT_UP, self.OnIndexListRightUp)

        self.AddButton = wx.Button(id=ID_EDITINGPANELADDBUTTON, label=_('Add'),
              name='AddButton', parent=self.IndexListPanel, pos=wx.Point(0, 0),
              size=wx.DefaultSize, style=0)
        self.AddButton.Bind(wx.EVT_BUTTON, self.OnAddButtonClick,
              id=ID_EDITINGPANELADDBUTTON)

        self.IndexChoice = wx.ComboBox(choices=[], id=ID_EDITINGPANELINDEXCHOICE,
              name='IndexChoice', parent=self.IndexListPanel, pos=wx.Point(50,
              0), size=wx.Size(-1, 30), style=wx.CB_READONLY)

        self._init_sizers()

    def __init__(self, parent, window, manager, editable = True):
        self.Editable = editable
        self._init_ctrls(parent)
        self.ParentWindow = window
        self.Manager = manager
        self.ListIndex = []
        self.ChoiceIndex = []
        self.FirstCall = False
        self.Index = None
        
        for values in DictionaryOrganisation:
            text = "   0x%04X-0x%04X      %s"%(values["minIndex"], values["maxIndex"], values["name"])
            self.PartList.Append(text)
        self.Table = SubindexTable(self, [], [], GetSubindexTableColnames())
        self.SubindexGrid.SetTable(self.Table)
        self.SubindexGrid.SetRowLabelSize(0)
        self.CallbackCheck.Disable()
        self.Table.ResetView(self.SubindexGrid)

        if not self.Editable:
            self.AddButton.Disable()
            self.IndexChoice.Disable()
            self.CallbackCheck.Disable()
            self.Table.Disable()
            
        wx.CallAfter(self.SetSashPosition, 110)
        wx.CallAfter(self.SecondSplitter.SetSashPosition, 280)

    def GetIndex(self):
        return self.Index
    
    def SetIndex(self, index):
        self.Index = index

    def GetSelection(self):
        selected = self.IndexList.GetSelection()
        if selected != wx.NOT_FOUND:
            index = self.ListIndex[selected]
            subIndex = self.SubindexGrid.GetGridCursorRow()
            return index, subIndex
        return None

    def OnSubindexGridCellLeftClick(self, event):
        if not self.ParentWindow.ModeSolo:
            col = event.GetCol()
            if self.Editable and col == 0:
                selected = self.IndexList.GetSelection()
                if selected != wx.NOT_FOUND:
                    index = self.ListIndex[selected]
                    subindex = event.GetRow()
                    entry_infos = self.Manager.GetEntryInfos(index)
                    if not entry_infos["struct"] & OD_MultipleSubindexes or subindex != 0:
                        subentry_infos = self.Manager.GetSubentryInfos(index, subindex)
                        typeinfos = self.Manager.GetEntryInfos(subentry_infos["type"])
                        if typeinfos:
                            bus_id = '.'.join(map(str, self.ParentWindow.GetBusId()))
                            var_name = "%s_%04x_%02x" % (self.Manager.GetCurrentNodeName(), index, subindex)
                            size = typeinfos["size"]
                            data = wx.TextDataObject(str(
                                ("%s%s.%d.%d"%(SizeConversion[size], bus_id, index, subindex), 
                                 "location", 
                                 IECTypeConversion.get(typeinfos["name"]), 
                                 var_name, "")))
                            dragSource = wx.DropSource(self.SubindexGrid)
                            dragSource.SetData(data)
                            dragSource.DoDragDrop()
                            return
            elif col == 0:
                selected = self.IndexList.GetSelection()
                node_id = self.ParentWindow.GetCurrentNodeId()
                if selected != wx.NOT_FOUND and node_id is not None:
                    index = self.ListIndex[selected]
                    subindex = event.GetRow()
                    entry_infos = self.Manager.GetEntryInfos(index)
                    if not entry_infos["struct"] & OD_MultipleSubindexes or subindex != 0:
                        subentry_infos = self.Manager.GetSubentryInfos(index, subindex)
                        typeinfos = self.Manager.GetEntryInfos(subentry_infos["type"])
                        if subentry_infos["pdo"] and typeinfos:
                            bus_id = '.'.join(map(str, self.ParentWindow.GetBusId()))
                            var_name = "%s_%04x_%02x" % (self.Manager.GetSlaveName(node_id), index, subindex)
                            size = typeinfos["size"]
                            data = wx.TextDataObject(str(
                                ("%s%s.%d.%d.%d"%(SizeConversion[size], bus_id, node_id, index, subindex), 
                                 "location", 
                                 IECTypeConversion.get(typeinfos["name"]), 
                                 var_name, "")))
                            dragSource = wx.DropSource(self.SubindexGrid)
                            dragSource.SetData(data)
                            dragSource.DoDragDrop()
                            return
        event.Skip()

    def OnAddButtonClick(self, event):
        if self.Editable:
            self.SubindexGrid.SetGridCursor(0, 0)
            selected = self.IndexChoice.GetStringSelection()
            if selected != "":
                choice = INDEXCHOICE_OPTIONS_DICT.get(selected, None)
                if choice is not None:
                    if INDEXCHOICE_OPTIONS[choice][1] == 0:
                        getattr(self.ParentWindow, INDEXCHOICE_OPTIONS[choice][2])()
                    elif INDEXCHOICE_OPTIONS[choice][1] == 1:
                        getattr(self.Manager, INDEXCHOICE_OPTIONS[choice][2])()
                elif selected in [menu for menu, indexes in self.Manager.GetCurrentSpecificMenu()]:
                    self.Manager.AddSpecificEntryToCurrent(selected)
                else:
                    index = self.ChoiceIndex[self.IndexChoice.GetSelection()]
                    self.Manager.ManageEntriesOfCurrent([index], [])
                self.ParentWindow.RefreshBufferState()
                self.RefreshIndexList()
        event.Skip()

    def OnPartListBoxClick(self, event):
        if not self.ParentWindow.IsClosing():
            self.SubindexGrid.SetGridCursor(0, 0)
            self.RefreshIndexList()
        event.Skip()

    def OnIndexListClick(self, event):
        if not self.ParentWindow.IsClosing():
            self.SubindexGrid.SetGridCursor(0, 0)
            self.RefreshTable()
        event.Skip()

    def OnSubindexGridSelectCell(self, event):
        if not self.ParentWindow.IsClosing():
            wx.CallAfter(self.ParentWindow.RefreshStatusBar)
        event.Skip()

#-------------------------------------------------------------------------------
#                             Refresh Functions
#-------------------------------------------------------------------------------

    def RefreshIndexList(self):
        selected = self.IndexList.GetSelection()
        choice = self.IndexChoice.GetStringSelection()
        choiceindex = self.IndexChoice.GetSelection()
        if selected != wx.NOT_FOUND:
            selectedindex = self.ListIndex[selected]
        self.IndexList.Clear()
        self.IndexChoice.Clear()
        i = self.PartList.GetSelection()
        if i < len(DictionaryOrganisation):
            values = DictionaryOrganisation[i]
            self.ListIndex = []
            for name, index in self.Manager.GetCurrentValidIndexes(values["minIndex"], values["maxIndex"]):
                self.IndexList.Append("0x%04X   %s"%(index, name))
                self.ListIndex.append(index)
            if self.Editable:
                self.ChoiceIndex = []
                choices = INDEXCHOICE_SECTIONS.get(i, None)
                if choices is not None:
                    for c in choices:
                        self.IndexChoice.Append(INDEXCHOICE_OPTIONS[c][0])
                    if len(choices) > 1:
                        if choiceindex != wx.NOT_FOUND and choice == self.IndexChoice.GetString(choiceindex):
                            self.IndexChoice.SetStringSelection(choice)
                    else:
                        self.IndexChoice.SetSelection(0)
                else:
                    for name, index in self.Manager.GetCurrentValidChoices(values["minIndex"], values["maxIndex"]):
                        if index:
                            self.IndexChoice.Append("0x%04X   %s"%(index, name))
                        else:
                            self.IndexChoice.Append(name)
                        self.ChoiceIndex.append(index)
                if choiceindex != wx.NOT_FOUND and choiceindex < self.IndexChoice.GetCount() and choice == self.IndexChoice.GetString(choiceindex):
                    self.IndexChoice.SetStringSelection(choice)
        if self.Editable:
            self.IndexChoice.Enable(self.IndexChoice.GetCount() != 0)
            self.AddButton.Enable(self.IndexChoice.GetCount() != 0)
        if selected == wx.NOT_FOUND or selected >= len(self.ListIndex) or selectedindex != self.ListIndex[selected]:
            self.Table.Empty()
            self.CallbackCheck.SetValue(False)
            self.CallbackCheck.Disable()
            self.Table.ResetView(self.SubindexGrid)
            self.ParentWindow.RefreshStatusBar()
        else:
            self.IndexList.SetSelection(selected)
            self.RefreshTable()

    def RefreshTable(self):
        selected = self.IndexList.GetSelection()
        if selected != wx.NOT_FOUND:
            index = self.ListIndex[selected]
            if index > 0x260 and self.Editable:
                self.CallbackCheck.Enable()
                self.CallbackCheck.SetValue(self.Manager.HasCurrentEntryCallbacks(index))
            result = self.Manager.GetCurrentEntryValues(index)
            if result != None:
                self.Table.SetCurrentIndex(index)
                data, editors = result
                self.Table.SetData(data)
                self.Table.SetEditors(editors)
                self.Table.ResetView(self.SubindexGrid)
        self.ParentWindow.RefreshStatusBar()

#-------------------------------------------------------------------------------
#                        Editing Table value function
#-------------------------------------------------------------------------------

    def OnSubindexGridEditorShown(self, event):
        row, col = event.GetRow(), event.GetCol() 
        if self.Table.GetEditor(row, col) == "dcf":
            wx.CallAfter(self.ShowDCFEntryDialog, row, col)
            event.Veto()
        else:
            event.Skip()

    def ShowDCFEntryDialog(self, row, col):
        if self.Editable or self.ParentWindow.GetCurrentNodeId() is None:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index):
                    dialog = DCFEntryValuesDialog(self, self.Editable)
                    dialog.SetValues(self.Table.GetValue(row, col).decode("hex_codec"))
                    if dialog.ShowModal() == wx.ID_OK and self.Editable:
                        value = dialog.GetValues()
                        self.Manager.SetCurrentEntry(index, row, value, "value", "dcf")
                        self.ParentWindow.RefreshBufferState()
                        wx.CallAfter(self.RefreshTable)

    def OnSubindexGridCellChange(self, event):
        if self.Editable:
            index = self.Table.GetCurrentIndex()
            subIndex = event.GetRow()
            col = event.GetCol()
            name = self.Table.GetColLabelValue(col, False)
            value = self.Table.GetValue(subIndex, col, False)
            editor = self.Table.GetEditor(subIndex, col)
            self.Manager.SetCurrentEntry(index, subIndex, value, name, editor)
            self.ParentWindow.RefreshBufferState()
            wx.CallAfter(self.RefreshTable)
        event.Skip()

    def OnCallbackCheck(self, event):
        if self.Editable:
            index = self.Table.GetCurrentIndex()
            self.Manager.SetCurrentEntryCallbacks(index, self.CallbackCheck.GetValue())
            self.ParentWindow.RefreshBufferState()
            wx.CallAfter(self.RefreshTable)
        event.Skip()

#-------------------------------------------------------------------------------
#                          Contextual Menu functions
#-------------------------------------------------------------------------------

    def OnIndexListRightUp(self, event):
        if self.Editable:
            if not self.FirstCall:
                self.FirstCall = True
                selected = self.IndexList.GetSelection()
                if selected != wx.NOT_FOUND:
                    index = self.ListIndex[selected]
                    if index < 0x260:
                        self.IndexListMenu.FindItemByPosition(0).Enable(False)
                        self.IndexListMenu.FindItemByPosition(1).Enable(True)
                        self.PopupMenu(self.IndexListMenu)
                    elif 0x1000 <= index <= 0x1FFF:
                        self.IndexListMenu.FindItemByPosition(0).Enable(False)
                        self.IndexListMenu.FindItemByPosition(1).Enable(False)
                        self.PopupMenu(self.IndexListMenu)
                    elif 0x2000 <= index <= 0x5FFF:
                        self.IndexListMenu.FindItemByPosition(0).Enable(True)
                        self.IndexListMenu.FindItemByPosition(1).Enable(False)
                        self.PopupMenu(self.IndexListMenu)
                    elif index >= 0x6000:
                        self.IndexListMenu.FindItemByPosition(0).Enable(False)
                        self.IndexListMenu.FindItemByPosition(1).Enable(False)
                        self.PopupMenu(self.IndexListMenu)
            else:
                self.FirstCall = False
        event.Skip()

    def OnSubindexGridRightClick(self, event):
        self.SubindexGrid.SetGridCursor(event.GetRow(), event.GetCol())
        if self.Editable:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index):
                    showpopup = False
                    infos = self.Manager.GetEntryInfos(index)
                    if 0x2000 <= index <= 0x5FFF and infos["struct"] & OD_MultipleSubindexes or infos["struct"] & OD_IdenticalSubindexes:
                        showpopup = True
                        self.SubindexGridMenu.FindItemByPosition(0).Enable(True)
                        self.SubindexGridMenu.FindItemByPosition(1).Enable(True)
                    else:
                        self.SubindexGridMenu.FindItemByPosition(0).Enable(False)
                        self.SubindexGridMenu.FindItemByPosition(1).Enable(False)
                    if self.Table.GetColLabelValue(event.GetCol(), False) == "value":
                        showpopup = True
                        self.SubindexGridMenu.FindItemByPosition(3).Enable(True)
                    else:
                        self.SubindexGridMenu.FindItemByPosition(3).Enable(False)
                    if showpopup:
                        self.PopupMenu(self.SubindexGridMenu)
        elif self.Table.GetColLabelValue(event.GetCol(), False) == "value" and self.ParentWindow.GetCurrentNodeId() is not None:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index):
                    infos = self.Manager.GetEntryInfos(index)
                    if not infos["struct"] & OD_MultipleSubindexes or event.GetRow() > 0:
                        self.SubindexGridMenu.FindItemByPosition(0).Enable(False)
                        self.SubindexGridMenu.FindItemByPosition(1).Enable(False)
                        self.SubindexGridMenu.FindItemByPosition(3).Enable(False)
                        self.SubindexGridMenu.FindItemByPosition(4).Enable(True)
                        self.PopupMenu(self.SubindexGridMenu)
        event.Skip()

    def OnAddToDCFSubindexMenu(self, event):
        if not self.Editable:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                subindex = self.SubindexGrid.GetGridCursorRow()
                entry_infos = self.Manager.GetEntryInfos(index)
                if not entry_infos["struct"] & OD_MultipleSubindexes or subindex != 0:
                    subentry_infos = self.Manager.GetSubentryInfos(index, subindex)
                    typeinfos = self.Manager.GetEntryInfos(subentry_infos["type"])
                    if typeinfos:
                        node_id = self.ParentWindow.GetCurrentNodeId()
                        value = self.Table.GetValueByName(subindex, "value")
                        if value == "True":
                            value = 1
                        elif value == "False":
                            value = 0
                        elif value.isdigit():
                            value = int(value)
                        elif value.startswith("0x"):
                            value = int(value, 16)
                        else:
                            value = int(value.encode("hex_codec"), 16)
                        self.Manager.AddToMasterDCF(node_id, index, subindex, max(1, typeinfos["size"] / 8), value)    
                        self.ParentWindow.OpenMasterDCFDialog(node_id)

    def OpenDCFDialog(self, node_id):
        self.PartList.SetSelection(7)
        self.RefreshIndexList()
        self.IndexList.SetSelection(self.ListIndex.index(0x1F22))
        self.RefreshTable()
        self.SubindexGrid.SetGridCursor(node_id, 3)
        self.ShowDCFEntryDialog(node_id, 3)

    def OnRenameIndexMenu(self, event):
        if self.Editable:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index):
                    infos = self.Manager.GetEntryInfos(index)
                    dialog = wx.TextEntryDialog(self, _("Give a new name for index 0x%04X")%index,
                                 _("Rename an index"), infos["name"], wx.OK|wx.CANCEL)
                    if dialog.ShowModal() == wx.ID_OK:
                        self.Manager.SetCurrentEntryName(index, dialog.GetValue())
                        self.ParentWindow.RefreshBufferState()
                        self.RefreshIndexList()
                    dialog.Destroy()
        
    def OnModifyIndexMenu(self, event):
        if self.Editable:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index) and index < 0x260:
                    values, valuetype = self.Manager.GetCustomisedTypeValues(index)
                    dialog = UserTypeDialog(self)
                    dialog.SetTypeList(self.Manager.GetCustomisableTypes(), values[1])
                    if valuetype == 0:
                        dialog.SetValues(min = values[2], max = values[3])
                    elif valuetype == 1:
                        dialog.SetValues(length = values[2])
                    if dialog.ShowModal() == wx.ID_OK:
                        type, min, max, length = dialog.GetValues()
                        self.Manager.SetCurrentUserType(index, type, min, max, length)
                        self.ParentWindow.RefreshBufferState()
                        self.RefreshIndexList()
        
    def OnDeleteIndexMenu(self, event):
        if self.Editable:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index):
                    self.Manager.ManageEntriesOfCurrent([],[index])
                    self.ParentWindow.RefreshBufferState()
                    self.RefreshIndexList()
        
    def OnAddSubindexMenu(self, event):
        if self.Editable:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index):
                    dialog = wx.TextEntryDialog(self, _("Number of subindexes to add:"),
                                 _("Add subindexes"), "1", wx.OK|wx.CANCEL)
                    if dialog.ShowModal() == wx.ID_OK:
                        try:
                            number = int(dialog.GetValue())
                            self.Manager.AddSubentriesToCurrent(index, number)
                            self.ParentWindow.RefreshBufferState()
                            self.RefreshIndexList()
                        except:
                            message = wx.MessageDialog(self, _("An integer is required!"), _("ERROR"), wx.OK|wx.ICON_ERROR)
                            message.ShowModal()
                            message.Destroy()
                    dialog.Destroy()
        
    def OnDeleteSubindexMenu(self, event):
        if self.Editable:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index):
                    dialog = wx.TextEntryDialog(self, _("Number of subindexes to delete:"),
                                 _("Delete subindexes"), "1", wx.OK|wx.CANCEL)
                    if dialog.ShowModal() == wx.ID_OK:
                        try:
                            number = int(dialog.GetValue())
                            self.Manager.RemoveSubentriesFromCurrent(index, number)
                            self.ParentWindow.RefreshBufferState()
                            self.RefreshIndexList()
                        except:
                            message = wx.MessageDialog(self, _("An integer is required!"), _("ERROR"), wx.OK|wx.ICON_ERROR)
                            message.ShowModal()
                            message.Destroy()
                    dialog.Destroy()
        
    def OnDefaultValueSubindexMenu(self, event):
        if self.Editable:
            selected = self.IndexList.GetSelection()
            if selected != wx.NOT_FOUND:
                index = self.ListIndex[selected]
                if self.Manager.IsCurrentEntry(index):
                    row = self.SubindexGrid.GetGridCursorRow()
                    self.Manager.SetCurrentEntryToDefault(index, row)
                    self.ParentWindow.RefreshBufferState()
                    self.RefreshIndexList()