24 |
24 |
25 import os |
25 import os |
26 import shutil |
26 import shutil |
27 |
27 |
28 import wx |
28 import wx |
|
29 import wx.lib.buttons |
29 |
30 |
30 from EditorPanel import EditorPanel |
31 from EditorPanel import EditorPanel |
31 from util.BitmapLibrary import GetBitmap |
32 from util.BitmapLibrary import GetBitmap |
32 |
33 from controls import FolderTree |
33 DRIVE, FOLDER, FILE = range(3) |
|
34 |
34 |
35 FILTER = _("All files (*.*)|*.*|CSV files (*.csv)|*.csv") |
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 |
36 |
255 class FileManagementPanel(EditorPanel): |
37 class FileManagementPanel(EditorPanel): |
256 |
38 |
257 def _init_Editor(self, parent): |
39 def _init_Editor(self, parent): |
258 self.Editor = wx.Panel(parent) |
40 self.Editor = wx.Panel(parent) |