Laurent@814: #
Laurent@814: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
Laurent@814: #
Laurent@814: #See COPYING file for copyrights details.
Laurent@814: #
Laurent@814: #This library is free software; you can redistribute it and/or
Laurent@814: #modify it under the terms of the GNU General Public
Laurent@814: #License as published by the Free Software Foundation; either
Laurent@814: #version 2.1 of the License, or (at your option) any later version.
Laurent@814: #
Laurent@814: #This library is distributed in the hope that it will be useful,
Laurent@814: #but WITHOUT ANY WARRANTY; without even the implied warranty of
Laurent@814: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Laurent@814: #General Public License for more details.
Laurent@814: #
Laurent@814: #You should have received a copy of the GNU General Public
Laurent@814: #License along with this library; if not, write to the Free Software
Laurent@814: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Laurent@814: 
Laurent@814: import os
Laurent@814: 
Laurent@814: import wx
Laurent@814: 
Laurent@814: from plcopen.structures import LOCATIONDATATYPES
Laurent@814: from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
laurent@829: from util.BitmapLibrary import GetBitmap
Laurent@814: 
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: #                                   Helpers
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: 
Laurent@855: def GetDirFilterChoiceOptions():
Laurent@814:     _ = lambda x : x
Laurent@814:     return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]), 
Laurent@814:             (_("Input"), [LOCATION_VAR_INPUT]), 
Laurent@814:             (_("Output"), [LOCATION_VAR_OUTPUT]), 
Laurent@814:             (_("Memory"), [LOCATION_VAR_MEMORY])]
Laurent@855: DIRFILTERCHOICE_OPTIONS = dict([(_(option), filter) for option, filter in GetDirFilterChoiceOptions()])
Laurent@855: 
Laurent@855: def GetTypeFilterChoiceOptions():
Laurent@855:     _ = lambda x : x
Laurent@855:     return [_("All"), 
Laurent@855:             _("Type and derivated"), 
Laurent@855:             _("Type strict")]
Laurent@814: 
Laurent@814: # turn LOCATIONDATATYPES inside-out
Laurent@814: LOCATION_SIZES = {}
Laurent@814: for size, types in LOCATIONDATATYPES.iteritems():
Laurent@814:     for type in types:
Laurent@814:         LOCATION_SIZES[type] = size
Laurent@814: 
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: #                            Browse Locations Dialog
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: 
Laurent@814: class BrowseLocationsDialog(wx.Dialog):
Laurent@814:     
Laurent@855:     def __init__(self, parent, var_type, controller):
Laurent@814:         wx.Dialog.__init__(self, parent,  
Laurent@855:               size=wx.Size(600, 400), title=_('Browse Locations'),
Laurent@855:               style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
Laurent@814:         
Laurent@814:         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
Laurent@814:         main_sizer.AddGrowableCol(0)
Laurent@814:         main_sizer.AddGrowableRow(1)
Laurent@814:         
Laurent@814:         locations_label = wx.StaticText(self, label=_('Locations available:'))
Laurent@814:         main_sizer.AddWindow(locations_label, border=20, 
Laurent@814:               flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
Laurent@814:         
Laurent@814:         self.LocationsTree = wx.TreeCtrl(self, 
Laurent@814:               style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT)
Laurent@814:         self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated, 
Laurent@814:                   self.LocationsTree)
Laurent@814:         main_sizer.AddWindow(self.LocationsTree, border=20, 
Laurent@814:               flag=wx.LEFT|wx.RIGHT|wx.GROW)
Laurent@814:         
Laurent@855:         button_gridsizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0)
Laurent@855:         button_gridsizer.AddGrowableCol(1)
Laurent@855:         button_gridsizer.AddGrowableCol(3)
Laurent@814:         button_gridsizer.AddGrowableRow(0)
Laurent@814:         main_sizer.AddSizer(button_gridsizer, border=20, 
Laurent@814:               flag=wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.GROW)
Laurent@814:         
Laurent@814:         direction_label = wx.StaticText(self, label=_('Direction:'))
Laurent@814:         button_gridsizer.AddWindow(direction_label,
Laurent@814:               flag=wx.ALIGN_CENTER_VERTICAL)
Laurent@814:         
Laurent@855:         self.DirFilterChoice = wx.ComboBox(self, size=wx.Size(0, -1), style=wx.CB_READONLY)
Laurent@855:         self.Bind(wx.EVT_COMBOBOX, self.OnFilterChoice, self.DirFilterChoice)
Laurent@855:         button_gridsizer.AddWindow(self.DirFilterChoice,
Laurent@855:               flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL)
Laurent@855:         
Laurent@855:         filter_label = wx.StaticText(self, label=_('Type:'))
Laurent@855:         button_gridsizer.AddWindow(filter_label,
Laurent@814:               flag=wx.ALIGN_CENTER_VERTICAL)
Laurent@814:         
Laurent@855:         self.TypeFilterChoice = wx.ComboBox(self, size=wx.Size(0, -1), style=wx.CB_READONLY)
Laurent@855:         self.Bind(wx.EVT_COMBOBOX, self.OnFilterChoice, self.TypeFilterChoice)
Laurent@855:         button_gridsizer.AddWindow(self.TypeFilterChoice,
Laurent@855:               flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL)
Laurent@855:         
Laurent@814:         button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
Laurent@814:         self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton())
Laurent@855:         button_gridsizer.AddSizer(button_sizer, flag=wx.ALIGN_RIGHT)
Laurent@814:         
Laurent@814:         self.SetSizer(main_sizer)
Laurent@814:         
Laurent@855:         self.Controller = controller
Laurent@814:         self.VarType = var_type
Laurent@855:         self.BaseVarType = self.Controller.GetBaseType(self.VarType)
Laurent@855:         self.VarTypeSize = LOCATION_SIZES[self.BaseVarType]
Laurent@855:         self.Locations = self.Controller.GetVariableLocationTree()
Laurent@814:         
Laurent@814:         # Define Tree item icon list
Laurent@814:         self.TreeImageList = wx.ImageList(16, 16)
Laurent@814:         self.TreeImageDict = {}
Laurent@814:         
Laurent@814:         # Icons for items
Laurent@814:         for imgname, itemtype in [
Laurent@814:             ("CONFIGURATION", LOCATION_CONFNODE), 
Laurent@814:             ("RESOURCE",      LOCATION_MODULE), 
Laurent@814:             ("PROGRAM",       LOCATION_GROUP), 
Laurent@814:             ("VAR_INPUT",     LOCATION_VAR_INPUT), 
Laurent@814:             ("VAR_OUTPUT",    LOCATION_VAR_OUTPUT), 
Laurent@814:             ("VAR_LOCAL",     LOCATION_VAR_MEMORY)]:
laurent@829:             self.TreeImageDict[itemtype]=self.TreeImageList.Add(GetBitmap(imgname))
Laurent@814:         
Laurent@814:         # Assign icon list to TreeCtrls
Laurent@814:         self.LocationsTree.SetImageList(self.TreeImageList)
Laurent@814:         
Laurent@814:         # Set a options for the choice
Laurent@855:         for option, filter in GetDirFilterChoiceOptions():
Laurent@855:             self.DirFilterChoice.Append(_(option))
Laurent@855:         self.DirFilterChoice.SetStringSelection(_("All"))
Laurent@855:         for option in GetTypeFilterChoiceOptions():
Laurent@855:             self.TypeFilterChoice.Append(_(option))
Laurent@855:         self.TypeFilterChoice.SetStringSelection(_("All"))
Laurent@855:         self.RefreshFilters()
Laurent@814:         
Laurent@814:         self.RefreshLocationsTree()
Laurent@814:     
Laurent@855:     def RefreshFilters(self):
Laurent@855:         self.DirFilter = DIRFILTERCHOICE_OPTIONS[self.DirFilterChoice.GetStringSelection()]
Laurent@855:         self.TypeFilter = self.TypeFilterChoice.GetSelection()
Laurent@814:     
Laurent@814:     def RefreshLocationsTree(self):
Laurent@814:         root = self.LocationsTree.GetRootItem()
Laurent@814:         if not root.IsOk():
Laurent@814:             root = self.LocationsTree.AddRoot("")
Laurent@814:         self.GenerateLocationsTreeBranch(root, self.Locations)
Laurent@814:     
Laurent@855:     def FilterType(self, location_type, location_size):
Laurent@855:         if self.TypeFilter == 0:
Laurent@855:             return True
Laurent@855:         
Laurent@855:         if location_size != self.VarTypeSize:
Laurent@855:             return False
Laurent@855:         
Laurent@855:         if self.TypeFilter == 1:
Laurent@855:             return self.Controller.IsOfType(location_type, self.BaseVarType)
Laurent@855:         elif self.TypeFilter == 2:
Laurent@855:             return location_type == self.VarType
Laurent@855:         
Laurent@855:         return True
Laurent@855:     
Laurent@814:     def GenerateLocationsTreeBranch(self, root, locations):
Laurent@814:         to_delete = []
Laurent@814:         item, root_cookie = self.LocationsTree.GetFirstChild(root)
Laurent@814:         for loc_infos in locations:
Laurent@814:             infos = loc_infos.copy()
Laurent@814:             if infos["type"] in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP] or\
Laurent@855:                infos["type"] in self.DirFilter and self.FilterType(infos["IEC_type"], infos["size"]):
Laurent@814:                 children = [child for child in infos.pop("children")]
Laurent@814:                 if not item.IsOk():
Laurent@814:                     item = self.LocationsTree.AppendItem(root, infos["name"])
Laurent@814:                     if wx.Platform != '__WXMSW__':
Laurent@814:                         item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie)
Laurent@814:                 else:
Laurent@814:                     self.LocationsTree.SetItemText(item, infos["name"])
Laurent@814:                 self.LocationsTree.SetPyData(item, infos)
Laurent@814:                 self.LocationsTree.SetItemImage(item, self.TreeImageDict[infos["type"]])
Laurent@814:                 self.GenerateLocationsTreeBranch(item, children)
Laurent@814:                 item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie)
Laurent@814:         while item.IsOk():
Laurent@814:             to_delete.append(item)
Laurent@814:             item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie)
Laurent@814:         for item in to_delete:
Laurent@814:             self.LocationsTree.Delete(item)
Laurent@814:     
Laurent@814:     def OnLocationsTreeItemActivated(self, event):
Laurent@814:         infos = self.LocationsTree.GetPyData(event.GetItem())
Laurent@814:         if infos["type"] not in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP]:
Laurent@814:             wx.CallAfter(self.EndModal, wx.ID_OK)
Laurent@814:         event.Skip()
Laurent@814:     
Laurent@855:     def OnFilterChoice(self, event):
Laurent@855:         self.RefreshFilters()
Laurent@814:         self.RefreshLocationsTree()
Laurent@855:     
Laurent@814:     def GetValues(self):
Laurent@814:         selected = self.LocationsTree.GetSelection()
Laurent@814:         return self.LocationsTree.GetPyData(selected)
Laurent@814:         
Laurent@814:     def OnOK(self, event):
Laurent@814:         selected = self.LocationsTree.GetSelection()
Laurent@814:         var_infos = None
Laurent@814:         if selected.IsOk():
Laurent@814:             var_infos = self.LocationsTree.GetPyData(selected)
Laurent@814:         if var_infos is None or var_infos["type"] in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP]:
Laurent@814:             dialog = wx.MessageDialog(self, _("A location must be selected!"), _("Error"), wx.OK|wx.ICON_ERROR)
Laurent@814:             dialog.ShowModal()
Laurent@814:             dialog.Destroy()
Laurent@814:         else:
Laurent@814:             self.EndModal(wx.ID_OK)