controls/LibraryPanel.py
changeset 1784 64beb9e9c749
parent 1782 5b6ad7a7fd9d
child 1847 6198190bc121
equal deleted inserted replaced
1729:31e63e25b4cc 1784:64beb9e9c749
    22 # along with this program; if not, write to the Free Software
    22 # along with this program; if not, write to the Free Software
    23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    24 
    24 
    25 import wx
    25 import wx
    26 
    26 
    27 #-------------------------------------------------------------------------------
    27 # -------------------------------------------------------------------------------
    28 #                                 Helpers
    28 #                                 Helpers
    29 #-------------------------------------------------------------------------------
    29 # -------------------------------------------------------------------------------
       
    30 
    30 
    31 
    31 [CATEGORY, BLOCK] = range(2)
    32 [CATEGORY, BLOCK] = range(2)
    32 
    33 
    33 #-------------------------------------------------------------------------------
    34 
       
    35 # -------------------------------------------------------------------------------
    34 #                              Library Panel
    36 #                              Library Panel
    35 #-------------------------------------------------------------------------------
    37 # -------------------------------------------------------------------------------
    36 
    38 
    37 """
       
    38 Class that implements a panel displaying a tree containing an hierarchical list
       
    39 of functions and function blocks available in project an a search control for
       
    40 quickly find one functions or function blocks in this list and a text control
       
    41 displaying informations about selected functions or function blocks
       
    42 """
       
    43 
    39 
    44 class LibraryPanel(wx.Panel):
    40 class LibraryPanel(wx.Panel):
    45     
    41     """
       
    42     Class that implements a panel displaying a tree containing an hierarchical list
       
    43     of functions and function blocks available in project an a search control for
       
    44     quickly find one functions or function blocks in this list and a text control
       
    45     displaying informations about selected functions or function blocks
       
    46     """
       
    47 
    46     def __init__(self, parent, enable_drag=False):
    48     def __init__(self, parent, enable_drag=False):
    47         """
    49         """
    48         Constructor
    50         Constructor
    49         @param parent: Parent wx.Window of LibraryPanel
    51         @param parent: Parent wx.Window of LibraryPanel
    50         @param enable_drag: Flag indicating that function or function block can
    52         @param enable_drag: Flag indicating that function or function block can
    51         be drag'n drop from LibraryPanel (default: False)
    53         be drag'n drop from LibraryPanel (default: False)
    52         """
    54         """
    53         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
    55         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
    54         
    56 
    55         # Define LibraryPanel main sizer
    57         # Define LibraryPanel main sizer
    56         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
    58         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
    57         main_sizer.AddGrowableCol(0)
    59         main_sizer.AddGrowableCol(0)
    58         main_sizer.AddGrowableRow(1)
    60         main_sizer.AddGrowableRow(1)
    59         
    61 
    60         # Add SearchCtrl to main sizer
    62         # Add SearchCtrl to main sizer
    61         self.SearchCtrl = wx.SearchCtrl(self)
    63         self.SearchCtrl = wx.SearchCtrl(self)
    62         # Add a button with a magnifying glass, essentially to show that this
    64         # Add a button with a magnifying glass, essentially to show that this
    63         # control is for searching in tree
    65         # control is for searching in tree
    64         self.SearchCtrl.ShowSearchButton(True)
    66         self.SearchCtrl.ShowSearchButton(True)
    65         self.Bind(wx.EVT_TEXT, self.OnSearchCtrlChanged, self.SearchCtrl)
    67         self.Bind(wx.EVT_TEXT, self.OnSearchCtrlChanged, self.SearchCtrl)
    66         self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, 
    68         self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN,
    67                   self.OnSearchButtonClick, self.SearchCtrl)
    69                   self.OnSearchButtonClick, self.SearchCtrl)
    68         # Bind keyboard event on SearchCtrl text control to catch UP and DOWN
    70         # Bind keyboard event on SearchCtrl text control to catch UP and DOWN
    69         # for search previous and next occurrence
    71         # for search previous and next occurrence
    70 
    72 
    71         # This protects from fail to start when no children[0] available (possible for wxPython 3.0)
    73         # This protects from fail to start when no children[0] available (possible for wxPython 3.0)
    72         if self.SearchCtrl.GetChildren():
    74         if self.SearchCtrl.GetChildren():
    73             search_textctrl = self.SearchCtrl.GetChildren()[0]
    75             search_textctrl = self.SearchCtrl.GetChildren()[0]
    74             search_textctrl.Bind(wx.EVT_CHAR, self.OnKeyDown)
    76             search_textctrl.Bind(wx.EVT_CHAR, self.OnKeyDown)
    75 
    77 
    76         main_sizer.AddWindow(self.SearchCtrl, flag=wx.GROW)
    78         main_sizer.AddWindow(self.SearchCtrl, flag=wx.GROW)
    77         
    79 
    78         # Add Splitter window for tree and block comment to main sizer
    80         # Add Splitter window for tree and block comment to main sizer
    79         splitter_window = wx.SplitterWindow(self)
    81         splitter_window = wx.SplitterWindow(self)
    80         splitter_window.SetSashGravity(1.0)
    82         splitter_window.SetSashGravity(1.0)
    81         main_sizer.AddWindow(splitter_window, flag=wx.GROW)
    83         main_sizer.AddWindow(splitter_window, flag=wx.GROW)
    82         
    84 
    83         # Add TreeCtrl for functions and function blocks library in splitter
    85         # Add TreeCtrl for functions and function blocks library in splitter
    84         # window
    86         # window
    85         self.Tree = wx.TreeCtrl(splitter_window,
    87         self.Tree = wx.TreeCtrl(splitter_window,
    86               size=wx.Size(0, 0),  
    88                                 size=wx.Size(0, 0),
    87               style=wx.TR_HAS_BUTTONS|
    89                                 style=(wx.TR_HAS_BUTTONS |
    88                     wx.TR_SINGLE|
    90                                        wx.TR_SINGLE |
    89                     wx.SUNKEN_BORDER|
    91                                        wx.SUNKEN_BORDER |
    90                     wx.TR_HIDE_ROOT|
    92                                        wx.TR_HIDE_ROOT |
    91                     wx.TR_LINES_AT_ROOT)
    93                                        wx.TR_LINES_AT_ROOT))
    92         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemSelected, self.Tree)
    94         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemSelected, self.Tree)
    93         self.Tree.Bind(wx.EVT_CHAR, self.OnKeyDown)
    95         self.Tree.Bind(wx.EVT_CHAR, self.OnKeyDown)
    94         # If drag'n drop is enabled, bind event generated when a drag begins on
    96         # If drag'n drop is enabled, bind event generated when a drag begins on
    95         # tree to start a drag'n drop
    97         # tree to start a drag'n drop
    96         if enable_drag:
    98         if enable_drag:
    97             self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, self.Tree)
    99             self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, self.Tree)
    98         
   100 
    99         # Add TextCtrl for function and function block informations
   101         # Add TextCtrl for function and function block informations
   100         self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80), 
   102         self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80),
   101               style=wx.TE_READONLY|wx.TE_MULTILINE)
   103                                    style=wx.TE_READONLY | wx.TE_MULTILINE)
   102         
   104 
   103         splitter_window.SplitHorizontally(self.Tree, self.Comment, -80)
   105         splitter_window.SplitHorizontally(self.Tree, self.Comment, -80)
   104         
   106 
   105         self.SetSizer(main_sizer)
   107         self.SetSizer(main_sizer)
   106         
   108 
   107         # Reference to the project controller
   109         # Reference to the project controller
   108         self.Controller = None
   110         self.Controller = None
   109         
   111 
   110         # Variable storing functions and function blocks library to display
   112         # Variable storing functions and function blocks library to display
   111         self.BlockList = None
   113         self.BlockList = None
   112     
   114 
   113     def __del__(self):
   115     def __del__(self):
   114         """
   116         """
   115         Destructor
   117         Destructor
   116         """
   118         """
   117         # Remove reference to project controller
   119         # Remove reference to project controller
   118         self.Controller = None
   120         self.Controller = None
   119     
   121 
   120     def SetController(self, controller):
   122     def SetController(self, controller):
   121         """
   123         """
   122         Set reference to project controller
   124         Set reference to project controller
   123         @param controller: Reference to project controller
   125         @param controller: Reference to project controller
   124         """
   126         """
   125         self.Controller = controller
   127         self.Controller = controller
   126     
   128 
   127     def SetBlockList(self, blocklist):
   129     def SetBlockList(self, blocklist):
   128         """
   130         """
   129         Set function and function block library to display in TreeCtrl
   131         Set function and function block library to display in TreeCtrl
   130         @param blocklist: Function and function block library
   132         @param blocklist: Function and function block library
   131         """
   133         """
   132         # Save functions and function blocks library
   134         # Save functions and function blocks library
   133         self.BlockList = blocklist
   135         self.BlockList = blocklist
   134         # Refresh TreeCtrl values
   136         # Refresh TreeCtrl values
   135         self.RefreshTree()
   137         self.RefreshTree()
   136     
   138 
   137     def SetFocus(self):
   139     def SetFocus(self):
   138         """
   140         """
   139         Called to give focus to LibraryPanel
   141         Called to give focus to LibraryPanel
   140         Override wx.Window SetFocus method 
   142         Override wx.Window SetFocus method
   141         """
   143         """
   142         # Give focus to SearchCtrl
   144         # Give focus to SearchCtrl
   143         self.SearchCtrl.SetFocus()
   145         self.SearchCtrl.SetFocus()
   144     
   146 
   145     def ResetTree(self):
   147     def ResetTree(self):
   146         """
   148         """
   147         Reset LibraryPanel values displayed in controls
   149         Reset LibraryPanel values displayed in controls
   148         """
   150         """
   149         # Clear SearchCtrl, TreeCtrl and TextCtrl
   151         # Clear SearchCtrl, TreeCtrl and TextCtrl
   150         self.SearchCtrl.SetValue("")
   152         self.SearchCtrl.SetValue("")
   151         self.Tree.DeleteAllItems()
   153         self.Tree.DeleteAllItems()
   152         self.Comment.SetValue("")
   154         self.Comment.SetValue("")
   153     
   155 
   154     def RefreshTree(self):
   156     def RefreshTree(self):
   155         """
   157         """
   156         Refresh LibraryPanel values displayed in controls
   158         Refresh LibraryPanel values displayed in controls
   157         """
   159         """
   158         # Get function and function blocks library
   160         # Get function and function blocks library
   159         blocktypes = self.BlockList
   161         blocktypes = self.BlockList
   160         if blocktypes is None and self.Controller is not None:
   162         if blocktypes is None and self.Controller is not None:
   161             # Get library from project controller if not defined
   163             # Get library from project controller if not defined
   162             blocktypes = self.Controller.GetBlockTypes()
   164             blocktypes = self.Controller.GetBlockTypes()
   163         
   165 
   164         # Refresh TreeCtrl values if a library is defined
   166         # Refresh TreeCtrl values if a library is defined
   165         if blocktypes is not None:
   167         if blocktypes is not None:
   166             # List that will contain tree items to be deleted when TreeCtrl
   168             # List that will contain tree items to be deleted when TreeCtrl
   167             # will be refreshed
   169             # will be refreshed
   168             items_to_delete = []
   170             items_to_delete = []
   169             
   171 
   170             # Get current selected item for selected it when values refreshed
   172             # Get current selected item for selected it when values refreshed
   171             selected_item = self.Tree.GetSelection()
   173             selected_item = self.Tree.GetSelection()
   172             selected_pydata = (self.Tree.GetPyData(selected_item)
   174             selected_pydata = (self.Tree.GetPyData(selected_item)
   173                                if selected_item.IsOk() and
   175                                if (selected_item.IsOk() and
   174                                   selected_item != self.Tree.GetRootItem()
   176                                    selected_item != self.Tree.GetRootItem())
   175                                else None)
   177                                else None)
   176             # Don't save selected item if it is a category
   178             # Don't save selected item if it is a category
   177             selected_infos = ((self.Tree.GetItemText(selected_item),
   179             selected_infos = ((self.Tree.GetItemText(selected_item),
   178                                selected_pydata["inputs"])
   180                                selected_pydata["inputs"])
   179                              if selected_pydata is not None and
   181                               if (selected_pydata is not None and
   180                                 selected_pydata["type"] == BLOCK
   182                                   selected_pydata["type"] == BLOCK)
   181                              else (None, None))
   183                               else (None, None))
   182             
   184 
   183             # Get TreeCtrl root item (hidden)
   185             # Get TreeCtrl root item (hidden)
   184             root = self.Tree.GetRootItem()
   186             root = self.Tree.GetRootItem()
   185             if not root.IsOk():
   187             if not root.IsOk():
   186                 # Create root if not present
   188                 # Create root if not present
   187                 root = self.Tree.AddRoot("")
   189                 root = self.Tree.AddRoot("")
   188             
   190 
   189             # Iterate over functions and function blocks library categories and
   191             # Iterate over functions and function blocks library categories and
   190             # add a tree item to root item for each of them
   192             # add a tree item to root item for each of them
   191             
   193 
   192             # Get first child under root item
   194             # Get first child under root item
   193             category_item, root_cookie = self.Tree.GetFirstChild(root)
   195             category_item, root_cookie = self.Tree.GetFirstChild(root)
   194             for category in blocktypes:
   196             for category in blocktypes:
   195                 # Store category name in a local variable to prevent script
   197                 # Store category name in a local variable to prevent script
   196                 # extracting translated strings for gettext to consider "name"
   198                 # extracting translated strings for gettext to consider "name"
   197                 # to be translated
   199                 # to be translated
   198                 category_name = category["name"]
   200                 category_name = category["name"]
   199                 
   201 
   200                 # Tree item already exists, set item label
   202                 # Tree item already exists, set item label
   201                 if category_item.IsOk():
   203                 if category_item.IsOk():
   202                     self.Tree.SetItemText(category_item, _(category_name))
   204                     self.Tree.SetItemText(category_item, _(category_name))
   203                 
   205 
   204                 # Tree item doesn't exist, add new one to root
   206                 # Tree item doesn't exist, add new one to root
   205                 else:
   207                 else:
   206                     category_item = self.Tree.AppendItem(root, _(category_name))
   208                     category_item = self.Tree.AppendItem(root, _(category_name))
   207                     # On Windows, needs to get next child of root to have a
   209                     # On Windows, needs to get next child of root to have a
   208                     # reference to the newly added tree item
   210                     # reference to the newly added tree item
   209                     if wx.Platform != '__WXMSW__':
   211                     if wx.Platform != '__WXMSW__':
   210                         category_item, root_cookie = \
   212                         category_item, root_cookie = \
   211                             self.Tree.GetNextChild(root, root_cookie)
   213                             self.Tree.GetNextChild(root, root_cookie)
   212                 
   214 
   213                 # Set data associated to tree item (only save that item is a 
   215                 # Set data associated to tree item (only save that item is a
   214                 # category)
   216                 # category)
   215                 self.Tree.SetPyData(category_item, {"type" : CATEGORY})
   217                 self.Tree.SetPyData(category_item, {"type": CATEGORY})
   216                 
   218 
   217                 # Iterate over functions and function blocks defined in library
   219                 # Iterate over functions and function blocks defined in library
   218                 # category add a tree item to category tree item for each of 
   220                 # category add a tree item to category tree item for each of
   219                 # them
   221                 # them
   220                 
   222 
   221                 # Get first child under category tree item
   223                 # Get first child under category tree item
   222                 blocktype_item, category_cookie = \
   224                 blocktype_item, category_cookie = \
   223                     self.Tree.GetFirstChild(category_item)
   225                     self.Tree.GetFirstChild(category_item)
   224                 for blocktype in category["list"]:
   226                 for blocktype in category["list"]:
   225                     
   227 
   226                     # Tree item already exists, set item label
   228                     # Tree item already exists, set item label
   227                     if blocktype_item.IsOk():
   229                     if blocktype_item.IsOk():
   228                         self.Tree.SetItemText(blocktype_item, blocktype["name"])
   230                         self.Tree.SetItemText(blocktype_item, blocktype["name"])
   229                     
   231 
   230                     # Tree item doesn't exist, add new one to category item
   232                     # Tree item doesn't exist, add new one to category item
   231                     else:
   233                     else:
   232                         blocktype_item = self.Tree.AppendItem(
   234                         blocktype_item = self.Tree.AppendItem(
   233                                             category_item, blocktype["name"])
   235                                             category_item, blocktype["name"])
   234                         # See comment when adding category
   236                         # See comment when adding category
   235                         if wx.Platform != '__WXMSW__':
   237                         if wx.Platform != '__WXMSW__':
   236                             blocktype_item, category_cookie = \
   238                             blocktype_item, category_cookie = \
   237                                 self.Tree.GetNextChild(category_item, 
   239                                 self.Tree.GetNextChild(category_item,
   238                                                        category_cookie)
   240                                                        category_cookie)
   239                     
   241 
   240                     # Define data to associate to block tree item
   242                     # Define data to associate to block tree item
   241                     comment = blocktype["comment"]
   243                     comment = blocktype["comment"]
   242                     block_data = {"type" : BLOCK, 
   244                     block_data = {
   243                                   "block_type" : blocktype["type"], 
   245                         "type":       BLOCK,
   244                                   "inputs" : tuple([type 
   246                         "block_type": blocktype["type"],
   245                                                     for name, type, modifier
   247                         "inputs":     tuple([type
   246                                                     in blocktype["inputs"]]), 
   248                                              for name, type, modifier
   247                                   "extension" : (len(blocktype["inputs"])
   249                                              in blocktype["inputs"]]),
   248                                                  if blocktype["extensible"]
   250                         "extension":  (len(blocktype["inputs"])
   249                                                  else None),
   251                                        if blocktype["extensible"] else None),
   250                                   "comment": _(comment) + 
   252                         "comment":    _(comment) + blocktype.get("usage", "")
   251                                              blocktype.get("usage", "")}
   253                     }
   252                     self.Tree.SetPyData(blocktype_item, block_data)
   254                     self.Tree.SetPyData(blocktype_item, block_data)
   253                     
   255 
   254                     # Select block tree item in tree if it corresponds to
   256                     # Select block tree item in tree if it corresponds to
   255                     # previously selected one
   257                     # previously selected one
   256                     if selected_infos == (blocktype["name"], 
   258                     if selected_infos == (blocktype["name"],
   257                                           blocktype["inputs"]):
   259                                           blocktype["inputs"]):
   258                         self.Tree.SelectItem(blocktype_item)
   260                         self.Tree.SelectItem(blocktype_item)
   259                         
   261 
   260                         # Update TextCtrl value
   262                         # Update TextCtrl value
   261                         self.Comment.SetValue(block_data["comment"])
   263                         self.Comment.SetValue(block_data["comment"])
   262                 
   264 
   263                     # Get next block tree item under category tree item
   265                     # Get next block tree item under category tree item
   264                     blocktype_item, category_cookie = \
   266                     blocktype_item, category_cookie = \
   265                         self.Tree.GetNextChild(category_item, category_cookie)
   267                         self.Tree.GetNextChild(category_item, category_cookie)
   266                 
   268 
   267                 # Add every remaining tree item under category tree item after
   269                 # Add every remaining tree item under category tree item after
   268                 # updating all block items to the list of items to delete
   270                 # updating all block items to the list of items to delete
   269                 while blocktype_item.IsOk():
   271                 while blocktype_item.IsOk():
   270                     items_to_delete.append(blocktype_item)
   272                     items_to_delete.append(blocktype_item)
   271                     blocktype_item, category_cookie = \
   273                     blocktype_item, category_cookie = \
   272                         self.Tree.GetNextChild(category_item, category_cookie)
   274                         self.Tree.GetNextChild(category_item, category_cookie)
   273                 
   275 
   274                 # Get next category tree item under root item
   276                 # Get next category tree item under root item
   275                 category_item, root_cookie = \
   277                 category_item, root_cookie = \
   276                     self.Tree.GetNextChild(root, root_cookie)
   278                     self.Tree.GetNextChild(root, root_cookie)
   277             
   279 
   278             # Add every remaining tree item under root item after updating all 
   280             # Add every remaining tree item under root item after updating all
   279             # category items to the list of items to delete
   281             # category items to the list of items to delete
   280             while category_item.IsOk():
   282             while category_item.IsOk():
   281                 items_to_delete.append(category_item)
   283                 items_to_delete.append(category_item)
   282                 category_item, root_cookie = \
   284                 category_item, root_cookie = \
   283                     self.Tree.GetNextChild(root, root_cookie)
   285                     self.Tree.GetNextChild(root, root_cookie)
   284             
   286 
   285             # Remove all items in list of items to delete from TreeCtrl
   287             # Remove all items in list of items to delete from TreeCtrl
   286             for item in items_to_delete:
   288             for item in items_to_delete:
   287                 self.Tree.Delete(item)
   289                 self.Tree.Delete(item)
   288     
   290 
   289     def GetSelectedBlock(self):
   291     def GetSelectedBlock(self):
   290         """
   292         """
   291         Get selected block informations
   293         Get selected block informations
   292         @return: {"type": block_type_name, "inputs": [input_type,...]} or None
   294         @return: {"type": block_type_name, "inputs": [input_type,...]} or None
   293         if no block selected
   295         if no block selected
   294         """
   296         """
   295         # Get selected item associated data in tree
   297         # Get selected item associated data in tree
   296         selected_item = self.Tree.GetSelection()
   298         selected_item = self.Tree.GetSelection()
   297         selected_pydata = (self.Tree.GetPyData(selected_item)
   299         selected_pydata = (self.Tree.GetPyData(selected_item)
   298                            if selected_item.IsOk() and
   300                            if (selected_item.IsOk() and
   299                               selected_item != self.Tree.GetRootItem()
   301                                selected_item != self.Tree.GetRootItem())
   300                            else None)
   302                            else None)
   301         
   303 
   302         # Return value is None if selected tree item is root or a category
   304         # Return value is None if selected tree item is root or a category
   303         return ({"type": self.Tree.GetItemText(selected_item), 
   305         return ({"type": self.Tree.GetItemText(selected_item),
   304                  "inputs": selected_pydata["inputs"]}
   306                  "inputs": selected_pydata["inputs"]}
   305                 if selected_pydata is not None and 
   307                 if (selected_pydata is not None and
   306                    selected_pydata["type"] == BLOCK
   308                     selected_pydata["type"] == BLOCK)
   307                 else None)
   309                 else None)
   308     
   310 
   309     def SelectTreeItem(self, name, inputs):
   311     def SelectTreeItem(self, name, inputs):
   310         """
   312         """
   311         Select Tree item corresponding to block informations given 
   313         Select Tree item corresponding to block informations given
   312         @param name: Block type name
   314         @param name: Block type name
   313         @param inputs: List of block inputs type [input_type,...]
   315         @param inputs: List of block inputs type [input_type,...]
   314         """
   316         """
   315         # Find tree item corresponding to block informations
   317         # Find tree item corresponding to block informations
   316         item = self.FindTreeItem(self.Tree.GetRootItem(), name, inputs)
   318         item = self.FindTreeItem(self.Tree.GetRootItem(), name, inputs)
   317         if item is not None and item.IsOk():
   319         if item is not None and item.IsOk():
   318             # Select tree item found
   320             # Select tree item found
   319             self.Tree.SelectItem(item)
   321             self.Tree.SelectItem(item)
   320             self.Tree.EnsureVisible(item)
   322             self.Tree.EnsureVisible(item)
   321     
   323 
   322     def FindTreeItem(self, item, name, inputs = None):
   324     def FindTreeItem(self, item, name, inputs=None):
   323         """
   325         """
   324         Find Tree item corresponding to block informations given
   326         Find Tree item corresponding to block informations given
   325         Function is recursive
   327         Function is recursive
   326         @param item: Item to test
   328         @param item: Item to test
   327         @param name: Block type name
   329         @param name: Block type name
   328         @param inputs: List of block inputs type [input_type,...]
   330         @param inputs: List of block inputs type [input_type,...]
   329         """
   331         """
   330         # Return immediately if item isn't valid
   332         # Return immediately if item isn't valid
   331         if not item.IsOk():
   333         if not item.IsOk():
   332             return None
   334             return None
   333         
   335 
   334         # Get data associated to item to test
   336         # Get data associated to item to test
   335         item_pydata = self.Tree.GetPyData(item)
   337         item_pydata = self.Tree.GetPyData(item)
   336         if item_pydata is not None and item_pydata["type"] == BLOCK:
   338         if item_pydata is not None and item_pydata["type"] == BLOCK:
   337             # Only test item corresponding to block
   339             # Only test item corresponding to block
   338             
   340 
   339             # Test if block inputs type are the same than those given
   341             # Test if block inputs type are the same than those given
   340             type_inputs = item_pydata.get("inputs", None)
   342             type_inputs = item_pydata.get("inputs", None)
   341             type_extension = item_pydata.get("extension", None)
   343             type_extension = item_pydata.get("extension", None)
   342             if inputs is not None and type_inputs is not None:
   344             if inputs is not None and type_inputs is not None:
   343                 same_inputs = reduce(
   345                 same_inputs = reduce(
   344                     lambda x, y: x and y,
   346                     lambda x, y: x and y,
   345                     map(
   347                     map(
   346                         lambda x: x[0]==x[1] or x[0]=='ANY' or x[1]=='ANY',
   348                         lambda x: x[0] == x[1] or x[0] == 'ANY' or x[1] == 'ANY',
   347                         zip(type_inputs,
   349                         zip(type_inputs,
   348                             (inputs[:type_extension]
   350                             (inputs[:type_extension]
   349                              if type_extension is not None
   351                              if type_extension is not None
   350                              else inputs))),
   352                              else inputs))),
   351                     True)
   353                     True)
   352             else:
   354             else:
   353                 same_inputs = True
   355                 same_inputs = True
   354             
   356 
   355             # Return item if  block data corresponds to informations given
   357             # Return item if  block data corresponds to informations given
   356             if self.Tree.GetItemText(item) == name and same_inputs:
   358             if self.Tree.GetItemText(item) == name and same_inputs:
   357                 return item
   359                 return item
   358         
   360 
   359         # Test item children if item doesn't correspond
   361         # Test item children if item doesn't correspond
   360         child, child_cookie = self.Tree.GetFirstChild(item)
   362         child, child_cookie = self.Tree.GetFirstChild(item)
   361         while child.IsOk():
   363         while child.IsOk():
   362             result = self.FindTreeItem(child, name, inputs)
   364             result = self.FindTreeItem(child, name, inputs)
   363             if result:
   365             if result:
   364                 return result
   366                 return result
   365             child, child_cookie = self.Tree.GetNextChild(item, child_cookie)
   367             child, child_cookie = self.Tree.GetNextChild(item, child_cookie)
   366         
   368 
   367         return None
   369         return None
   368     
   370 
   369     def SearchInTree(self, value, mode="first"):
   371     def SearchInTree(self, value, mode="first"):
   370         """
   372         """
   371         Search in Tree and select item that name contains string given
   373         Search in Tree and select item that name contains string given
   372         @param value: String contained in block name to find
   374         @param value: String contained in block name to find
   373         @param mode: Search mode ('first', 'previous' or 'next')
   375         @param mode: Search mode ('first', 'previous' or 'next')
   376         """
   378         """
   377         # Return immediately if root isn't valid
   379         # Return immediately if root isn't valid
   378         root = self.Tree.GetRootItem()
   380         root = self.Tree.GetRootItem()
   379         if not root.IsOk():
   381         if not root.IsOk():
   380             return False
   382             return False
   381         
   383 
   382         # Set function to navigate in Tree item sibling according to search
   384         # Set function to navigate in Tree item sibling according to search
   383         # mode defined 
   385         # mode defined
   384         sibling_function = (self.Tree.GetPrevSibling
   386         sibling_function = (self.Tree.GetPrevSibling
   385                             if mode == "previous"
   387                             if mode == "previous"
   386                             else self.Tree.GetNextSibling)
   388                             else self.Tree.GetNextSibling)
   387         
   389 
   388         # Get current selected item (for next and previous mode)
   390         # Get current selected item (for next and previous mode)
   389         item = self.Tree.GetSelection()
   391         item = self.Tree.GetSelection()
   390         if not item.IsOk() or mode == "first":
   392         if not item.IsOk() or mode == "first":
   391             item, item_cookie = self.Tree.GetFirstChild(root)
   393             item, item_cookie = self.Tree.GetFirstChild(root)
   392             selected = None
   394             selected = None
   393         else:
   395         else:
   394             selected = item
   396             selected = item
   395         
   397 
   396         # Navigate through tree items until one matching found or reach tree
   398         # Navigate through tree items until one matching found or reach tree
   397         # starting or ending
   399         # starting or ending
   398         while item.IsOk():
   400         while item.IsOk():
   399             
   401 
   400             # Get item data to get item type
   402             # Get item data to get item type
   401             item_pydata = self.Tree.GetPyData(item)
   403             item_pydata = self.Tree.GetPyData(item)
   402             
   404 
   403             # Item is a block category
   405             # Item is a block category
   404             if (item == root) or item_pydata["type"] == CATEGORY:
   406             if (item == root) or item_pydata["type"] == CATEGORY:
   405                 
   407 
   406                 # Get category first or last child according to search mode 
   408                 # Get category first or last child according to search mode
   407                 # defined
   409                 # defined
   408                 child = (self.Tree.GetLastChild(item)
   410                 child = (self.Tree.GetLastChild(item)
   409                          if mode == "previous"
   411                          if mode == "previous"
   410                          else self.Tree.GetFirstChild(item)[0])
   412                          else self.Tree.GetFirstChild(item)[0])
   411                 
   413 
   412                 # If category has no child, go to sibling category
   414                 # If category has no child, go to sibling category
   413                 item = (child if child.IsOk() else sibling_function(item))
   415                 item = (child if child.IsOk() else sibling_function(item))
   414             
   416 
   415             # Item is a block
   417             # Item is a block
   416             else:
   418             else:
   417                 
   419 
   418                 # Extract item block name
   420                 # Extract item block name
   419                 name = self.Tree.GetItemText(item)
   421                 name = self.Tree.GetItemText(item)
   420                 # Test if block name contains string given
   422                 # Test if block name contains string given
   421                 if name.upper().find(value.upper()) != -1 and item != selected:
   423                 if name.upper().find(value.upper()) != -1 and item != selected:
   422                     # Select block and collapse all categories other than block
   424                     # Select block and collapse all categories other than block
   426                         self.Tree.CollapseAllChildren(child)
   428                         self.Tree.CollapseAllChildren(child)
   427                         child, child_cookie = self.Tree.GetNextChild(root, child_cookie)
   429                         child, child_cookie = self.Tree.GetNextChild(root, child_cookie)
   428                     self.Tree.SelectItem(item)
   430                     self.Tree.SelectItem(item)
   429                     self.Tree.EnsureVisible(item)
   431                     self.Tree.EnsureVisible(item)
   430                     return True
   432                     return True
   431                 
   433 
   432                 # Go to next item sibling if block not found
   434                 # Go to next item sibling if block not found
   433                 next = sibling_function(item)
   435                 next = sibling_function(item)
   434                 
   436 
   435                 # If category has no other child, go to next category sibling
   437                 # If category has no other child, go to next category sibling
   436                 item = (next
   438                 item = (next
   437                         if next.IsOk()
   439                         if next.IsOk()
   438                         else sibling_function(self.Tree.GetItemParent(item)))
   440                         else sibling_function(self.Tree.GetItemParent(item)))
   439         
   441 
   440         return False
   442         return False
   441     
   443 
   442     def OnSearchCtrlChanged(self, event):
   444     def OnSearchCtrlChanged(self, event):
   443         """
   445         """
   444         Called when SearchCtrl text control value changed
   446         Called when SearchCtrl text control value changed
   445         @param event: TextCtrl change event
   447         @param event: TextCtrl change event
   446         """
   448         """
   447         # Search for block containing SearchCtrl value in 'first' mode
   449         # Search for block containing SearchCtrl value in 'first' mode
   448         self.SearchInTree(self.SearchCtrl.GetValue())
   450         self.SearchInTree(self.SearchCtrl.GetValue())
   449         event.Skip()
   451         event.Skip()
   450     
   452 
   451     def OnSearchButtonClick(self, event):
   453     def OnSearchButtonClick(self, event):
   452         """
   454         """
   453         Called when SearchCtrl search button was clicked
   455         Called when SearchCtrl search button was clicked
   454         @param event: Button clicked event
   456         @param event: Button clicked event
   455         """
   457         """
   456         # Search for block containing SearchCtrl value in 'next' mode
   458         # Search for block containing SearchCtrl value in 'next' mode
   457         self.SearchInTree(self.SearchCtrl.GetValue(), "next")
   459         self.SearchInTree(self.SearchCtrl.GetValue(), "next")
   458         event.Skip()
   460         event.Skip()
   459     
   461 
   460     def OnTreeItemSelected(self, event):
   462     def OnTreeItemSelected(self, event):
   461         """
   463         """
   462         Called when tree item is selected
   464         Called when tree item is selected
   463         @param event: wx.TreeEvent
   465         @param event: wx.TreeEvent
   464         """
   466         """
   466         item_pydata = self.Tree.GetPyData(event.GetItem())
   468         item_pydata = self.Tree.GetPyData(event.GetItem())
   467         self.Comment.SetValue(
   469         self.Comment.SetValue(
   468             item_pydata["comment"]
   470             item_pydata["comment"]
   469             if item_pydata is not None and item_pydata["type"] == BLOCK
   471             if item_pydata is not None and item_pydata["type"] == BLOCK
   470             else "")
   472             else "")
   471         
   473 
   472         # Call extra function defined when tree item is selected
   474         # Call extra function defined when tree item is selected
   473         if getattr(self, "_OnTreeItemSelected", None) is not None:
   475         if getattr(self, "_OnTreeItemSelected", None) is not None:
   474             self._OnTreeItemSelected(event)
   476             self._OnTreeItemSelected(event)
   475         
   477 
   476         event.Skip()
   478         event.Skip()
   477     
   479 
   478     def OnTreeBeginDrag(self, event):
   480     def OnTreeBeginDrag(self, event):
   479         """
   481         """
   480         Called when a drag is started in tree
   482         Called when a drag is started in tree
   481         @param event: wx.TreeEvent
   483         @param event: wx.TreeEvent
   482         """
   484         """
   483         selected_item = event.GetItem()
   485         selected_item = event.GetItem()
   484         item_pydata = self.Tree.GetPyData(selected_item)
   486         item_pydata = self.Tree.GetPyData(selected_item)
   485         
   487 
   486         # Item dragged is a block
   488         # Item dragged is a block
   487         if item_pydata is not None and item_pydata["type"] == BLOCK:
   489         if item_pydata is not None and item_pydata["type"] == BLOCK:
   488             # Start a drag'n drop
   490             # Start a drag'n drop
   489             data = wx.TextDataObject(str(
   491             data = wx.TextDataObject(str(
   490                 (self.Tree.GetItemText(selected_item), 
   492                 (self.Tree.GetItemText(selected_item),
   491                  item_pydata["block_type"], 
   493                  item_pydata["block_type"],
   492                  "", 
   494                  "",
   493                  item_pydata["inputs"])))
   495                  item_pydata["inputs"])))
   494             dragSource = wx.DropSource(self.Tree)
   496             dragSource = wx.DropSource(self.Tree)
   495             dragSource.SetData(data)
   497             dragSource.SetData(data)
   496             dragSource.DoDragDrop()
   498             dragSource.DoDragDrop()
   497     
   499 
   498     def OnKeyDown(self, event):
   500     def OnKeyDown(self, event):
   499         """
   501         """
   500         Called when key is pressed in SearchCtrl text control
   502         Called when key is pressed in SearchCtrl text control
   501         @param event: wx.KeyEvent
   503         @param event: wx.KeyEvent
   502         """
   504         """
   503         # Get event keycode and value in SearchCtrl
   505         # Get event keycode and value in SearchCtrl
   504         keycode = event.GetKeyCode()
   506         keycode = event.GetKeyCode()
   505         search_value = self.SearchCtrl.GetValue()
   507         search_value = self.SearchCtrl.GetValue()
   506         
   508 
   507         # Up key was pressed and SearchCtrl isn't empty, search for block in
   509         # Up key was pressed and SearchCtrl isn't empty, search for block in
   508         # 'previous' mode 
   510         # 'previous' mode
   509         if keycode == wx.WXK_UP and search_value != "":
   511         if keycode == wx.WXK_UP and search_value != "":
   510             self.SearchInTree(search_value, "previous")
   512             self.SearchInTree(search_value, "previous")
   511         
   513 
   512         # Down key was pressed and SearchCtrl isn't empty, search for block in
   514         # Down key was pressed and SearchCtrl isn't empty, search for block in
   513         # 'next' mode 
   515         # 'next' mode
   514         elif keycode == wx.WXK_DOWN and search_value != "":
   516         elif keycode == wx.WXK_DOWN and search_value != "":
   515             self.SearchInTree(search_value, "next")
   517             self.SearchInTree(search_value, "next")
   516         
   518 
   517         # Handle key normally
   519         # Handle key normally
   518         else:
   520         else:
   519             event.Skip()
   521             event.Skip()