editors/FileManagementPanel.py
changeset 814 5743cbdff669
parent 801 435e49e80832
child 815 e4f24593a758
equal deleted inserted replaced
813:1460273f40ed 814:5743cbdff669
       
     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 EditorPanel import EditorPanel
       
    31 from util.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         old_filepath = self.GetPath(event.GetItem())
       
   202         new_filepath = os.path.join(os.path.split(old_filepath)[0], event.GetLabel())
       
   203         if new_filepath != old_filepath:
       
   204             if not os.path.exists(new_filepath):
       
   205                 os.rename(old_filepath, new_filepath)
       
   206                 event.Skip()
       
   207             else:
       
   208                 message =  wx.MessageDialog(self, 
       
   209                     _("File '%s' already exists!") % event.GetLabel(), 
       
   210                     _("Error"), wx.OK|wx.ICON_ERROR)
       
   211                 message.ShowModal()
       
   212                 message.Destroy()
       
   213                 event.Veto()
       
   214     
       
   215     def OnFilterChanged(self, event):
       
   216         self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
       
   217         self.RefreshTree()
       
   218         event.Skip()
       
   219     
       
   220     def _SelectItem(self, root, parts):
       
   221         if len(parts) == 0:
       
   222             self.Tree.SelectItem(root)
       
   223         else:
       
   224             item, item_cookie = self.Tree.GetFirstChild(root)
       
   225             while item.IsOk():
       
   226                 if self.Tree.GetItemText(item) == parts[0]:
       
   227                     if (self.Tree.ItemHasChildren(item) and 
       
   228                         not self.Tree.IsExpanded(item)):
       
   229                         self.Tree.Expand(item)
       
   230                         wx.CallAfter(self._SelectItem, item, parts[1:])
       
   231                     else:
       
   232                         self._SelectItem(item, parts[1:])
       
   233                     return
       
   234                 item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
       
   235     
       
   236     def SetPath(self, path):
       
   237         if path.startswith(self.Folder):
       
   238             root = self.Tree.GetRootItem()
       
   239             if root.IsOk():
       
   240                 relative_path = path.replace(os.path.join(self.Folder, ""), "")
       
   241                 self._SelectItem(root, splitpath(relative_path))
       
   242     
       
   243     def GetPath(self, item=None):
       
   244         if item is None:
       
   245             item = self.Tree.GetSelection()
       
   246         if item.IsOk():
       
   247             filepath = self.Tree.GetItemText(item)
       
   248             parent = self.Tree.GetItemParent(item)
       
   249             while parent.IsOk() and parent != self.Tree.GetRootItem():
       
   250                 filepath = os.path.join(self.Tree.GetItemText(parent), filepath)
       
   251                 parent = self.Tree.GetItemParent(parent)
       
   252             return os.path.join(self.Folder, filepath)
       
   253         return self.Folder
       
   254 
       
   255 class FileManagementPanel(EditorPanel):
       
   256     
       
   257     def _init_Editor(self, parent):
       
   258         self.Editor = wx.Panel(parent)
       
   259         
       
   260         main_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   261         
       
   262         left_sizer = wx.BoxSizer(wx.VERTICAL)
       
   263         main_sizer.AddSizer(left_sizer, 1, border=5, flag=wx.GROW|wx.ALL)
       
   264         
       
   265         managed_dir_label = wx.StaticText(self.Editor, label=self.TagName + ":")
       
   266         left_sizer.AddWindow(managed_dir_label, border=5, flag=wx.GROW|wx.BOTTOM)
       
   267         
       
   268         self.ManagedDir = FolderTree(self.Editor, self.Folder, FILTER)
       
   269         left_sizer.AddWindow(self.ManagedDir, 1, flag=wx.GROW)
       
   270         
       
   271         managed_treectrl = self.ManagedDir.GetTreeCtrl()
       
   272         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, managed_treectrl)
       
   273         if self.EnableDragNDrop:
       
   274             self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, managed_treectrl)
       
   275         
       
   276         button_sizer = wx.BoxSizer(wx.VERTICAL)
       
   277         main_sizer.AddSizer(button_sizer, border=5, 
       
   278               flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL)
       
   279         
       
   280         for idx, (name, bitmap, help) in enumerate([
       
   281                 ("DeleteButton", "remove_element", _("Remove file from left folder")),
       
   282                 ("LeftCopyButton", "LeftCopy", _("Copy file from right folder to left")),
       
   283                 ("RightCopyButton", "RightCopy", _("Copy file from left folder to right")),
       
   284                 ("EditButton", "edit", _("Edit file"))]):
       
   285             button = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   286                   bitmap=GetBitmap(bitmap), 
       
   287                   size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   288             button.SetToolTipString(help)
       
   289             setattr(self, name, button)
       
   290             if idx > 0:
       
   291                 flag = wx.TOP
       
   292             else:
       
   293                 flag = 0
       
   294             self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
       
   295             button_sizer.AddWindow(button, border=20, flag=flag)
       
   296         
       
   297         right_sizer = wx.BoxSizer(wx.VERTICAL)
       
   298         main_sizer.AddSizer(right_sizer, 1, border=5, flag=wx.GROW|wx.ALL)
       
   299         
       
   300         if wx.Platform == '__WXMSW__':
       
   301             system_dir_label = wx.StaticText(self.Editor, label=_("My Computer:"))
       
   302         else:
       
   303             system_dir_label = wx.StaticText(self.Editor, label=_("Home Directory:"))
       
   304         right_sizer.AddWindow(system_dir_label, border=5, flag=wx.GROW|wx.BOTTOM)
       
   305         
       
   306         self.SystemDir = FolderTree(self.Editor, self.HomeDirectory, FILTER, False)
       
   307         right_sizer.AddWindow(self.SystemDir, 1, flag=wx.GROW)
       
   308         
       
   309         system_treectrl = self.SystemDir.GetTreeCtrl()
       
   310         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, system_treectrl)
       
   311         
       
   312         self.Editor.SetSizer(main_sizer)
       
   313         
       
   314     def __init__(self, parent, controler, name, folder, enable_dragndrop=False):
       
   315         self.Folder = os.path.realpath(folder)
       
   316         self.EnableDragNDrop = enable_dragndrop
       
   317         
       
   318         if wx.Platform == '__WXMSW__':
       
   319             self.HomeDirectory = "/"
       
   320         else:
       
   321             self.HomeDirectory = os.path.expanduser("~")
       
   322         
       
   323         EditorPanel.__init__(self, parent, name, None, None)
       
   324         
       
   325         self.Controler = controler
       
   326         
       
   327         self.EditableFileExtensions = []
       
   328         self.EditButton.Hide()
       
   329         
       
   330         self.SetIcon(GetBitmap("FOLDER"))
       
   331     
       
   332     def __del__(self):
       
   333         self.Controler.OnCloseEditor(self)
       
   334     
       
   335     def GetTitle(self):
       
   336         return self.TagName
       
   337     
       
   338     def SetEditableFileExtensions(self, extensions):
       
   339         self.EditableFileExtensions = extensions
       
   340         if len(self.EditableFileExtensions) > 0:
       
   341             self.EditButton.Show()
       
   342     
       
   343     def RefreshView(self):
       
   344         self.ManagedDir.RefreshTree()
       
   345         self.SystemDir.RefreshTree()
       
   346         self.RefreshButtonsState()
       
   347     
       
   348     def RefreshButtonsState(self):
       
   349         managed_filepath = self.ManagedDir.GetPath()
       
   350         system_filepath = self.SystemDir.GetPath()
       
   351         
       
   352         self.DeleteButton.Enable(os.path.isfile(managed_filepath))
       
   353         self.LeftCopyButton.Enable(os.path.isfile(system_filepath))
       
   354         self.RightCopyButton.Enable(os.path.isfile(managed_filepath))
       
   355         if len(self.EditableFileExtensions) > 0:
       
   356             self.EditButton.Enable(
       
   357                 os.path.isfile(managed_filepath) and
       
   358                 os.path.splitext(managed_filepath)[1] in self.EditableFileExtensions)
       
   359     
       
   360     def OnTreeItemChanged(self, event):
       
   361         self.RefreshButtonsState()
       
   362         event.Skip()
       
   363         
       
   364     def OnDeleteButton(self, event):
       
   365         filepath = self.ManagedDir.GetPath()
       
   366         if os.path.isfile(filepath):
       
   367             folder, filename = os.path.split(filepath)
       
   368             
       
   369             dialog = wx.MessageDialog(self, 
       
   370                   _("Do you really want to delete the file '%s'?") % filename, 
       
   371                   _("Delete File"), wx.YES_NO|wx.ICON_QUESTION)
       
   372             remove = dialog.ShowModal() == wx.ID_YES
       
   373             dialog.Destroy()
       
   374             
       
   375             if remove:
       
   376                 os.remove(filepath)
       
   377                 self.ManagedDir.RefreshTree()
       
   378         event.Skip()
       
   379 
       
   380     def OnEditButton(self, event):
       
   381         filepath = self.ManagedDir.GetPath()
       
   382         if (os.path.isfile(filepath) and 
       
   383             os.path.splitext(filepath)[1] in self.EditableFileExtensions):
       
   384             self.Controler._OpenView(filepath + "::")
       
   385         event.Skip()
       
   386         
       
   387     def CopyFile(self, src, dst):
       
   388         if os.path.isfile(src):
       
   389             src_folder, src_filename = os.path.split(src)
       
   390             if os.path.isfile(dst):
       
   391                 dst_folder, dst_filename = os.path.split(dst)
       
   392             else:
       
   393                 dst_folder = dst
       
   394             
       
   395             dst_filepath = os.path.join(dst_folder, src_filename)
       
   396             if os.path.isfile(dst_filepath):
       
   397                 dialog = wx.MessageDialog(self, 
       
   398                       _("The file '%s' already exist.\nDo you want to replace it?") % src_filename, 
       
   399                       _("Replace File"), wx.YES_NO|wx.ICON_QUESTION)
       
   400                 copy = dialog.ShowModal() == wx.ID_YES
       
   401                 dialog.Destroy()
       
   402             else:
       
   403                 copy = True
       
   404                 
       
   405             if copy:
       
   406                 shutil.copyfile(src, dst_filepath)
       
   407                 return dst_filepath
       
   408         return None
       
   409 
       
   410     def OnLeftCopyButton(self, event):
       
   411         filepath = self.CopyFile(self.SystemDir.GetPath(), self.ManagedDir.GetPath())
       
   412         if filepath is not None:
       
   413             self.ManagedDir.RefreshTree()
       
   414             self.ManagedDir.SetPath(filepath)
       
   415         event.Skip()
       
   416 
       
   417     def OnRightCopyButton(self, event):
       
   418         filepath = self.CopyFile(self.ManagedDir.GetPath(), self.SystemDir.GetPath())
       
   419         if filepath is not None:
       
   420             self.SystemDir.RefreshTree()
       
   421             self.SystemDir.SetPath(filepath)
       
   422         event.Skip()
       
   423     
       
   424     def OnTreeBeginDrag(self, event):
       
   425         filepath = self.ManagedDir.GetPath()
       
   426         if os.path.isfile(filepath):
       
   427             relative_filepath = filepath.replace(os.path.join(self.Folder, ""), "")
       
   428             data = wx.TextDataObject(str(("'%s'" % relative_filepath, "Constant")))
       
   429             dragSource = wx.DropSource(self)
       
   430             dragSource.SetData(data)
       
   431             dragSource.DoDragDrop()
       
   432