util/FileManagementPanel.py
changeset 787 f49875b9e946
parent 784 a1d970365e41
child 789 b1b1a8f85e2f
equal deleted inserted replaced
779:7499535588e6 787:f49875b9e946
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of Beremiz, a Integrated Development Environment for
       
     5 #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import os
       
    26 import shutil
       
    27 
       
    28 import wx
       
    29 
       
    30 from controls import EditorPanel
       
    31 from utils.BitmapLibrary import GetBitmap
       
    32 
       
    33 DRIVE, FOLDER, FILE = range(3)
       
    34 
       
    35 FILTER = _("All files (*.*)|*.*|CSV files (*.csv)|*.csv")
       
    36 
       
    37 def sort_folder(x, y):
       
    38     if x[1] == y[1]:
       
    39         return cmp(x[0], y[0])
       
    40     elif x[1] != FILE:
       
    41         return -1
       
    42     else:
       
    43         return 1
       
    44 
       
    45 def splitpath(path):
       
    46     head, tail = os.path.split(path)
       
    47     if head == "":
       
    48         return [tail]
       
    49     elif tail == "":
       
    50         return splitpath(head)
       
    51     return splitpath(head) + [tail]
       
    52     
       
    53 class FolderTree(wx.Panel):
       
    54     
       
    55     def __init__(self, parent, folder, filter, editable=True):
       
    56         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
       
    57         
       
    58         main_sizer = wx.BoxSizer(wx.VERTICAL)
       
    59         
       
    60         self.Tree = wx.TreeCtrl(self, 
       
    61               style=wx.TR_HAS_BUTTONS|
       
    62                     wx.TR_SINGLE|
       
    63                     wx.SUNKEN_BORDER|
       
    64                     wx.TR_HIDE_ROOT|
       
    65                     wx.TR_LINES_AT_ROOT|
       
    66                     wx.TR_EDIT_LABELS)
       
    67         if wx.Platform == '__WXMSW__':
       
    68             self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTreeItemExpanded, self.Tree)
       
    69             self.Tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
       
    70         else:
       
    71             self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnTreeItemExpanded, self.Tree)
       
    72         self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnTreeItemCollapsed, self.Tree)
       
    73         self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeBeginLabelEdit, self.Tree)
       
    74         self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeEndLabelEdit, self.Tree)
       
    75         main_sizer.AddWindow(self.Tree, 1, flag=wx.GROW)
       
    76         
       
    77         self.Filter = wx.ComboBox(self, style=wx.CB_READONLY)
       
    78         self.Bind(wx.EVT_COMBOBOX, self.OnFilterChanged, self.Filter)
       
    79         main_sizer.AddWindow(self.Filter, flag=wx.GROW)
       
    80         
       
    81         self.SetSizer(main_sizer)
       
    82         
       
    83         self.Folder = folder
       
    84         self.Editable = editable
       
    85         
       
    86         self.TreeImageList = wx.ImageList(16, 16)
       
    87         self.TreeImageDict = {}
       
    88         for item_type, bitmap in [(DRIVE, "tree_drive"),
       
    89                                   (FOLDER, "tree_folder"),
       
    90                                   (FILE, "tree_file")]:
       
    91             self.TreeImageDict[item_type] = self.TreeImageList.Add(GetBitmap(bitmap))
       
    92         self.Tree.SetImageList(self.TreeImageList)
       
    93         
       
    94         self.Filters = {}
       
    95         filter_parts = filter.split("|")
       
    96         for idx in xrange(0, len(filter_parts), 2):
       
    97             if filter_parts[idx + 1] == "*.*":
       
    98                 self.Filters[filter_parts[idx]] = ""
       
    99             else:
       
   100                 self.Filters[filter_parts[idx]] = filter_parts[idx + 1].replace("*", "")
       
   101             self.Filter.Append(filter_parts[idx])
       
   102             if idx == 0:
       
   103                 self.Filter.SetStringSelection(filter_parts[idx])
       
   104             
       
   105         self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
       
   106     
       
   107     def _GetFolderChildren(self, folderpath, recursive=True):
       
   108         items = []
       
   109         if wx.Platform == '__WXMSW__' and folderpath == "/":
       
   110             for c in xrange(ord('a'), ord('z')):
       
   111                 drive = os.path.join("%s:\\" % chr(c))
       
   112                 if os.path.exists(drive):
       
   113                     items.append((drive, DRIVE, self._GetFolderChildren(drive, False)))
       
   114         else:
       
   115             try:
       
   116                 files = os.listdir(folderpath)
       
   117             except:
       
   118                 return []
       
   119             for filename in files:
       
   120                 if not filename.startswith("."):
       
   121                     filepath = os.path.join(folderpath, filename)
       
   122                     if os.path.isdir(filepath):
       
   123                         if recursive:
       
   124                             children = len(self._GetFolderChildren(filepath, False))
       
   125                         else:
       
   126                             children = 0
       
   127                         items.append((filename, FOLDER, children))
       
   128                     elif (self.CurrentFilter == "" or 
       
   129                           os.path.splitext(filename)[1] == self.CurrentFilter):
       
   130                         items.append((filename, FILE, None))
       
   131         if recursive:
       
   132             items.sort(sort_folder)
       
   133         return items
       
   134     
       
   135     def GetTreeCtrl(self):
       
   136         return self.Tree
       
   137     
       
   138     def RefreshTree(self):
       
   139         root = self.Tree.GetRootItem()
       
   140         if not root.IsOk():
       
   141             root = self.Tree.AddRoot("")
       
   142         self.GenerateTreeBranch(root, self.Folder)
       
   143         
       
   144     def GenerateTreeBranch(self, root, folderpath):
       
   145         item, item_cookie = self.Tree.GetFirstChild(root)
       
   146         for idx, (filename, item_type, children) in enumerate(self._GetFolderChildren(folderpath)):
       
   147             if not item.IsOk():
       
   148                 item = self.Tree.AppendItem(root, filename, self.TreeImageDict[item_type])
       
   149                 if wx.Platform != '__WXMSW__':
       
   150                     item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   151             elif self.Tree.GetItemText(item) != filename:
       
   152                 item = self.Tree.InsertItemBefore(root, idx, filename, self.TreeImageDict[item_type])
       
   153             filepath = os.path.join(folderpath, filename)
       
   154             if item_type != FILE:
       
   155                 if self.Tree.IsExpanded(item):
       
   156                     self.GenerateTreeBranch(item, filepath)
       
   157                 elif children > 0:
       
   158                     self.Tree.SetItemHasChildren(item)
       
   159             item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   160         to_delete = []
       
   161         while item.IsOk():
       
   162             to_delete.append(item)
       
   163             item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   164         for item in to_delete:
       
   165             self.Tree.Delete(item)
       
   166 
       
   167     def ExpandItem(self, item):
       
   168         self.GenerateTreeBranch(item, self.GetPath(item))
       
   169         self.Tree.Expand(item)
       
   170     
       
   171     def OnTreeItemActivated(self, event):
       
   172         self.ExpandItem(event.GetItem())
       
   173         event.Skip()
       
   174     
       
   175     def OnTreeLeftDown(self, event):
       
   176         item, flags = self.Tree.HitTest(event.GetPosition())
       
   177         if flags & wx.TREE_HITTEST_ONITEMBUTTON and not self.Tree.IsExpanded(item):
       
   178             self.ExpandItem(item)
       
   179         else:
       
   180             event.Skip()
       
   181     
       
   182     def OnTreeItemExpanded(self, event):
       
   183         item = event.GetItem()
       
   184         self.GenerateTreeBranch(item, self.GetPath(item))
       
   185         event.Skip()
       
   186 
       
   187     def OnTreeItemCollapsed(self, event):
       
   188         item = event.GetItem()
       
   189         self.Tree.DeleteChildren(item)
       
   190         self.Tree.SetItemHasChildren(item)
       
   191         event.Skip()
       
   192 
       
   193     def OnTreeBeginLabelEdit(self, event):
       
   194         item = event.GetItem()
       
   195         if self.Editable and not self.Tree.ItemHasChildren(item):
       
   196             event.Skip()
       
   197         else:
       
   198             event.Veto()
       
   199     
       
   200     def OnTreeEndLabelEdit(self, event):
       
   201         event.Veto()
       
   202     
       
   203     def OnFilterChanged(self, event):
       
   204         self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
       
   205         self.RefreshTree()
       
   206         event.Skip()
       
   207     
       
   208     def _SelectItem(self, root, parts):
       
   209         if len(parts) == 0:
       
   210             self.Tree.SelectItem(root)
       
   211         else:
       
   212             item, item_cookie = self.Tree.GetFirstChild(root)
       
   213             while item.IsOk():
       
   214                 if self.Tree.GetItemText(item) == parts[0]:
       
   215                     if (self.Tree.ItemHasChildren(item) and 
       
   216                         not self.Tree.IsExpanded(item)):
       
   217                         self.Tree.Expand(item)
       
   218                         wx.CallAfter(self._SelectItem, item, parts[1:])
       
   219                     else:
       
   220                         self._SelectItem(item, parts[1:])
       
   221                     return
       
   222                 item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   223     
       
   224     def SetPath(self, path):
       
   225         if path.startswith(self.Folder):
       
   226             root = self.Tree.GetRootItem()
       
   227             if root.IsOk():
       
   228                 relative_path = path.replace(os.path.join(self.Folder, ""), "")
       
   229                 self._SelectItem(root, splitpath(relative_path))
       
   230     
       
   231     def GetPath(self, item=None):
       
   232         if item is None:
       
   233             item = self.Tree.GetSelection()
       
   234         if item.IsOk():
       
   235             filepath = self.Tree.GetItemText(item)
       
   236             parent = self.Tree.GetItemParent(item)
       
   237             while parent.IsOk() and parent != self.Tree.GetRootItem():
       
   238                 filepath = os.path.join(self.Tree.GetItemText(parent), filepath)
       
   239                 parent = self.Tree.GetItemParent(parent)
       
   240             return os.path.join(self.Folder, filepath)
       
   241         return self.Folder
       
   242 
       
   243 class FileManagementPanel(EditorPanel):
       
   244     
       
   245     def _init_Editor(self, parent):
       
   246         self.Editor = wx.Panel(parent)
       
   247         
       
   248         main_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   249         
       
   250         left_sizer = wx.BoxSizer(wx.VERTICAL)
       
   251         main_sizer.AddSizer(left_sizer, 1, border=5, flag=wx.GROW|wx.ALL)
       
   252         
       
   253         managed_dir_label = wx.StaticText(self.Editor, label=self.TagName + ":")
       
   254         left_sizer.AddWindow(managed_dir_label, border=5, flag=wx.GROW|wx.BOTTOM)
       
   255         
       
   256         self.ManagedDir = FolderTree(self.Editor, self.Folder, FILTER)
       
   257         left_sizer.AddWindow(self.ManagedDir, 1, flag=wx.GROW)
       
   258         
       
   259         managed_treectrl = self.ManagedDir.GetTreeCtrl()
       
   260         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, managed_treectrl)
       
   261         if self.EnableDragNDrop:
       
   262             self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, managed_treectrl)
       
   263         
       
   264         button_sizer = wx.BoxSizer(wx.VERTICAL)
       
   265         main_sizer.AddSizer(button_sizer, border=5, 
       
   266               flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL)
       
   267         
       
   268         for idx, (name, bitmap, help) in enumerate([
       
   269                 ("DeleteButton", "remove_element", _("Remove file from left folder")),
       
   270                 ("LeftCopyButton", "LeftCopy", _("Copy file from right folder to left")),
       
   271                 ("RightCopyButton", "RightCopy", _("copy file from left folder to right")),
       
   272                 ("EditButton", "edit", _("Edit file"))]):
       
   273             button = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   274                   bitmap=GetBitmap(bitmap), 
       
   275                   size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   276             button.SetToolTipString(help)
       
   277             setattr(self, name, button)
       
   278             if idx > 0:
       
   279                 flag = wx.TOP
       
   280             else:
       
   281                 flag = 0
       
   282             self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
       
   283             button_sizer.AddWindow(button, border=20, flag=flag)
       
   284         
       
   285         right_sizer = wx.BoxSizer(wx.VERTICAL)
       
   286         main_sizer.AddSizer(right_sizer, 1, border=5, flag=wx.GROW|wx.ALL)
       
   287         
       
   288         if wx.Platform == '__WXMSW__':
       
   289             system_dir_label = wx.StaticText(self.Editor, label=_("My Computer:"))
       
   290         else:
       
   291             system_dir_label = wx.StaticText(self.Editor, label=_("Home Directory:"))
       
   292         right_sizer.AddWindow(system_dir_label, border=5, flag=wx.GROW|wx.BOTTOM)
       
   293         
       
   294         self.SystemDir = FolderTree(self.Editor, self.HomeDirectory, FILTER, False)
       
   295         right_sizer.AddWindow(self.SystemDir, 1, flag=wx.GROW)
       
   296         
       
   297         system_treectrl = self.SystemDir.GetTreeCtrl()
       
   298         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, system_treectrl)
       
   299         
       
   300         self.Editor.SetSizer(main_sizer)
       
   301         
       
   302     def __init__(self, parent, controler, name, folder, enable_dragndrop=False):
       
   303         self.Folder = os.path.realpath(folder)
       
   304         self.EnableDragNDrop = enable_dragndrop
       
   305         
       
   306         if wx.Platform == '__WXMSW__':
       
   307             self.HomeDirectory = "/"
       
   308         else:
       
   309             self.HomeDirectory = os.path.expanduser("~")
       
   310         
       
   311         EditorPanel.__init__(self, parent, name, None, None)
       
   312         
       
   313         self.Controler = controler
       
   314         
       
   315         self.EditableFileExtensions = []
       
   316         self.EditButton.Hide()
       
   317         
       
   318         self.SetIcon(GetBitmap("FOLDER"))
       
   319     
       
   320     def __del__(self):
       
   321         self.Controler.OnCloseEditor(self)
       
   322     
       
   323     def GetTitle(self):
       
   324         return self.TagName
       
   325     
       
   326     def SetEditableFileExtensions(self, extensions):
       
   327         self.EditableFileExtensions = extensions
       
   328         if len(self.EditableFileExtensions) > 0:
       
   329             self.EditButton.Show()
       
   330     
       
   331     def RefreshView(self):
       
   332         self.ManagedDir.RefreshTree()
       
   333         self.SystemDir.RefreshTree()
       
   334         self.RefreshButtonsState()
       
   335     
       
   336     def RefreshButtonsState(self):
       
   337         managed_filepath = self.ManagedDir.GetPath()
       
   338         system_filepath = self.SystemDir.GetPath()
       
   339         
       
   340         self.DeleteButton.Enable(os.path.isfile(managed_filepath))
       
   341         self.LeftCopyButton.Enable(os.path.isfile(system_filepath))
       
   342         self.RightCopyButton.Enable(os.path.isfile(managed_filepath))
       
   343         if len(self.EditableFileExtensions) > 0:
       
   344             self.EditButton.Enable(
       
   345                 os.path.isfile(managed_filepath) and
       
   346                 os.path.splitext(managed_filepath)[1] in self.EditableFileExtensions)
       
   347     
       
   348     def OnTreeItemChanged(self, event):
       
   349         self.RefreshButtonsState()
       
   350         event.Skip()
       
   351         
       
   352     def OnDeleteButton(self, event):
       
   353         filepath = self.ManagedDir.GetPath()
       
   354         if os.path.isfile(filepath):
       
   355             folder, filename = os.path.split(filepath)
       
   356             
       
   357             dialog = wx.MessageDialog(self, 
       
   358                   _("Do you really want to delete the file '%s'?") % filename, 
       
   359                   _("Delete File"), wx.YES_NO|wx.ICON_QUESTION)
       
   360             remove = dialog.ShowModal() == wx.ID_YES
       
   361             dialog.Destroy()
       
   362             
       
   363             if remove:
       
   364                 os.remove(filepath)
       
   365                 self.ManagedDir.RefreshTree()
       
   366         event.Skip()
       
   367 
       
   368     def OnEditButton(self, event):
       
   369         filepath = self.ManagedDir.GetPath()
       
   370         if (os.path.isfile(filepath) and 
       
   371             os.path.splitext(filepath)[1] in self.EditableFileExtensions):
       
   372             self.Controler._OpenView(filepath)
       
   373         event.Skip()
       
   374         
       
   375     def CopyFile(self, src, dst):
       
   376         if os.path.isfile(src):
       
   377             src_folder, src_filename = os.path.split(src)
       
   378             if os.path.isfile(dst):
       
   379                 dst_folder, dst_filename = os.path.split(dst)
       
   380             else:
       
   381                 dst_folder = dst
       
   382             
       
   383             dst_filepath = os.path.join(dst_folder, src_filename)
       
   384             if os.path.isfile(dst_filepath):
       
   385                 dialog = wx.MessageDialog(self, 
       
   386                       _("The file '%s' already exist.\nDo you want to replace it?") % src_filename, 
       
   387                       _("Replace File"), wx.YES_NO|wx.ICON_QUESTION)
       
   388                 copy = dialog.ShowModal() == wx.ID_YES
       
   389                 dialog.Destroy()
       
   390             else:
       
   391                 copy = True
       
   392                 
       
   393             if copy:
       
   394                 shutil.copyfile(src, dst_filepath)
       
   395                 return dst_filepath
       
   396         return None
       
   397 
       
   398     def OnLeftCopyButton(self, event):
       
   399         filepath = self.CopyFile(self.SystemDir.GetPath(), self.ManagedDir.GetPath())
       
   400         if filepath is not None:
       
   401             self.ManagedDir.RefreshTree()
       
   402             self.ManagedDir.SetPath(filepath)
       
   403         event.Skip()
       
   404 
       
   405     def OnRightCopyButton(self, event):
       
   406         filepath = self.CopyFile(self.ManagedDir.GetPath(), self.SystemDir.GetPath())
       
   407         if filepath is not None:
       
   408             self.SystemDir.RefreshTree()
       
   409             self.SystemDir.SetPath(filepath)
       
   410         event.Skip()
       
   411     
       
   412     def OnTreeBeginDrag(self, event):
       
   413         filepath = self.ManagedDir.GetPath()
       
   414         if os.path.isfile(filepath):
       
   415             relative_filepath = filepath.replace(os.path.join(self.Folder, ""), "")
       
   416             data = wx.TextDataObject(str(("'%s'" % relative_filepath, "Constant")))
       
   417             dragSource = wx.DropSource(self)
       
   418             dragSource.SetData(data)
       
   419             dragSource.DoDragDrop()
       
   420