controls/FolderTree.py
changeset 1784 64beb9e9c749
parent 1780 c52d1460cea8
child 1881 091005ec69c4
equal deleted inserted replaced
1729:31e63e25b4cc 1784:64beb9e9c749
    27 import wx
    27 import wx
    28 
    28 
    29 from util.BitmapLibrary import GetBitmap
    29 from util.BitmapLibrary import GetBitmap
    30 
    30 
    31 DRIVE, FOLDER, FILE = range(3)
    31 DRIVE, FOLDER, FILE = range(3)
       
    32 
    32 
    33 
    33 def sort_folder(x, y):
    34 def sort_folder(x, y):
    34     if x[1] == y[1]:
    35     if x[1] == y[1]:
    35         return cmp(x[0], y[0])
    36         return cmp(x[0], y[0])
    36     elif x[1] != FILE:
    37     elif x[1] != FILE:
    37         return -1
    38         return -1
    38     else:
    39     else:
    39         return 1
    40         return 1
    40 
    41 
       
    42 
    41 def splitpath(path):
    43 def splitpath(path):
    42     head, tail = os.path.split(path)
    44     head, tail = os.path.split(path)
    43     if head == "":
    45     if head == "":
    44         return [tail]
    46         return [tail]
    45     elif tail == "":
    47     elif tail == "":
    46         return splitpath(head)
    48         return splitpath(head)
    47     return splitpath(head) + [tail]
    49     return splitpath(head) + [tail]
    48 
    50 
       
    51 
    49 class FolderTree(wx.Panel):
    52 class FolderTree(wx.Panel):
    50     
    53 
    51     def __init__(self, parent, folder, filter=None, editable=True):
    54     def __init__(self, parent, folder, filter=None, editable=True):
    52         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
    55         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
    53         
    56 
    54         main_sizer = wx.BoxSizer(wx.VERTICAL)
    57         main_sizer = wx.BoxSizer(wx.VERTICAL)
    55         
    58 
    56         self.Tree = wx.TreeCtrl(self, 
    59         self.Tree = wx.TreeCtrl(self,
    57               style=wx.TR_HAS_BUTTONS|
    60                                 style=(wx.TR_HAS_BUTTONS |
    58                     wx.TR_SINGLE|
    61                                        wx.TR_SINGLE |
    59                     wx.SUNKEN_BORDER|
    62                                        wx.SUNKEN_BORDER |
    60                     wx.TR_HIDE_ROOT|
    63                                        wx.TR_HIDE_ROOT |
    61                     wx.TR_LINES_AT_ROOT|
    64                                        wx.TR_LINES_AT_ROOT |
    62                     wx.TR_EDIT_LABELS)
    65                                        wx.TR_EDIT_LABELS))
    63         if wx.Platform == '__WXMSW__':
    66         if wx.Platform == '__WXMSW__':
    64             self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTreeItemExpanded, self.Tree)
    67             self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTreeItemExpanded, self.Tree)
    65             self.Tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
    68             self.Tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
    66         else:
    69         else:
    67             self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnTreeItemExpanded, self.Tree)
    70             self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnTreeItemExpanded, self.Tree)
    68         self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnTreeItemCollapsed, self.Tree)
    71         self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnTreeItemCollapsed, self.Tree)
    69         self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeBeginLabelEdit, self.Tree)
    72         self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeBeginLabelEdit, self.Tree)
    70         self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeEndLabelEdit, self.Tree)
    73         self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeEndLabelEdit, self.Tree)
    71         main_sizer.AddWindow(self.Tree, 1, flag=wx.GROW)
    74         main_sizer.AddWindow(self.Tree, 1, flag=wx.GROW)
    72         
    75 
    73         if filter is not None:
    76         if filter is not None:
    74             self.Filter = wx.ComboBox(self, style=wx.CB_READONLY)
    77             self.Filter = wx.ComboBox(self, style=wx.CB_READONLY)
    75             self.Bind(wx.EVT_COMBOBOX, self.OnFilterChanged, self.Filter)
    78             self.Bind(wx.EVT_COMBOBOX, self.OnFilterChanged, self.Filter)
    76             main_sizer.AddWindow(self.Filter, flag=wx.GROW)
    79             main_sizer.AddWindow(self.Filter, flag=wx.GROW)
    77         else:
    80         else:
    78             self.Filter = None
    81             self.Filter = None
    79         
    82 
    80         self.SetSizer(main_sizer)
    83         self.SetSizer(main_sizer)
    81         
    84 
    82         self.Folder = folder
    85         self.Folder = folder
    83         self.Editable = editable
    86         self.Editable = editable
    84         
    87 
    85         self.TreeImageList = wx.ImageList(16, 16)
    88         self.TreeImageList = wx.ImageList(16, 16)
    86         self.TreeImageDict = {}
    89         self.TreeImageDict = {}
    87         for item_type, bitmap in [(DRIVE, "tree_drive"),
    90         for item_type, bitmap in [(DRIVE, "tree_drive"),
    88                                   (FOLDER, "tree_folder"),
    91                                   (FOLDER, "tree_folder"),
    89                                   (FILE, "tree_file")]:
    92                                   (FILE, "tree_file")]:
    90             self.TreeImageDict[item_type] = self.TreeImageList.Add(GetBitmap(bitmap))
    93             self.TreeImageDict[item_type] = self.TreeImageList.Add(GetBitmap(bitmap))
    91         self.Tree.SetImageList(self.TreeImageList)
    94         self.Tree.SetImageList(self.TreeImageList)
    92         
    95 
    93         self.Filters = {}
    96         self.Filters = {}
    94         if self.Filter is not None:
    97         if self.Filter is not None:
    95             filter_parts = filter.split("|")
    98             filter_parts = filter.split("|")
    96             for idx in xrange(0, len(filter_parts), 2):
    99             for idx in xrange(0, len(filter_parts), 2):
    97                 if filter_parts[idx + 1] == "*.*":
   100                 if filter_parts[idx + 1] == "*.*":
    99                 else:
   102                 else:
   100                     self.Filters[filter_parts[idx]] = filter_parts[idx + 1].replace("*", "")
   103                     self.Filters[filter_parts[idx]] = filter_parts[idx + 1].replace("*", "")
   101                 self.Filter.Append(filter_parts[idx])
   104                 self.Filter.Append(filter_parts[idx])
   102                 if idx == 0:
   105                 if idx == 0:
   103                     self.Filter.SetStringSelection(filter_parts[idx])
   106                     self.Filter.SetStringSelection(filter_parts[idx])
   104                 
   107 
   105             self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
   108             self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
   106         else:
   109         else:
   107             self.CurrentFilter = ""
   110             self.CurrentFilter = ""
   108     
   111 
   109     def _GetFolderChildren(self, folderpath, recursive=True):
   112     def _GetFolderChildren(self, folderpath, recursive=True):
   110         items = []
   113         items = []
   111         if wx.Platform == '__WXMSW__' and folderpath == "/":
   114         if wx.Platform == '__WXMSW__' and folderpath == "/":
   112             for c in xrange(ord('a'), ord('z')):
   115             for c in xrange(ord('a'), ord('z')):
   113                 drive = os.path.join("%s:\\" % chr(c))
   116                 drive = os.path.join("%s:\\" % chr(c))
   114                 if os.path.exists(drive):
   117                 if os.path.exists(drive):
   115                     items.append((drive, DRIVE, self._GetFolderChildren(drive, False)))
   118                     items.append((drive, DRIVE, self._GetFolderChildren(drive, False)))
   116         else:
   119         else:
   117             try:
   120             try:
   118                 files = os.listdir(folderpath)
   121                 files = os.listdir(folderpath)
   119             except:
   122             except Exception:
   120                 return []
   123                 return []
   121             for filename in files:
   124             for filename in files:
   122                 if not filename.startswith("."):
   125                 if not filename.startswith("."):
   123                     filepath = os.path.join(folderpath, filename)
   126                     filepath = os.path.join(folderpath, filename)
   124                     if os.path.isdir(filepath):
   127                     if os.path.isdir(filepath):
   125                         if recursive:
   128                         if recursive:
   126                             children = len(self._GetFolderChildren(filepath, False))
   129                             children = len(self._GetFolderChildren(filepath, False))
   127                         else:
   130                         else:
   128                             children = 0
   131                             children = 0
   129                         items.append((filename, FOLDER, children))
   132                         items.append((filename, FOLDER, children))
   130                     elif (self.CurrentFilter == "" or 
   133                     elif (self.CurrentFilter == "" or
   131                           os.path.splitext(filename)[1] == self.CurrentFilter):
   134                           os.path.splitext(filename)[1] == self.CurrentFilter):
   132                         items.append((filename, FILE, None))
   135                         items.append((filename, FILE, None))
   133         if recursive:
   136         if recursive:
   134             items.sort(sort_folder)
   137             items.sort(sort_folder)
   135         return items
   138         return items
   136     
   139 
   137     def SetFilter(self, filter):
   140     def SetFilter(self, filter):
   138         self.CurrentFilter = filter
   141         self.CurrentFilter = filter
   139     
   142 
   140     def GetTreeCtrl(self):
   143     def GetTreeCtrl(self):
   141         return self.Tree
   144         return self.Tree
   142     
   145 
   143     def RefreshTree(self):
   146     def RefreshTree(self):
   144         root = self.Tree.GetRootItem()
   147         root = self.Tree.GetRootItem()
   145         if not root.IsOk():
   148         if not root.IsOk():
   146             root = self.Tree.AddRoot("")
   149             root = self.Tree.AddRoot("")
   147         self.GenerateTreeBranch(root, self.Folder)
   150         self.GenerateTreeBranch(root, self.Folder)
   148         
   151 
   149     def GenerateTreeBranch(self, root, folderpath):
   152     def GenerateTreeBranch(self, root, folderpath):
   150         item, item_cookie = self.Tree.GetFirstChild(root)
   153         item, item_cookie = self.Tree.GetFirstChild(root)
   151         for idx, (filename, item_type, children) in enumerate(self._GetFolderChildren(folderpath)):
   154         for idx, (filename, item_type, children) in enumerate(self._GetFolderChildren(folderpath)):
   152             if not item.IsOk():
   155             if not item.IsOk():
   153                 item = self.Tree.AppendItem(root, filename, self.TreeImageDict[item_type])
   156                 item = self.Tree.AppendItem(root, filename, self.TreeImageDict[item_type])
   170             self.Tree.Delete(item)
   173             self.Tree.Delete(item)
   171 
   174 
   172     def ExpandItem(self, item):
   175     def ExpandItem(self, item):
   173         self.GenerateTreeBranch(item, self.GetPath(item))
   176         self.GenerateTreeBranch(item, self.GetPath(item))
   174         self.Tree.Expand(item)
   177         self.Tree.Expand(item)
   175     
   178 
   176     def OnTreeItemActivated(self, event):
   179     def OnTreeItemActivated(self, event):
   177         self.ExpandItem(event.GetItem())
   180         self.ExpandItem(event.GetItem())
   178         event.Skip()
   181         event.Skip()
   179     
   182 
   180     def OnTreeLeftDown(self, event):
   183     def OnTreeLeftDown(self, event):
   181         item, flags = self.Tree.HitTest(event.GetPosition())
   184         item, flags = self.Tree.HitTest(event.GetPosition())
   182         if flags & wx.TREE_HITTEST_ONITEMBUTTON and not self.Tree.IsExpanded(item):
   185         if flags & wx.TREE_HITTEST_ONITEMBUTTON and not self.Tree.IsExpanded(item):
   183             self.ExpandItem(item)
   186             self.ExpandItem(item)
   184         else:
   187         else:
   185             event.Skip()
   188             event.Skip()
   186     
   189 
   187     def OnTreeItemExpanded(self, event):
   190     def OnTreeItemExpanded(self, event):
   188         item = event.GetItem()
   191         item = event.GetItem()
   189         self.GenerateTreeBranch(item, self.GetPath(item))
   192         self.GenerateTreeBranch(item, self.GetPath(item))
   190         event.Skip()
   193         event.Skip()
   191 
   194 
   199         item = event.GetItem()
   202         item = event.GetItem()
   200         if self.Editable and not self.Tree.ItemHasChildren(item):
   203         if self.Editable and not self.Tree.ItemHasChildren(item):
   201             event.Skip()
   204             event.Skip()
   202         else:
   205         else:
   203             event.Veto()
   206             event.Veto()
   204     
   207 
   205     def OnTreeEndLabelEdit(self, event):
   208     def OnTreeEndLabelEdit(self, event):
   206         new_name = event.GetLabel()
   209         new_name = event.GetLabel()
   207         if new_name != "":
   210         if new_name != "":
   208             old_filepath = self.GetPath(event.GetItem())
   211             old_filepath = self.GetPath(event.GetItem())
   209             new_filepath = os.path.join(os.path.split(old_filepath)[0], new_name)
   212             new_filepath = os.path.join(os.path.split(old_filepath)[0], new_name)
   210             if new_filepath != old_filepath:
   213             if new_filepath != old_filepath:
   211                 if not os.path.exists(new_filepath):
   214                 if not os.path.exists(new_filepath):
   212                     os.rename(old_filepath, new_filepath)
   215                     os.rename(old_filepath, new_filepath)
   213                     event.Skip()
   216                     event.Skip()
   214                 else:
   217                 else:
   215                     message =  wx.MessageDialog(self, 
   218                     message = wx.MessageDialog(self,
   216                         _("File '%s' already exists!") % new_name, 
   219                                                _("File '%s' already exists!") % new_name,
   217                         _("Error"), wx.OK|wx.ICON_ERROR)
   220                                                _("Error"), wx.OK | wx.ICON_ERROR)
   218                     message.ShowModal()
   221                     message.ShowModal()
   219                     message.Destroy()
   222                     message.Destroy()
   220                     event.Veto()
   223                     event.Veto()
   221         else:
   224         else:
   222             event.Skip()
   225             event.Skip()
   223     
   226 
   224     def OnFilterChanged(self, event):
   227     def OnFilterChanged(self, event):
   225         self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
   228         self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
   226         self.RefreshTree()
   229         self.RefreshTree()
   227         event.Skip()
   230         event.Skip()
   228     
   231 
   229     def _SelectItem(self, root, parts):
   232     def _SelectItem(self, root, parts):
   230         if len(parts) == 0:
   233         if len(parts) == 0:
   231             self.Tree.SelectItem(root)
   234             self.Tree.SelectItem(root)
   232         else:
   235         else:
   233             item, item_cookie = self.Tree.GetFirstChild(root)
   236             item, item_cookie = self.Tree.GetFirstChild(root)
   234             while item.IsOk():
   237             while item.IsOk():
   235                 if self.Tree.GetItemText(item) == parts[0]:
   238                 if self.Tree.GetItemText(item) == parts[0]:
   236                     if (self.Tree.ItemHasChildren(item) and 
   239                     if self.Tree.ItemHasChildren(item) and \
   237                         not self.Tree.IsExpanded(item)):
   240                        not self.Tree.IsExpanded(item):
   238                         self.Tree.Expand(item)
   241                         self.Tree.Expand(item)
   239                         wx.CallAfter(self._SelectItem, item, parts[1:])
   242                         wx.CallAfter(self._SelectItem, item, parts[1:])
   240                     else:
   243                     else:
   241                         self._SelectItem(item, parts[1:])
   244                         self._SelectItem(item, parts[1:])
   242                     return
   245                     return
   243                 item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
   246                 item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
   244     
   247 
   245     def SetPath(self, path):
   248     def SetPath(self, path):
   246         if path.startswith(self.Folder):
   249         if path.startswith(self.Folder):
   247             root = self.Tree.GetRootItem()
   250             root = self.Tree.GetRootItem()
   248             if root.IsOk():
   251             if root.IsOk():
   249                 relative_path = path.replace(os.path.join(self.Folder, ""), "")
   252                 relative_path = path.replace(os.path.join(self.Folder, ""), "")
   250                 self._SelectItem(root, splitpath(relative_path))
   253                 self._SelectItem(root, splitpath(relative_path))
   251     
   254 
   252     def GetPath(self, item=None):
   255     def GetPath(self, item=None):
   253         if item is None:
   256         if item is None:
   254             item = self.Tree.GetSelection()
   257             item = self.Tree.GetSelection()
   255         if item.IsOk():
   258         if item.IsOk():
   256             filepath = self.Tree.GetItemText(item)
   259             filepath = self.Tree.GetItemText(item)