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) |