Module filesystem
[hide private]
[frames] | no frames]

Source Code for Module filesystem

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 
  4  ### 
  5  ### This program is free software; you can redistribute it and/or modify 
  6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  7  ### 
  8  ### $Id: filesystem.py 4390 2007-09-05 00:08:19Z djpham $ 
  9   
 10  """The main gui code for BitPim""" 
 11   
 12  # System modules 
 13  from __future__ import with_statement 
 14  import ConfigParser 
 15  import thread, threading 
 16  import Queue 
 17  import time 
 18  import os 
 19  import cStringIO 
 20  import zipfile 
 21  import re 
 22  import sys 
 23  import shutil 
 24  import types 
 25  import datetime 
 26  import sha 
 27  import codecs 
 28  import fileview 
 29  import widgets 
 30   
 31  # wx modules 
 32  import wx 
 33  import wx.lib.colourdb 
 34  import wx.gizmos 
 35  import wx.html 
 36  import wx.lib.mixins.listctrl  as  listmix 
 37   
 38  # my modules 
 39  import guiwidgets 
 40  import common 
 41  import helpids 
 42  import comdiagnose 
 43  import guihelper 
 44  import hexeditor 
 45  import pubsub 
 46  import phones.com_brew as com_brew 
 47  import gui 
 48  import widgets 
 49   
 50   
51 -class FileSystemView(wx.SplitterWindow, widgets.BitPimWidget):
52 - def __init__(self, mainwindow, parent, id=-1):
53 # the listbox and textbox in a splitter 54 self.mainwindow=mainwindow 55 wx.SplitterWindow.__init__(self, parent, id, style=wx.SP_LIVE_UPDATE) 56 self.tree=FileSystemDirectoryView(mainwindow, self, wx.NewId(), style=(wx.TR_DEFAULT_STYLE|wx.TR_NO_LINES)&~wx.TR_TWIST_BUTTONS) 57 self.list=FileSystemFileView(mainwindow, self, wx.NewId()) 58 self.sash_pos=mainwindow.config.ReadInt("filesystemsplitterpos", 200) 59 self.update_sash=False 60 self.SplitVertically(self.tree, self.list, self.sash_pos) 61 self.SetMinimumPaneSize(20) 62 wx.EVT_SPLITTER_SASH_POS_CHANGED(self, id, self.OnSplitterPosChanged) 63 pubsub.subscribe(self.OnPhoneModelChanged, pubsub.PHONE_MODEL_CHANGED)
64
65 - def __del__(self):
67
68 - def OnPhoneModelChanged(self, msg):
69 # if the phone changes we reset ourselves 70 self.list.ResetView() 71 self.tree.ResetView()
72
73 - def OnSplitterPosChanged(self,_):
74 if self.update_sash: 75 self.sash_pos=self.GetSashPosition() 76 self.mainwindow.config.WriteInt("filesystemsplitterpos", 77 self.sash_pos)
78
79 - def OnPreActivate(self):
80 self.update_sash=False
81 - def OnPostActivate(self):
82 self.SetSashPosition(self.sash_pos) 83 self.update_sash=True
84
85 - def OnPhoneReboot(self,_):
86 mw=self.mainwindow 87 mw.MakeCall( gui.Request(mw.wt.phonerebootrequest), 88 gui.Callback(self.OnPhoneRebootResults) )
89
90 - def OnPhoneRebootResults(self, exception, _):
91 # special case - we always clear the comm connection 92 # it is needed if the reboot succeeds, and if it didn't 93 # we probably have bad comms anyway 94 mw=self.mainwindow 95 mw.wt.clearcomm() 96 if mw.HandleException(exception): return
97
98 - def OnPhoneOffline(self,_):
99 mw=self.mainwindow 100 mw.MakeCall( gui.Request(mw.wt.phoneofflinerequest), 101 gui.Callback(self.OnPhoneOfflineResults) )
102
103 - def OnPhoneOfflineResults(self, exception, _):
104 mw=self.mainwindow 105 if mw.HandleException(exception): return
106
107 - def OnModemMode(self,_):
108 mw=self.mainwindow 109 mw.MakeCall( gui.Request(mw.wt.modemmoderequest), 110 gui.Callback(self.OnModemModeResults) )
111
112 - def OnModemModeResults(self, exception, _):
113 mw=self.mainwindow 114 if mw.HandleException(exception): return
115
116 - def ShowFiles(self, dir, refresh=False):
117 self.list.ShowFiles(dir, refresh)
118
119 - def OnNewFileResults(self, parentdir, exception, _):
120 mw=self.mainwindow 121 if mw.HandleException(exception): return 122 self.ShowFiles(parentdir, True)
123
124 -class FileSystemFileView(wx.ListCtrl, listmix.ColumnSorterMixin):
125 - def __init__(self, mainwindow, parent, id, style=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_SINGLE_SEL):
126 wx.ListCtrl.__init__(self, parent, id, style=style) 127 self.parent=parent 128 self.mainwindow=mainwindow 129 self.datacolumn=False # used for debugging and inspection of values 130 self.InsertColumn(0, "Name", width=300) 131 self.InsertColumn(1, "Size", format=wx.LIST_FORMAT_RIGHT) 132 self.InsertColumn(2, "Date", width=200) 133 self.font=wx.TheFontList.FindOrCreateFont(10, family=wx.SWISS, style=wx.NORMAL, weight=wx.NORMAL) 134 135 self.ResetView() 136 137 if self.datacolumn: 138 self.InsertColumn(3, "Extra Stuff", width=400) 139 listmix.ColumnSorterMixin.__init__(self, 4) 140 else: 141 listmix.ColumnSorterMixin.__init__(self, 3) 142 143 #sort by genre (column 2), A->Z ascending order (1) 144 self.filemenu=wx.Menu() 145 self.filemenu.Append(guihelper.ID_FV_SAVE, "Save ...") 146 self.filemenu.Append(guihelper.ID_FV_HEXVIEW, "Hexdump") 147 self.filemenu.AppendSeparator() 148 self.filemenu.Append(guihelper.ID_FV_DELETE, "Delete") 149 self.filemenu.Append(guihelper.ID_FV_OVERWRITE, "Overwrite ...") 150 # generic menu 151 self.genericmenu=wx.Menu() 152 self.genericmenu.Append(guihelper.ID_FV_NEWFILE, "New File ...") 153 self.genericmenu.AppendSeparator() 154 self.genericmenu.Append(guihelper.ID_FV_OFFLINEPHONE, "Offline Phone") 155 self.genericmenu.Append(guihelper.ID_FV_REBOOTPHONE, "Reboot Phone") 156 self.genericmenu.Append(guihelper.ID_FV_MODEMMODE, "Go to modem mode") 157 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_NEWFILE, self.OnNewFile) 158 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_OFFLINEPHONE, parent.OnPhoneOffline) 159 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_REBOOTPHONE, parent.OnPhoneReboot) 160 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_MODEMMODE, parent.OnModemMode) 161 wx.EVT_MENU(self.filemenu, guihelper.ID_FV_SAVE, self.OnFileSave) 162 wx.EVT_MENU(self.filemenu, guihelper.ID_FV_HEXVIEW, self.OnHexView) 163 wx.EVT_MENU(self.filemenu, guihelper.ID_FV_DELETE, self.OnFileDelete) 164 wx.EVT_MENU(self.filemenu, guihelper.ID_FV_OVERWRITE, self.OnFileOverwrite) 165 wx.EVT_RIGHT_DOWN(self.GetMainWindow(), self.OnRightDown) 166 wx.EVT_RIGHT_UP(self.GetMainWindow(), self.OnRightUp) 167 wx.EVT_LIST_ITEM_ACTIVATED(self,id, self.OnItemActivated) 168 self.image_list=wx.ImageList(16, 16) 169 a={"sm_up":"GO_UP","sm_dn":"GO_DOWN","w_idx":"WARNING","e_idx":"ERROR","i_idx":"QUESTION"} 170 for k,v in a.items(): 171 s="self.%s= self.image_list.Add(wx.ArtProvider_GetBitmap(wx.ART_%s,wx.ART_TOOLBAR,(16,16)))" % (k,v) 172 exec(s) 173 self.img_file=self.image_list.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, 174 wx.ART_OTHER, 175 (16, 16))) 176 self.SetImageList(self.image_list, wx.IMAGE_LIST_SMALL) 177 178 #if guihelper.IsMSWindows(): 179 # turn on drag-and-drag for windows 180 #wx.EVT_MOTION(self, self.OnStartDrag) 181 182 self.__dragging=False 183 self.add_files=[] 184 self.droptarget=fileview.MyFileDropTarget(self, True, False) 185 self.SetDropTarget(self.droptarget)
186
187 - def OnPaint(self, evt):
188 w,h=self.GetSize() 189 self.Refresh() 190 dc=wx.PaintDC(self) 191 dc.BeginDrawing() 192 dc.SetFont(self.font) 193 x,y= dc.GetTextExtent("There are no items to show in this view") 194 # center the text 195 xx=(w-x)/2 196 if xx<0: 197 xx=0 198 dc.DrawText("There are no items to show in this view", xx, h/3) 199 dc.EndDrawing()
200
201 - def OnDropFiles(self, _, dummy, filenames):
202 # There is a bug in that the most recently created tab 203 # in the notebook that accepts filedrop receives these 204 # files, not the most visible one. We find the currently 205 # viewed tab in the notebook and send the files there 206 if self.__dragging: 207 # I'm the drag source, forget 'bout it ! 208 return 209 target=self # fallback 210 t=self.mainwindow.GetCurrentActiveWidget() 211 if isinstance(t, FileSystemFileView): 212 # changing target in dragndrop 213 target=t 214 self.add_files=filenames 215 target.OnAddFiles()
216
217 - def OnDragOver(self, x, y, d):
218 # force copy (instead of move) 219 return wx._misc.DragCopy
220
221 - def OnAddFiles(self):
222 mw=self.mainwindow 223 if not len(self.add_files): 224 return 225 for file in self.add_files: 226 if file is None: 227 continue 228 if len(self.path): 229 path=self.path+"/"+os.path.basename(file) 230 else: 231 path=os.path.basename(file) # you can't create files in root but I won't stop you 232 contents=open(file, "rb").read() 233 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 234 gui.Callback(self.OnAddFilesResults, self.path) ) 235 self.add_files.remove(file) 236 # can only add one file at a time 237 break
238
239 - def OnAddFilesResults(self, parentdir, exception, _):
240 mw=self.mainwindow 241 if mw.HandleException(exception): return 242 # add next file if there is one 243 if not len(self.add_files): 244 self.ShowFiles(parentdir, True) 245 else: 246 self.OnAddFiles()
247 248 if guihelper.IsMSWindows(): 249 # drag-and-drop files only works in Windows
250 - def OnStartDrag(self, evt):
251 evt.Skip() 252 if not evt.LeftIsDown(): 253 return 254 path=self.itemtopath(self.GetFirstSelected()) 255 drag_source=wx.DropSource(self) 256 file_names=wx.FileDataObject() 257 file_names.AddFile(path) 258 drag_source.SetData(file_names) 259 self.__dragging=True 260 res=drag_source.DoDragDrop(wx.Drag_CopyOnly) 261 self.__dragging=False
262
263 - def OnRightUp(self, event):
264 pt = event.GetPosition() 265 item, flags = self.HitTest(pt) 266 if item is not -1: 267 self.Select(item) 268 self.PopupMenu(self.filemenu, pt) 269 else: 270 self.PopupMenu(self.genericmenu, pt)
271
272 - def OnRightDown(self,event):
273 # You have to capture right down otherwise it doesn't feed you right up 274 pt = event.GetPosition(); 275 item, flags = self.HitTest(pt) 276 try: 277 self.Select(item) 278 except: 279 pass
280
281 - def OnNewFile(self,_):
282 with guihelper.WXDialogWrapper(wx.FileDialog(self, style=wx.OPEN|wx.HIDE_READONLY|wx.CHANGE_DIR), 283 True) as (dlg, retcode): 284 if retcode==wx.ID_OK: 285 infile=dlg.GetPath() 286 contents=open(infile, "rb").read() 287 if len(self.path): 288 path=self.path+"/"+os.path.basename(dlg.GetPath()) 289 else: 290 path=os.path.basename(dlg.GetPath()) # you can't create files in root but I won't stop you 291 mw=self.mainwindow 292 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 293 gui.Callback(self.parent.OnNewFileResults, self.path) )
294
295 - def OnFileSave(self, _):
296 path=self.itemtopath(self.GetFirstSelected()) 297 mw=self.mainwindow 298 mw.MakeCall( gui.Request(mw.wt.getfile, path), 299 gui.Callback(self.OnFileSaveResults, path) )
300
301 - def OnFileSaveResults(self, path, exception, contents):
302 mw=self.mainwindow 303 if mw.HandleException(exception): return 304 bn=guihelper.basename(path) 305 ext=guihelper.getextension(bn) 306 if len(ext): 307 ext="%s files (*.%s)|*.%s" % (ext.upper(), ext, ext) 308 else: 309 ext="All files|*" 310 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Save File As", defaultFile=bn, wildcard=ext, 311 style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR), 312 True) as (dlg, retcode): 313 if retcode==wx.ID_OK: 314 file(dlg.GetPath(), "wb").write(contents)
315
316 - def OnItemActivated(self,_):
317 self.OnHexView(self)
318
319 - def OnHexView(self, _):
320 path=self.itemtopath(self.GetFirstSelected()) 321 mw=self.mainwindow 322 mw.MakeCall( gui.Request(mw.wt.getfile, path), 323 gui.Callback(self.OnHexViewResults, path) )
324
325 - def OnHexViewResults(self, path, exception, result):
326 mw=self.mainwindow 327 if mw.HandleException(exception): return 328 # ::TODO:: make this use HexEditor 329 ## dlg=guiwidgets.MyFixedScrolledMessageDialog(self, common.datatohexstring(result), 330 ## path+" Contents", helpids.ID_HEXVIEW_DIALOG) 331 dlg=hexeditor.HexEditorDialog(self, result, path+" Contents") 332 dlg.Show()
333
334 - def OnFileDelete(self, _):
335 path=self.itemtopath(self.GetFirstSelected()) 336 mw=self.mainwindow 337 mw.MakeCall( gui.Request(mw.wt.rmfile, path), 338 gui.Callback(self.OnFileDeleteResults, guihelper.dirname(path)) )
339
340 - def OnFileDeleteResults(self, parentdir, exception, _):
341 mw=self.mainwindow 342 if mw.HandleException(exception): return 343 self.ShowFiles(parentdir, True)
344
345 - def OnFileOverwrite(self,_):
346 path=self.itemtopath(self.GetFirstSelected()) 347 with guihelper.WXDialogWrapper(wx.FileDialog(self, style=wx.OPEN|wx.HIDE_READONLY|wx.CHANGE_DIR), 348 True) as (dlg, retcode): 349 if retcode==wx.ID_OK: 350 infile=dlg.GetPath() 351 contents=open(infile, "rb").read() 352 mw=self.mainwindow 353 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 354 gui.Callback(self.OnFileOverwriteResults, guihelper.dirname(path)) )
355
356 - def OnFileOverwriteResults(self, parentdir, exception, _):
357 mw=self.mainwindow 358 if mw.HandleException(exception): return 359 self.ShowFiles(parentdir, True)
360
361 - def ResetView(self):
362 self.DeleteAllItems() 363 self.files={} 364 self.path=None 365 self.itemDataMap = self.files 366 self.itemIndexMap = self.files.keys() 367 self.SetItemCount(0)
368
369 - def ShowFiles(self, path, refresh=False):
370 mw=self.mainwindow 371 if path == self.path and not refresh: 372 return 373 self.path=None 374 mw.MakeCall( gui.Request(mw.wt.getfileonlylist, path), 375 gui.Callback(self.OnShowFilesResults, path) )
376
377 - def OnShowFilesResults(self, path, exception, result):
378 mw=self.mainwindow 379 if mw.HandleException(exception): return 380 count=self.GetItemCount() 381 self.path=path 382 self.DeleteAllItems() 383 self.files={} 384 index=0 385 for file in result: 386 index=index+1 387 f=guihelper.basename(file) 388 if self.datacolumn: 389 self.files[index]=(f, `result[file]['size']`, result[file]['date'][1], result[file]['data'], file) 390 else: 391 self.files[index]=(f, `result[file]['size']`, result[file]['date'][1], file) 392 self.itemDataMap = self.files 393 self.itemIndexMap = self.files.keys() 394 self.SetItemCount(index) 395 self.SortListItems() 396 if count!=0 and index==0: 397 wx.EVT_PAINT(self, self.OnPaint) 398 elif count==0 and index!=0: 399 self.Unbind(wx.EVT_PAINT)
400
401 - def itemtopath(self, item):
402 index=self.itemIndexMap[item] 403 if self.datacolumn: 404 return self.itemDataMap[index][4] 405 return self.itemDataMap[index][3]
406
407 - def SortItems(self,sorter=None):
408 col=self._col 409 sf=self._colSortFlag[col] 410 411 #creating pairs [column item defined by col, key] 412 items=[] 413 for k,v in self.itemDataMap.items(): 414 if col==1: 415 items.append([int(v[col]),k]) 416 else: 417 items.append([v[col],k]) 418 419 items.sort() 420 k=[key for value, key in items] 421 422 # False is descending 423 if sf==False: 424 k.reverse() 425 426 self.itemIndexMap=k 427 428 #redrawing the list 429 self.Refresh()
430
431 - def GetListCtrl(self):
432 return self
433
434 - def GetSortImages(self):
435 return (self.sm_dn, self.sm_up)
436
437 - def OnGetItemText(self, item, col):
438 index=self.itemIndexMap[item] 439 s = self.itemDataMap[index][col] 440 return s
441
442 - def OnGetItemImage(self, item):
443 return self.img_file
444
445 - def OnGetItemAttr(self, item):
446 return None
447
448 -class FileSystemDirectoryView(wx.TreeCtrl):
449 - def __init__(self, mainwindow, parent, id, style):
450 wx.TreeCtrl.__init__(self, parent, id, style=style) 451 self.parent=parent 452 self.mainwindow=mainwindow 453 wx.EVT_TREE_ITEM_EXPANDED(self, id, self.OnItemExpanded) 454 wx.EVT_TREE_SEL_CHANGED(self,id, self.OnItemSelected) 455 self.dirmenu=wx.Menu() 456 self.dirmenu.Append(guihelper.ID_FV_NEWSUBDIR, "Make subdirectory ...") 457 self.dirmenu.Append(guihelper.ID_FV_NEWFILE, "New File ...") 458 self.dirmenu.AppendSeparator() 459 self.dirmenu.Append(guihelper.ID_FV_BACKUP, "Backup directory ...") 460 self.dirmenu.Append(guihelper.ID_FV_BACKUP_TREE, "Backup entire tree ...") 461 self.dirmenu.Append(guihelper.ID_FV_RESTORE, "Restore ...") 462 self.dirmenu.AppendSeparator() 463 self.dirmenu.Append(guihelper.ID_FV_REFRESH, "Refresh") 464 self.dirmenu.AppendSeparator() 465 self.dirmenu.Append(guihelper.ID_FV_DELETE, "Delete") 466 self.dirmenu.AppendSeparator() 467 self.dirmenu.Append(guihelper.ID_FV_TOTAL_REFRESH, "Refresh Filesystem") 468 self.dirmenu.Append(guihelper.ID_FV_OFFLINEPHONE, "Offline Phone") 469 self.dirmenu.Append(guihelper.ID_FV_REBOOTPHONE, "Reboot Phone") 470 self.dirmenu.Append(guihelper.ID_FV_MODEMMODE, "Go to modem mode") 471 # generic menu 472 self.genericmenu=wx.Menu() 473 self.genericmenu.Append(guihelper.ID_FV_TOTAL_REFRESH, "Refresh Filesystem") 474 self.genericmenu.Append(guihelper.ID_FV_OFFLINEPHONE, "Offline Phone") 475 self.genericmenu.Append(guihelper.ID_FV_REBOOTPHONE, "Reboot Phone") 476 self.genericmenu.Append(guihelper.ID_FV_MODEMMODE, "Go to modem mode") 477 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_TOTAL_REFRESH, self.OnRefresh) 478 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_OFFLINEPHONE, parent.OnPhoneOffline) 479 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_REBOOTPHONE, parent.OnPhoneReboot) 480 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_MODEMMODE, parent.OnModemMode) 481 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_NEWSUBDIR, self.OnNewSubdir) 482 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_NEWFILE, self.OnNewFile) 483 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_DELETE, self.OnDirDelete) 484 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_BACKUP, self.OnBackupDirectory) 485 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_BACKUP_TREE, self.OnBackupTree) 486 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_RESTORE, self.OnRestore) 487 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_REFRESH, self.OnDirRefresh) 488 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_TOTAL_REFRESH, self.OnRefresh) 489 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_OFFLINEPHONE, parent.OnPhoneOffline) 490 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_REBOOTPHONE, parent.OnPhoneReboot) 491 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_MODEMMODE, parent.OnModemMode) 492 wx.EVT_RIGHT_DOWN(self, self.OnRightDown) 493 wx.EVT_RIGHT_UP(self, self.OnRightUp) 494 self.image_list=wx.ImageList(16, 16) 495 self.img_dir=self.image_list.Add(wx.ArtProvider_GetBitmap(guihelper.ART_FOLDER, 496 wx.ART_OTHER, 497 (16, 16))) 498 self.img_dir_open=self.image_list.Add(wx.ArtProvider_GetBitmap(guihelper.ART_FOLDER_OPEN, 499 wx.ART_OTHER, 500 (16, 16))) 501 self.SetImageList(self.image_list) 502 self.add_files=[] 503 self.add_target="" 504 self.droptarget=fileview.MyFileDropTarget(self, True, True) 505 self.SetDropTarget(self.droptarget) 506 self.ResetView()
507
508 - def ResetView(self):
509 self.first_time=True 510 self.DeleteAllItems() 511 self.root=self.AddRoot("/") 512 self.item=self.root 513 self.SetPyData(self.root, None) 514 self.SetItemHasChildren(self.root, True) 515 self.SetItemImage(self.root, self.img_dir) 516 self.SetItemImage(self.root, self.img_dir_open, which=wx.TreeItemIcon_Expanded) 517 self.SetPyData(self.AppendItem(self.root, "Retrieving..."), None) 518 self.selections=[] 519 self.dragging=False 520 self.skip_dir_list=0
521
522 - def OnDropFiles(self, x, y, filenames):
523 target=self 524 t=self.mainwindow.GetCurrentActiveWidget() 525 if isinstance(t, FileSystemDirectoryView): 526 # changing target in dragndrop 527 target=t 528 # make sure that the files are being dropped onto a real directory 529 item, flags = self.HitTest((x, y)) 530 if item.IsOk(): 531 self.SelectItem(item) 532 self.add_target=self.itemtopath(item) 533 self.add_files=filenames 534 target.OnAddFiles() 535 self.dragging=False
536
537 - def OnDragOver(self, x, y, d):
538 target=self 539 t=self.mainwindow.GetCurrentActiveWidget() 540 if isinstance(t, FileSystemDirectoryView): 541 # changing target in dragndrop 542 target=t 543 # make sure that the files are being dropped onto a real directory 544 item, flags = self.HitTest((x, y)) 545 selections = self.GetSelections() 546 if item.IsOk(): 547 if selections != [item]: 548 self.UnselectAll() 549 self.SelectItem(item) 550 return wx._misc.DragCopy 551 elif selections: 552 self.UnselectAll() 553 return wx._misc.DragNone
554
555 - def _saveSelection(self):
556 self.selections = self.GetSelections() 557 self.UnselectAll()
558
559 - def _restoreSelection(self):
560 self.UnselectAll() 561 for i in self.selections: 562 self.SelectItem(i) 563 self.selections=[]
564
565 - def OnEnter(self, x, y, d):
566 self._saveSelection() 567 self.dragging=True 568 return d
569
570 - def OnLeave(self):
571 self.dragging=False 572 self._restoreSelection()
573
574 - def OnAddFiles(self):
575 mw=self.mainwindow 576 if not len(self.add_files): 577 return 578 for file in self.add_files: 579 if file is None: 580 continue 581 if len(self.add_target): 582 path=self.add_target+"/"+os.path.basename(file) 583 else: 584 path=os.path.basename(file) # you can't create files in root but I won't stop you 585 contents=open(file, "rb").read() 586 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 587 gui.Callback(self.OnAddFilesResults, self.add_target) ) 588 self.add_files.remove(file) 589 # can only add one file at a time 590 break
591
592 - def OnAddFilesResults(self, parentdir, exception, _):
593 mw=self.mainwindow 594 if mw.HandleException(exception): return 595 # add next file if there is one 596 if not len(self.add_files): 597 self.parent.ShowFiles(parentdir, True) 598 else: 599 self.OnAddFiles()
600
601 - def OnRightUp(self, event):
602 pt = event.GetPosition(); 603 item, flags = self.HitTest(pt) 604 if item.IsOk(): 605 self.SelectItem(item) 606 self.PopupMenu(self.dirmenu, pt) 607 else: 608 self.SelectItem(self.item) 609 self.PopupMenu(self.genericmenu, pt)
610
611 - def OnRightDown(self, _):
612 # You have to capture right down otherwise it doesn't feed you right up 613 pass
614
615 - def OnItemSelected(self,_):
616 if not self.dragging and not self.first_time: 617 item=self.GetSelection() 618 if item.IsOk() and item != self.item: 619 path=self.itemtopath(item) 620 self.parent.ShowFiles(path) 621 if not self.skip_dir_list: 622 self.OnDirListing(path) 623 self.item=item
624
625 - def OnItemExpanded(self, event):
626 if not self.skip_dir_list: 627 item=event.GetItem() 628 if self.first_time: 629 self.GetFullFS() 630 else: 631 path=self.itemtopath(item) 632 self.OnDirListing(path)
633
634 - def AddDirectory(self, location, name):
635 new_item=self.AppendItem(location, name) 636 self.SetPyData(new_item, None) 637 self.SetItemImage(new_item, self.img_dir) 638 self.SetItemImage(new_item, self.img_dir_open, which=wx.TreeItemIcon_Expanded) 639 # workaround for bug, + does not get displayed if this is the first child 640 if self.GetChildrenCount(location, False) == 1 and not self.IsExpanded(location): 641 self.skip_dir_list+=1 642 self.Expand(location) 643 self.Collapse(location) 644 self.skip_dir_list-=1 645 return new_item
646
647 - def RemoveDirectory(self, parent, item):
648 # if this is the last item in the parent we need to collapse the parent 649 if self.GetChildrenCount(parent, False) == 1: 650 self.Collapse(parent) 651 self.Delete(item)
652
653 - def GetFullFS(self):
654 mw=self.mainwindow 655 mw.OnBusyStart() 656 mw.GetStatusBar().progressminor(0, 100, 'Reading Phone File System ...') 657 mw.MakeCall( gui.Request(mw.wt.fulldirlisting), 658 gui.Callback(self.OnFullDirListingResults) )
659
660 - def OnFullDirListingResults(self, exception, result):
661 mw=self.mainwindow 662 mw.OnBusyEnd() 663 if mw.HandleException(exception): 664 self.Collapse(self.root) 665 return 666 self.first_time=False 667 self.skip_dir_list+=1 668 self.SelectItem(self.root) 669 self.DeleteChildren(self.root) 670 keys=result.keys() 671 keys.sort() 672 # build up the tree 673 for k in keys: 674 path, dir=os.path.split(k) 675 item=self.pathtoitem(path) 676 self.AddDirectory(item, dir) 677 self.skip_dir_list-=1 678 self.parent.ShowFiles("")
679
680 - def OnDirListing(self, path):
681 mw=self.mainwindow 682 mw.MakeCall( gui.Request(mw.wt.singledirlisting, path), 683 gui.Callback(self.OnDirListingResults, path) )
684
685 - def OnDirListingResults(self, path, exception, result):
686 mw=self.mainwindow 687 if mw.HandleException(exception): return 688 item=self.pathtoitem(path) 689 l=[] 690 child,cookie=self.GetFirstChild(item) 691 for dummy in range(0,self.GetChildrenCount(item,False)): 692 l.append(child) 693 child,cookie=self.GetNextChild(item,cookie) 694 # we now have a list of children in l 695 sort=False 696 for file in result: 697 children=True 698 f=guihelper.basename(file) 699 found=None 700 for i in l: 701 if self.GetItemText(i)==f: 702 found=i 703 break 704 if found is None: 705 # this only happens if the phone has added the directory 706 # after we got the initial file view, unusual but possible 707 found=self.AddDirectory(item, f) 708 self.OnDirListing(file) 709 sort=True 710 for i in l: # remove all children not present in result 711 if not result.has_key(self.itemtopath(i)): 712 self.RemoveDirectory(item, i) 713 if sort: 714 self.SortChildren(item)
715
716 - def OnNewSubdir(self, _):
717 with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, "Subdirectory name?", "Create Subdirectory", "newfolder"), 718 True) as (dlg, retcode): 719 if retcode==wx.ID_OK: 720 item=self.GetSelection() 721 parent=self.itemtopath(item) 722 if len(parent): 723 path=parent+"/"+dlg.GetValue() 724 else: 725 path=dlg.GetValue() 726 mw=self.mainwindow 727 mw.MakeCall( gui.Request(mw.wt.mkdir, path), 728 gui.Callback(self.OnNewSubdirResults, path) )
729
730 - def OnNewSubdirResults(self, new_path, exception, _):
731 mw=self.mainwindow 732 if mw.HandleException(exception): return 733 path, dir=os.path.split(new_path) 734 item=self.pathtoitem(path) 735 self.AddDirectory(item, dir) 736 self.SortChildren(item) 737 self.Expand(item) 738 # requery the phone just incase 739 self.OnDirListing(path)
740
741 - def OnNewFile(self,_):
742 parent=self.itemtopath(self.GetSelection()) 743 with guihelper.WXDialogWrapper(wx.FileDialog(self, style=wx.OPEN|wx.HIDE_READONLY|wx.CHANGE_DIR), 744 True) as (dlg, retcode): 745 if retcode==wx.ID_OK: 746 infile=dlg.GetPath() 747 contents=open(infile, "rb").read() 748 if len(parent): 749 path=parent+"/"+os.path.basename(dlg.GetPath()) 750 else: 751 path=os.path.basename(dlg.GetPath()) # you can't create files in root but I won't stop you 752 mw=self.mainwindow 753 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 754 gui.Callback(self.OnNewFileResults, parent) )
755
756 - def OnNewFileResults(self, parentdir, exception, _):
757 mw=self.mainwindow 758 if mw.HandleException(exception): return 759 self.parent.ShowFiles(parentdir, True)
760
761 - def OnDirDelete(self, _):
762 path=self.itemtopath(self.GetSelection()) 763 mw=self.mainwindow 764 mw.MakeCall( gui.Request(mw.wt.rmdirs, path), 765 gui.Callback(self.OnDirDeleteResults, path) )
766
767 - def OnDirDeleteResults(self, path, exception, _):
768 mw=self.mainwindow 769 if mw.HandleException(exception): return 770 # remove the directory from the view 771 parent, dir=os.path.split(path) 772 parent_item=self.pathtoitem(parent) 773 del_item=self.pathtoitem(path) 774 self.RemoveDirectory(parent_item, del_item) 775 # requery the phone just incase 776 self.OnDirListing(parent)
777
778 - def OnBackupTree(self, _):
779 self.OnBackup(recurse=100)
780
781 - def OnBackupDirectory(self, _):
782 self.OnBackup()
783
784 - def OnBackup(self, recurse=0):
785 path=self.itemtopath(self.GetSelection()) 786 mw=self.mainwindow 787 mw.MakeCall( gui.Request(mw.wt.getbackup, path, recurse), 788 gui.Callback(self.OnBackupResults, path) )
789
790 - def OnBackupResults(self, path, exception, backup):
791 mw=self.mainwindow 792 if mw.HandleException(exception): return 793 bn=guihelper.basename(path) 794 if len(bn)<1: 795 bn="root" 796 bn+=".zip" 797 ext="Zip files|*.zip|All Files|*" 798 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Save File As", defaultFile=bn, wildcard=ext, 799 style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR), 800 True) as (dlg, retcode): 801 if retcode==wx.ID_OK: 802 file(dlg.GetPath(), "wb").write(backup)
803
804 - def OnRestore(self, _):
805 ext="Zip files|*.zip|All Files|*" 806 path=self.itemtopath(self.GetSelection()) 807 bn=guihelper.basename(path) 808 if len(bn)<1: 809 bn="root" 810 bn+=".zip" 811 ext="Zip files|*.zip|All Files|*" 812 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Open backup file", defaultFile=bn, wildcard=ext, 813 style=wx.OPEN|wx.HIDE_READONLY|wx.CHANGE_DIR), 814 True) as (dlg, retcode): 815 if retcode==wx.ID_OK: 816 name=dlg.GetPath() 817 if not zipfile.is_zipfile(name): 818 with guihelper.WXDialogWrapper(guiwidgets.AlertDialogWithHelp(self.mainwindow, name+" is not a valid zipfile.", "Zip file required", 819 lambda _: wx.GetApp().displayhelpid(helpids.ID_NOT_A_ZIPFILE), 820 style=wx.OK|wx.ICON_ERROR), 821 True): 822 return 823 zipf=zipfile.ZipFile(name, "r") 824 xx=zipf.testzip() 825 if xx is not None: 826 with guihelper.WXDialogWrapper(guiwidgets.AlertDialogWithHelp(self.mainwindow, name+" has corrupted contents. Use a repair utility to fix it", 827 "Zip file corrupted", 828 lambda _: wx.GetApp().displayhelpid(helpids.ID_ZIPFILE_CORRUPTED), 829 style=wx.OK|wx.ICON_ERROR), 830 True): 831 return 832 833 RestoreDialog(self.mainwindow, "Restore files", zipf, path, self.OnRestoreOK).Show(True)
834
835 - def OnRestoreOK(self, zipf, names, parentdir):
836 if len(names)==0: 837 wx.MessageBox("You didn't select any files to restore!", "No files selected", 838 wx.OK|wx.ICON_EXCLAMATION) 839 return 840 l=[] 841 for zipname, fsname in names: 842 l.append( (fsname, zipf.read(zipname)) ) 843 844 mw=self.mainwindow 845 mw.MakeCall( gui.Request(mw.wt.restorefiles, l), 846 gui.Callback(self.OnRestoreResults, parentdir) )
847
848 - def OnRestoreResults(self, parentdir, exception, results):
849 mw=self.mainwindow 850 if mw.HandleException(exception): return 851 ok=filter(lambda s: s[0], results) 852 fail=filter(lambda s: not s[0], results) 853 854 # re-read the filesystem (if anything was restored) 855 if len(parentdir): 856 dirs=[] 857 for _, name in results: 858 while(len(name)>len(parentdir)): 859 name=guihelper.dirname(name) 860 if name not in dirs: 861 dirs.append(name) 862 dirs.sort() 863 for d in dirs: 864 self.OnDirListing(d) 865 866 self.OnDirListing(parentdir) 867 868 if len(ok) and len(fail)==0: 869 dlg=wx.MessageDialog(mw, "All files restored ok", "All files restored", 870 wx.OK|wx.ICON_INFORMATION) 871 dlg.Show(True) 872 return 873 if len(fail) and len(ok)==0: 874 wx.MessageBox("All files failed to restore", "No files restored", 875 wx.OK|wx.ICON_ERROR) 876 return 877 878 op="Failed to restore some files. Check the log for reasons.:\n\n" 879 for s,n in fail: 880 op+=" "+n+"\n" 881 wx.MessageBox(op, "Some restores failed", wx.OK|wx.ICON_ERROR)
882
883 - def OnDirRefresh(self, _):
884 path=self.itemtopath(self.GetSelection()) 885 self.parent.ShowFiles(path, True) 886 self.OnDirListing(path)
887
888 - def OnRefresh(self, _):
889 self.GetFullFS()
890
891 - def itemtopath(self, item):
892 if item==self.root: return "" 893 res=self.GetItemText(item) 894 while True: 895 parent=self.GetItemParent(item) 896 if parent==self.root: 897 return res 898 item=parent 899 res=self.GetItemText(item)+"/"+res 900 # can't get here, but pychecker doesn't seem to realise 901 assert False 902 return ""
903
904 - def pathtoitem(self, path):
905 if path=="": return self.root 906 dirs=path.split('/') 907 node=self.root 908 for n in range(0, len(dirs)): 909 foundnode=None 910 child,cookie=self.GetFirstChild(node) 911 for dummy in range(0, self.GetChildrenCount(node, False)): 912 d=self.GetItemText(child) 913 if d==dirs[n]: 914 node=child 915 foundnode=node 916 break 917 child,cookie=self.GetNextChild(node,cookie) 918 if foundnode is not None: 919 continue 920 # make the node 921 node=self.AppendItem(node, dirs[n]) 922 self.SetPyData(node, None) 923 return node
924
925 -class RestoreDialog(wx.Dialog):
926 """A dialog that lists all the files that will be restored""" 927
928 - def __init__(self, parent, title, zipf, path, okcb):
929 """Constructor 930 931 @param path: Placed before names in the archive. Should not include a 932 trailing slash. 933 """ 934 wx.Dialog.__init__(self, parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 935 vbs=wx.BoxSizer(wx.VERTICAL) 936 vbs.Add( wx.StaticText(self, -1, "Choose files to restore"), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 937 938 nl=zipf.namelist() 939 nl.sort() 940 941 prefix=path 942 if len(prefix)=="/" or prefix=="": 943 prefix="" 944 else: 945 prefix+="/" 946 947 nnl=map(lambda i: prefix+i, nl) 948 949 self.clb=wx.CheckListBox(self, -1, choices=nnl, style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB, size=wx.Size(200,300)) 950 951 for i in range(len(nnl)): 952 self.clb.Check(i, True) 953 954 vbs.Add( self.clb, 1, wx.EXPAND|wx.ALL, 5) 955 956 vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 957 958 vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTER|wx.ALL, 5) 959 960 self.SetSizer(vbs) 961 self.SetAutoLayout(True) 962 vbs.Fit(self) 963 964 wx.EVT_BUTTON(self, wx.ID_HELP, lambda _: wx.GetApp().displayhelpid(helpids.ID_RESTOREDIALOG)) 965 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOK) 966 self.okcb=okcb 967 self.zipf=zipf 968 self.nl=zip(nl, nnl) 969 self.path=path
970
971 - def OnOK(self, _):
972 names=[] 973 for i in range(len(self.nl)): 974 if self.clb.IsChecked(i): 975 names.append(self.nl[i]) 976 self.okcb(self.zipf, names, self.path) 977 self.Show(False) 978 self.Destroy()
979