util/FileManagementPanel.py
changeset 782 6f0e10085df9
child 783 f5cea1a6851e
equal deleted inserted replaced
781:cdc6393705ce 782:6f0e10085df9
       
     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 FILTER = _("All files (*.*)|*.*|CSV files (*.csv)|*.csv")
       
    34 
       
    35 def sort_folder(x, y):
       
    36     if x[1] == y[1]:
       
    37         return cmp(x[0], y[0])
       
    38     elif x[1]:
       
    39         return -1
       
    40     else:
       
    41         return 1
       
    42 
       
    43 def splitpath(path):
       
    44     head, tail = os.path.split(path)
       
    45     if head == "":
       
    46         return [tail]
       
    47     elif tail == "":
       
    48         return splitpath(head)
       
    49     return splitpath(head) + [tail]
       
    50     
       
    51 class FolderTree(wx.Panel):
       
    52     
       
    53     def __init__(self, parent, folder, filter, editable=True):
       
    54         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
       
    55         
       
    56         main_sizer = wx.BoxSizer(wx.VERTICAL)
       
    57         
       
    58         self.Tree = wx.TreeCtrl(self, 
       
    59               style=wx.TR_HAS_BUTTONS|
       
    60                     wx.TR_SINGLE|
       
    61                     wx.SUNKEN_BORDER|
       
    62                     wx.TR_HIDE_ROOT|
       
    63                     wx.TR_LINES_AT_ROOT|
       
    64                     wx.TR_EDIT_LABELS)
       
    65         self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnTreeItemExpanded, self.Tree)
       
    66         self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnTreeItemCollapsed, self.Tree)
       
    67         self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeBeginLabelEdit, self.Tree)
       
    68         self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeEndLabelEdit, self.Tree)
       
    69         main_sizer.AddWindow(self.Tree, 1, flag=wx.GROW)
       
    70         
       
    71         self.Filter = wx.ComboBox(self, style=wx.CB_READONLY)
       
    72         self.Bind(wx.EVT_COMBOBOX, self.OnFilterChanged, self.Filter)
       
    73         main_sizer.AddWindow(self.Filter, flag=wx.GROW)
       
    74         
       
    75         self.SetSizer(main_sizer)
       
    76         
       
    77         self.Folder = folder
       
    78         self.Editable = editable
       
    79         
       
    80         self.TreeImageList = wx.ImageList(16, 16)
       
    81         self.FOLDER_IMAGE = self.TreeImageList.Add(GetBitmap("tree_folder"))
       
    82         self.FILE_IMAGE = self.TreeImageList.Add(GetBitmap("tree_file"))
       
    83         self.Tree.SetImageList(self.TreeImageList)
       
    84         
       
    85         self.Filters = {}
       
    86         filter_parts = filter.split("|")
       
    87         for idx in xrange(0, len(filter_parts), 2):
       
    88             if filter_parts[idx + 1] == "*.*":
       
    89                 self.Filters[filter_parts[idx]] = ""
       
    90             else:
       
    91                 self.Filters[filter_parts[idx]] = filter_parts[idx + 1].replace("*", "")
       
    92             self.Filter.Append(filter_parts[idx])
       
    93             if idx == 0:
       
    94                 self.Filter.SetStringSelection(filter_parts[idx])
       
    95             
       
    96         self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
       
    97     
       
    98     def _GetFolderChildren(self, folderpath, recursive=True):
       
    99         items = []
       
   100         for filename in os.listdir(folderpath):
       
   101             if not filename.startswith("."):
       
   102                 filepath = os.path.join(folderpath, filename)
       
   103                 if os.path.isdir(filepath):
       
   104                     if recursive:
       
   105                         children = len(self._GetFolderChildren(filepath, False))
       
   106                     else:
       
   107                         children = 0
       
   108                     items.append((filename, True, children))
       
   109                 elif (self.CurrentFilter == "" or 
       
   110                       os.path.splitext(filename)[1] == self.CurrentFilter):
       
   111                     items.append((filename, False, None))
       
   112         if recursive:
       
   113             items.sort(sort_folder)
       
   114         return items
       
   115     
       
   116     def GetTreeCtrl(self):
       
   117         return self.Tree
       
   118     
       
   119     def RefreshTree(self):
       
   120         root = self.Tree.GetRootItem()
       
   121         if not root.IsOk():
       
   122             root = self.Tree.AddRoot("")
       
   123         self.GenerateTreeBranch(root, self.Folder)
       
   124         
       
   125     def GenerateTreeBranch(self, root, folderpath):
       
   126         item, item_cookie = self.Tree.GetFirstChild(root)
       
   127         for idx, (filename, isdir, children) in enumerate(self._GetFolderChildren(folderpath)):
       
   128             new = False
       
   129             if not item.IsOk():
       
   130                 item = self.Tree.AppendItem(root, filename)
       
   131                 if wx.Platform != '__WXMSW__':
       
   132                     item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   133                 new = True
       
   134             elif self.Tree.GetItemText(item) != filename:
       
   135                 item = self.Tree.InsertItemBefore(root, idx, filename)
       
   136                 new = True
       
   137             filepath = os.path.join(folderpath, filename)
       
   138             if isdir:
       
   139                 if new:
       
   140                     self.Tree.SetItemImage(item, self.FOLDER_IMAGE)
       
   141                     if children > 0:
       
   142                         self.Tree.SetItemHasChildren(item)
       
   143                 elif self.Tree.IsExpanded(item):
       
   144                     self.GenerateTreeBranch(item, filepath)
       
   145             elif new:
       
   146                 self.Tree.SetItemImage(item, self.FILE_IMAGE)
       
   147             item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   148         to_delete = []
       
   149         while item.IsOk():
       
   150             to_delete.append(item)
       
   151             item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   152         for item in to_delete:
       
   153             self.Tree.Delete(item)
       
   154 
       
   155     def OnTreeItemExpanded(self, event):
       
   156         item = event.GetItem()
       
   157         self.GenerateTreeBranch(item, self.GetPath(item))
       
   158         event.Skip()
       
   159 
       
   160     def OnTreeItemCollapsed(self, event):
       
   161         item = event.GetItem()
       
   162         self.Tree.DeleteChildren(item)
       
   163         self.Tree.SetItemHasChildren(item)
       
   164         event.Skip()
       
   165 
       
   166     def OnTreeBeginLabelEdit(self, event):
       
   167         item = event.GetItem()
       
   168         if self.Editable and not self.Tree.ItemHasChildren(item):
       
   169             event.Skip()
       
   170         else:
       
   171             event.Veto()
       
   172     
       
   173     def OnTreeEndLabelEdit(self, event):
       
   174         event.Veto()
       
   175     
       
   176     def OnFilterChanged(self, event):
       
   177         self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
       
   178         self.RefreshTree()
       
   179         event.Skip()
       
   180     
       
   181     def _SelectItem(self, root, parts):
       
   182         if len(parts) == 0:
       
   183             self.Tree.SelectItem(root)
       
   184         else:
       
   185             item, item_cookie = self.Tree.GetFirstChild(root)
       
   186             while item.IsOk():
       
   187                 if self.Tree.GetItemText(item) == parts[0]:
       
   188                     if (self.Tree.ItemHasChildren(item) and 
       
   189                         not self.Tree.IsExpanded(item)):
       
   190                         self.Tree.Expand(item)
       
   191                         wx.CallAfter(self._SelectItem, item, parts[1:])
       
   192                     else:
       
   193                         self._SelectItem(item, parts[1:])
       
   194                     return
       
   195                 item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   196     
       
   197     def SetPath(self, path):
       
   198         if path.startswith(self.Folder):
       
   199             root = self.Tree.GetRootItem()
       
   200             if root.IsOk():
       
   201                 relative_path = path.replace(os.path.join(self.Folder, ""), "")
       
   202                 self._SelectItem(root, splitpath(relative_path))
       
   203     
       
   204     def GetPath(self, item=None):
       
   205         if item is None:
       
   206             item = self.Tree.GetSelection()
       
   207         if item.IsOk():
       
   208             filepath = self.Tree.GetItemText(item)
       
   209             parent = self.Tree.GetItemParent(item)
       
   210             while parent.IsOk() and parent != self.Tree.GetRootItem():
       
   211                 filepath = os.path.join(self.Tree.GetItemText(parent), filepath)
       
   212                 parent = self.Tree.GetItemParent(parent)
       
   213             return os.path.join(self.Folder, filepath)
       
   214         return self.Folder
       
   215 
       
   216 class FileManagementPanel(EditorPanel):
       
   217     
       
   218     def _init_Editor(self, parent):
       
   219         self.Editor = wx.Panel(parent)
       
   220         
       
   221         main_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   222         
       
   223         left_sizer = wx.BoxSizer(wx.VERTICAL)
       
   224         main_sizer.AddSizer(left_sizer, 1, border=5, flag=wx.GROW|wx.ALL)
       
   225         
       
   226         managed_dir_label = wx.StaticText(self.Editor, label=self.TagName + ":")
       
   227         left_sizer.AddWindow(managed_dir_label, border=5, flag=wx.GROW|wx.BOTTOM)
       
   228         
       
   229         self.ManagedDir = FolderTree(self.Editor, self.Folder, FILTER)
       
   230         left_sizer.AddWindow(self.ManagedDir, 1, flag=wx.GROW)
       
   231         
       
   232         managed_treectrl = self.ManagedDir.GetTreeCtrl()
       
   233         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, managed_treectrl)
       
   234         if self.EnableDragNDrop:
       
   235             self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, managed_treectrl)
       
   236         
       
   237         button_sizer = wx.BoxSizer(wx.VERTICAL)
       
   238         main_sizer.AddSizer(button_sizer, border=5, 
       
   239               flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL)
       
   240         
       
   241         for idx, (name, bitmap, help) in enumerate([
       
   242                 ("DeleteButton", "remove_element", _("Remove file from left folder")),
       
   243                 ("LeftCopyButton", "LeftCopy", _("Copy file from right folder to left")),
       
   244                 ("RightCopyButton", "RightCopy", _("copy file from left folder to right"))]):
       
   245             button = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   246                   bitmap=GetBitmap(bitmap), 
       
   247                   size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   248             button.SetToolTipString(help)
       
   249             setattr(self, name, button)
       
   250             if idx > 0:
       
   251                 flag = wx.TOP
       
   252             else:
       
   253                 flag = 0
       
   254             self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
       
   255             button_sizer.AddWindow(button, border=20, flag=flag)
       
   256         
       
   257         right_sizer = wx.BoxSizer(wx.VERTICAL)
       
   258         main_sizer.AddSizer(right_sizer, 1, border=5, flag=wx.GROW|wx.ALL)
       
   259         
       
   260         if wx.Platform == '__WXMSW__':
       
   261             system_dir_label = wx.StaticText(self.Editor, label=_("My Computer:"))
       
   262         else:
       
   263             system_dir_label = wx.StaticText(self.Editor, label=_("Home Directory:"))
       
   264         right_sizer.AddWindow(system_dir_label, border=5, flag=wx.GROW|wx.BOTTOM)
       
   265         
       
   266         self.SystemDir = FolderTree(self.Editor, self.HomeDirectory, FILTER, False)
       
   267         right_sizer.AddWindow(self.SystemDir, 1, flag=wx.GROW)
       
   268         
       
   269         system_treectrl = self.SystemDir.GetTreeCtrl()
       
   270         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, system_treectrl)
       
   271         
       
   272         self.Editor.SetSizer(main_sizer)
       
   273         
       
   274     def __init__(self, parent, controler, name, folder, enable_dragndrop=False):
       
   275         self.Folder = os.path.realpath(folder)
       
   276         self.EnableDragNDrop = enable_dragndrop
       
   277         self.HomeDirectory = os.path.expanduser("~")
       
   278         
       
   279         EditorPanel.__init__(self, parent, name, None, None)
       
   280         
       
   281         self.Controler = controler
       
   282         
       
   283         self.SetIcon(GetBitmap("FOLDER"))
       
   284     
       
   285     def __del__(self):
       
   286         self.Controler.OnCloseEditor(self)
       
   287     
       
   288     def GetTitle(self):
       
   289         return self.TagName
       
   290     
       
   291     def RefreshView(self):
       
   292         self.ManagedDir.RefreshTree()
       
   293         self.SystemDir.RefreshTree()
       
   294         self.RefreshButtonsState()
       
   295     
       
   296     def RefreshButtonsState(self):
       
   297         managed_filepath = self.ManagedDir.GetPath()
       
   298         system_filepath = self.SystemDir.GetPath()
       
   299         
       
   300         self.DeleteButton.Enable(os.path.isfile(managed_filepath))
       
   301         self.LeftCopyButton.Enable(os.path.isfile(system_filepath))
       
   302         self.RightCopyButton.Enable(os.path.isfile(managed_filepath))
       
   303     
       
   304     def OnTreeItemChanged(self, event):
       
   305         self.RefreshButtonsState()
       
   306         event.Skip()
       
   307         
       
   308     def OnDeleteButton(self, event):
       
   309         filepath = self.ManagedDir.GetPath()
       
   310         if os.path.isfile(filepath):
       
   311             folder, filename = os.path.split(filepath)
       
   312             
       
   313             dialog = wx.MessageDialog(self, 
       
   314                   _("Do you really want to delete the file '%s'?") % filename, 
       
   315                   _("Delete File"), wx.YES_NO|wx.ICON_QUESTION)
       
   316             remove = dialog.ShowModal() == wx.ID_YES
       
   317             dialog.Destroy()
       
   318             
       
   319             if remove:
       
   320                 os.remove(filepath)
       
   321                 self.ManagedDir.RefreshTree()
       
   322         event.Skip()
       
   323 
       
   324     def CopyFile(self, src, dst):
       
   325         if os.path.isfile(src):
       
   326             src_folder, src_filename = os.path.split(src)
       
   327             if os.path.isfile(dst):
       
   328                 dst_folder, dst_filename = os.path.split(dst)
       
   329             else:
       
   330                 dst_folder = dst
       
   331             
       
   332             dst_filepath = os.path.join(dst_folder, src_filename)
       
   333             if os.path.isfile(dst_filepath):
       
   334                 dialog = wx.MessageDialog(self, 
       
   335                       _("The file '%s' already exist.\nDo you want to replace it?") % src_filename, 
       
   336                       _("Replace File"), wx.YES_NO|wx.ICON_QUESTION)
       
   337                 copy = dialog.ShowModal() == wx.ID_YES
       
   338                 dialog.Destroy()
       
   339             else:
       
   340                 copy = True
       
   341                 
       
   342             if copy:
       
   343                 shutil.copyfile(src, dst_filepath)
       
   344                 return dst_filepath
       
   345         return None
       
   346 
       
   347     def OnLeftCopyButton(self, event):
       
   348         filepath = self.CopyFile(self.SystemDir.GetPath(), self.ManagedDir.GetPath())
       
   349         if filepath is not None:
       
   350             self.ManagedDir.RefreshTree()
       
   351             self.ManagedDir.SetPath(filepath)
       
   352         event.Skip()
       
   353 
       
   354     def OnRightCopyButton(self, event):
       
   355         filepath = self.CopyFile(self.ManagedDir.GetPath(), self.SystemDir.GetPath())
       
   356         if filepath is not None:
       
   357             self.SystemDir.RefreshTree()
       
   358             self.SystemDir.SetPath(filepath)
       
   359         event.Skip()
       
   360     
       
   361     def OnTreeBeginDrag(self, event):
       
   362         filepath = self.ManagedDir.GetPath()
       
   363         if os.path.isfile(filepath):
       
   364             relative_filepath = filepath.replace(os.path.join(self.Folder, ""), "")
       
   365             data = wx.TextDataObject(str(("'%s'" % relative_filepath, "Constant")))
       
   366             dragSource = wx.DropSource(self)
       
   367             dragSource.SetData(data)
       
   368             dragSource.DoDragDrop()
       
   369