0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: filesystem.py 4390 2007-09-05 00:08:19Z djpham $ 0009 0010 """The main gui code for BitPim""" 0011 0012 # System modules 0013 from __future__ import with_statement 0014 import ConfigParser 0015 import thread, threading 0016 import Queue 0017 import time 0018 import os 0019 import cStringIO 0020 import zipfile 0021 import re 0022 import sys 0023 import shutil 0024 import types 0025 import datetime 0026 import sha 0027 import codecs 0028 import fileview 0029 import widgets 0030 0031 # wx modules 0032 import wx 0033 import wx.lib.colourdb 0034 import wx.gizmos 0035 import wx.html 0036 import wx.lib.mixins.listctrl as listmix 0037 0038 # my modules 0039 import guiwidgets 0040 import common 0041 import helpids 0042 import comdiagnose 0043 import guihelper 0044 import hexeditor 0045 import pubsub 0046 import phones.com_brew as com_brew 0047 import gui 0048 import widgets 0049 0050 0051 class FileSystemView(wx.SplitterWindow, widgets.BitPimWidget): 0052 def __init__(self, mainwindow, parent, id=-1): 0053 # the listbox and textbox in a splitter 0054 self.mainwindow=mainwindow 0055 wx.SplitterWindow.__init__(self, parent, id, style=wx.SP_LIVE_UPDATE) 0056 self.tree=FileSystemDirectoryView(mainwindow, self, wx.NewId(), style=(wx.TR_DEFAULT_STYLE|wx.TR_NO_LINES)&~wx.TR_TWIST_BUTTONS) 0057 self.list=FileSystemFileView(mainwindow, self, wx.NewId()) 0058 self.sash_pos=mainwindow.config.ReadInt("filesystemsplitterpos", 200) 0059 self.update_sash=False 0060 self.SplitVertically(self.tree, self.list, self.sash_pos) 0061 self.SetMinimumPaneSize(20) 0062 wx.EVT_SPLITTER_SASH_POS_CHANGED(self, id, self.OnSplitterPosChanged) 0063 pubsub.subscribe(self.OnPhoneModelChanged, pubsub.PHONE_MODEL_CHANGED) 0064 0065 def __del__(self): 0066 pubsub.unsubscribe(self.OnPhoneModelChanged) 0067 0068 def OnPhoneModelChanged(self, msg): 0069 # if the phone changes we reset ourselves 0070 self.list.ResetView() 0071 self.tree.ResetView() 0072 0073 def OnSplitterPosChanged(self,_): 0074 if self.update_sash: 0075 self.sash_pos=self.GetSashPosition() 0076 self.mainwindow.config.WriteInt("filesystemsplitterpos", 0077 self.sash_pos) 0078 0079 def OnPreActivate(self): 0080 self.update_sash=False 0081 def OnPostActivate(self): 0082 self.SetSashPosition(self.sash_pos) 0083 self.update_sash=True 0084 0085 def OnPhoneReboot(self,_): 0086 mw=self.mainwindow 0087 mw.MakeCall( gui.Request(mw.wt.phonerebootrequest), 0088 gui.Callback(self.OnPhoneRebootResults) ) 0089 0090 def OnPhoneRebootResults(self, exception, _): 0091 # special case - we always clear the comm connection 0092 # it is needed if the reboot succeeds, and if it didn't 0093 # we probably have bad comms anyway 0094 mw=self.mainwindow 0095 mw.wt.clearcomm() 0096 if mw.HandleException(exception): return 0097 0098 def OnPhoneOffline(self,_): 0099 mw=self.mainwindow 0100 mw.MakeCall( gui.Request(mw.wt.phoneofflinerequest), 0101 gui.Callback(self.OnPhoneOfflineResults) ) 0102 0103 def OnPhoneOfflineResults(self, exception, _): 0104 mw=self.mainwindow 0105 if mw.HandleException(exception): return 0106 0107 def OnModemMode(self,_): 0108 mw=self.mainwindow 0109 mw.MakeCall( gui.Request(mw.wt.modemmoderequest), 0110 gui.Callback(self.OnModemModeResults) ) 0111 0112 def OnModemModeResults(self, exception, _): 0113 mw=self.mainwindow 0114 if mw.HandleException(exception): return 0115 0116 def ShowFiles(self, dir, refresh=False): 0117 self.list.ShowFiles(dir, refresh) 0118 0119 def OnNewFileResults(self, parentdir, exception, _): 0120 mw=self.mainwindow 0121 if mw.HandleException(exception): return 0122 self.ShowFiles(parentdir, True) 0123 0124 class FileSystemFileView(wx.ListCtrl, listmix.ColumnSorterMixin): 0125 def __init__(self, mainwindow, parent, id, style=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_SINGLE_SEL): 0126 wx.ListCtrl.__init__(self, parent, id, style=style) 0127 self.parent=parent 0128 self.mainwindow=mainwindow 0129 self.datacolumn=False # used for debugging and inspection of values 0130 self.InsertColumn(0, "Name", width=300) 0131 self.InsertColumn(1, "Size", format=wx.LIST_FORMAT_RIGHT) 0132 self.InsertColumn(2, "Date", width=200) 0133 self.font=wx.TheFontList.FindOrCreateFont(10, family=wx.SWISS, style=wx.NORMAL, weight=wx.NORMAL) 0134 0135 self.ResetView() 0136 0137 if self.datacolumn: 0138 self.InsertColumn(3, "Extra Stuff", width=400) 0139 listmix.ColumnSorterMixin.__init__(self, 4) 0140 else: 0141 listmix.ColumnSorterMixin.__init__(self, 3) 0142 0143 #sort by genre (column 2), A->Z ascending order (1) 0144 self.filemenu=wx.Menu() 0145 self.filemenu.Append(guihelper.ID_FV_SAVE, "Save ...") 0146 self.filemenu.Append(guihelper.ID_FV_HEXVIEW, "Hexdump") 0147 self.filemenu.AppendSeparator() 0148 self.filemenu.Append(guihelper.ID_FV_DELETE, "Delete") 0149 self.filemenu.Append(guihelper.ID_FV_OVERWRITE, "Overwrite ...") 0150 # generic menu 0151 self.genericmenu=wx.Menu() 0152 self.genericmenu.Append(guihelper.ID_FV_NEWFILE, "New File ...") 0153 self.genericmenu.AppendSeparator() 0154 self.genericmenu.Append(guihelper.ID_FV_OFFLINEPHONE, "Offline Phone") 0155 self.genericmenu.Append(guihelper.ID_FV_REBOOTPHONE, "Reboot Phone") 0156 self.genericmenu.Append(guihelper.ID_FV_MODEMMODE, "Go to modem mode") 0157 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_NEWFILE, self.OnNewFile) 0158 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_OFFLINEPHONE, parent.OnPhoneOffline) 0159 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_REBOOTPHONE, parent.OnPhoneReboot) 0160 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_MODEMMODE, parent.OnModemMode) 0161 wx.EVT_MENU(self.filemenu, guihelper.ID_FV_SAVE, self.OnFileSave) 0162 wx.EVT_MENU(self.filemenu, guihelper.ID_FV_HEXVIEW, self.OnHexView) 0163 wx.EVT_MENU(self.filemenu, guihelper.ID_FV_DELETE, self.OnFileDelete) 0164 wx.EVT_MENU(self.filemenu, guihelper.ID_FV_OVERWRITE, self.OnFileOverwrite) 0165 wx.EVT_RIGHT_DOWN(self.GetMainWindow(), self.OnRightDown) 0166 wx.EVT_RIGHT_UP(self.GetMainWindow(), self.OnRightUp) 0167 wx.EVT_LIST_ITEM_ACTIVATED(self,id, self.OnItemActivated) 0168 self.image_list=wx.ImageList(16, 16) 0169 a={"sm_up":"GO_UP","sm_dn":"GO_DOWN","w_idx":"WARNING","e_idx":"ERROR","i_idx":"QUESTION"} 0170 for k,v in a.items(): 0171 s="self.%s= self.image_list.Add(wx.ArtProvider_GetBitmap(wx.ART_%s,wx.ART_TOOLBAR,(16,16)))" % (k,v) 0172 exec(s) 0173 self.img_file=self.image_list.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, 0174 wx.ART_OTHER, 0175 (16, 16))) 0176 self.SetImageList(self.image_list, wx.IMAGE_LIST_SMALL) 0177 0178 #if guihelper.IsMSWindows(): 0179 # turn on drag-and-drag for windows 0180 #wx.EVT_MOTION(self, self.OnStartDrag) 0181 0182 self.__dragging=False 0183 self.add_files=[] 0184 self.droptarget=fileview.MyFileDropTarget(self, True, False) 0185 self.SetDropTarget(self.droptarget) 0186 0187 def OnPaint(self, evt): 0188 w,h=self.GetSize() 0189 self.Refresh() 0190 dc=wx.PaintDC(self) 0191 dc.BeginDrawing() 0192 dc.SetFont(self.font) 0193 x,y= dc.GetTextExtent("There are no items to show in this view") 0194 # center the text 0195 xx=(w-x)/2 0196 if xx<0: 0197 xx=0 0198 dc.DrawText("There are no items to show in this view", xx, h/3) 0199 dc.EndDrawing() 0200 0201 def OnDropFiles(self, _, dummy, filenames): 0202 # There is a bug in that the most recently created tab 0203 # in the notebook that accepts filedrop receives these 0204 # files, not the most visible one. We find the currently 0205 # viewed tab in the notebook and send the files there 0206 if self.__dragging: 0207 # I'm the drag source, forget 'bout it ! 0208 return 0209 target=self # fallback 0210 t=self.mainwindow.GetCurrentActiveWidget() 0211 if isinstance(t, FileSystemFileView): 0212 # changing target in dragndrop 0213 target=t 0214 self.add_files=filenames 0215 target.OnAddFiles() 0216 0217 def OnDragOver(self, x, y, d): 0218 # force copy (instead of move) 0219 return wx._misc.DragCopy 0220 0221 def OnAddFiles(self): 0222 mw=self.mainwindow 0223 if not len(self.add_files): 0224 return 0225 for file in self.add_files: 0226 if file is None: 0227 continue 0228 if len(self.path): 0229 path=self.path+"/"+os.path.basename(file) 0230 else: 0231 path=os.path.basename(file) # you can't create files in root but I won't stop you 0232 contents=open(file, "rb").read() 0233 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 0234 gui.Callback(self.OnAddFilesResults, self.path) ) 0235 self.add_files.remove(file) 0236 # can only add one file at a time 0237 break 0238 0239 def OnAddFilesResults(self, parentdir, exception, _): 0240 mw=self.mainwindow 0241 if mw.HandleException(exception): return 0242 # add next file if there is one 0243 if not len(self.add_files): 0244 self.ShowFiles(parentdir, True) 0245 else: 0246 self.OnAddFiles() 0247 0248 if guihelper.IsMSWindows(): 0249 # drag-and-drop files only works in Windows 0250 def OnStartDrag(self, evt): 0251 evt.Skip() 0252 if not evt.LeftIsDown(): 0253 return 0254 path=self.itemtopath(self.GetFirstSelected()) 0255 drag_source=wx.DropSource(self) 0256 file_names=wx.FileDataObject() 0257 file_names.AddFile(path) 0258 drag_source.SetData(file_names) 0259 self.__dragging=True 0260 res=drag_source.DoDragDrop(wx.Drag_CopyOnly) 0261 self.__dragging=False 0262 0263 def OnRightUp(self, event): 0264 pt = event.GetPosition() 0265 item, flags = self.HitTest(pt) 0266 if item is not -1: 0267 self.Select(item) 0268 self.PopupMenu(self.filemenu, pt) 0269 else: 0270 self.PopupMenu(self.genericmenu, pt) 0271 0272 def OnRightDown(self,event): 0273 # You have to capture right down otherwise it doesn't feed you right up 0274 pt = event.GetPosition(); 0275 item, flags = self.HitTest(pt) 0276 try: 0277 self.Select(item) 0278 except: 0279 pass 0280 0281 def OnNewFile(self,_): 0282 with guihelper.WXDialogWrapper(wx.FileDialog(self, style=wx.OPEN|wx.HIDE_READONLY|wx.CHANGE_DIR), 0283 True) as (dlg, retcode): 0284 if retcode==wx.ID_OK: 0285 infile=dlg.GetPath() 0286 contents=open(infile, "rb").read() 0287 if len(self.path): 0288 path=self.path+"/"+os.path.basename(dlg.GetPath()) 0289 else: 0290 path=os.path.basename(dlg.GetPath()) # you can't create files in root but I won't stop you 0291 mw=self.mainwindow 0292 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 0293 gui.Callback(self.parent.OnNewFileResults, self.path) ) 0294 0295 def OnFileSave(self, _): 0296 path=self.itemtopath(self.GetFirstSelected()) 0297 mw=self.mainwindow 0298 mw.MakeCall( gui.Request(mw.wt.getfile, path), 0299 gui.Callback(self.OnFileSaveResults, path) ) 0300 0301 def OnFileSaveResults(self, path, exception, contents): 0302 mw=self.mainwindow 0303 if mw.HandleException(exception): return 0304 bn=guihelper.basename(path) 0305 ext=guihelper.getextension(bn) 0306 if len(ext): 0307 ext="%s files (*.%s)|*.%s" % (ext.upper(), ext, ext) 0308 else: 0309 ext="All files|*" 0310 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Save File As", defaultFile=bn, wildcard=ext, 0311 style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR), 0312 True) as (dlg, retcode): 0313 if retcode==wx.ID_OK: 0314 file(dlg.GetPath(), "wb").write(contents) 0315 0316 def OnItemActivated(self,_): 0317 self.OnHexView(self) 0318 0319 def OnHexView(self, _): 0320 path=self.itemtopath(self.GetFirstSelected()) 0321 mw=self.mainwindow 0322 mw.MakeCall( gui.Request(mw.wt.getfile, path), 0323 gui.Callback(self.OnHexViewResults, path) ) 0324 0325 def OnHexViewResults(self, path, exception, result): 0326 mw=self.mainwindow 0327 if mw.HandleException(exception): return 0328 # ::TODO:: make this use HexEditor 0329 ## dlg=guiwidgets.MyFixedScrolledMessageDialog(self, common.datatohexstring(result), 0330 ## path+" Contents", helpids.ID_HEXVIEW_DIALOG) 0331 dlg=hexeditor.HexEditorDialog(self, result, path+" Contents") 0332 dlg.Show() 0333 0334 def OnFileDelete(self, _): 0335 path=self.itemtopath(self.GetFirstSelected()) 0336 mw=self.mainwindow 0337 mw.MakeCall( gui.Request(mw.wt.rmfile, path), 0338 gui.Callback(self.OnFileDeleteResults, guihelper.dirname(path)) ) 0339 0340 def OnFileDeleteResults(self, parentdir, exception, _): 0341 mw=self.mainwindow 0342 if mw.HandleException(exception): return 0343 self.ShowFiles(parentdir, True) 0344 0345 def OnFileOverwrite(self,_): 0346 path=self.itemtopath(self.GetFirstSelected()) 0347 with guihelper.WXDialogWrapper(wx.FileDialog(self, style=wx.OPEN|wx.HIDE_READONLY|wx.CHANGE_DIR), 0348 True) as (dlg, retcode): 0349 if retcode==wx.ID_OK: 0350 infile=dlg.GetPath() 0351 contents=open(infile, "rb").read() 0352 mw=self.mainwindow 0353 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 0354 gui.Callback(self.OnFileOverwriteResults, guihelper.dirname(path)) ) 0355 0356 def OnFileOverwriteResults(self, parentdir, exception, _): 0357 mw=self.mainwindow 0358 if mw.HandleException(exception): return 0359 self.ShowFiles(parentdir, True) 0360 0361 def ResetView(self): 0362 self.DeleteAllItems() 0363 self.files={} 0364 self.path=None 0365 self.itemDataMap = self.files 0366 self.itemIndexMap = self.files.keys() 0367 self.SetItemCount(0) 0368 0369 def ShowFiles(self, path, refresh=False): 0370 mw=self.mainwindow 0371 if path == self.path and not refresh: 0372 return 0373 self.path=None 0374 mw.MakeCall( gui.Request(mw.wt.getfileonlylist, path), 0375 gui.Callback(self.OnShowFilesResults, path) ) 0376 0377 def OnShowFilesResults(self, path, exception, result): 0378 mw=self.mainwindow 0379 if mw.HandleException(exception): return 0380 count=self.GetItemCount() 0381 self.path=path 0382 self.DeleteAllItems() 0383 self.files={} 0384 index=0 0385 for file in result: 0386 index=index+1 0387 f=guihelper.basename(file) 0388 if self.datacolumn: 0389 self.files[index]=(f, `result[file]['size']`, result[file]['date'][1], result[file]['data'], file) 0390 else: 0391 self.files[index]=(f, `result[file]['size']`, result[file]['date'][1], file) 0392 self.itemDataMap = self.files 0393 self.itemIndexMap = self.files.keys() 0394 self.SetItemCount(index) 0395 self.SortListItems() 0396 if count!=0 and index==0: 0397 wx.EVT_PAINT(self, self.OnPaint) 0398 elif count==0 and index!=0: 0399 self.Unbind(wx.EVT_PAINT) 0400 0401 def itemtopath(self, item): 0402 index=self.itemIndexMap[item] 0403 if self.datacolumn: 0404 return self.itemDataMap[index][4] 0405 return self.itemDataMap[index][3] 0406 0407 def SortItems(self,sorter=None): 0408 col=self._col 0409 sf=self._colSortFlag[col] 0410 0411 #creating pairs [column item defined by col, key] 0412 items=[] 0413 for k,v in self.itemDataMap.items(): 0414 if col==1: 0415 items.append([int(v[col]),k]) 0416 else: 0417 items.append([v[col],k]) 0418 0419 items.sort() 0420 k=[key for value, key in items] 0421 0422 # False is descending 0423 if sf==False: 0424 k.reverse() 0425 0426 self.itemIndexMap=k 0427 0428 #redrawing the list 0429 self.Refresh() 0430 0431 def GetListCtrl(self): 0432 return self 0433 0434 def GetSortImages(self): 0435 return (self.sm_dn, self.sm_up) 0436 0437 def OnGetItemText(self, item, col): 0438 index=self.itemIndexMap[item] 0439 s = self.itemDataMap[index][col] 0440 return s 0441 0442 def OnGetItemImage(self, item): 0443 return self.img_file 0444 0445 def OnGetItemAttr(self, item): 0446 return None 0447 0448 class FileSystemDirectoryView(wx.TreeCtrl): 0449 def __init__(self, mainwindow, parent, id, style): 0450 wx.TreeCtrl.__init__(self, parent, id, style=style) 0451 self.parent=parent 0452 self.mainwindow=mainwindow 0453 wx.EVT_TREE_ITEM_EXPANDED(self, id, self.OnItemExpanded) 0454 wx.EVT_TREE_SEL_CHANGED(self,id, self.OnItemSelected) 0455 self.dirmenu=wx.Menu() 0456 self.dirmenu.Append(guihelper.ID_FV_NEWSUBDIR, "Make subdirectory ...") 0457 self.dirmenu.Append(guihelper.ID_FV_NEWFILE, "New File ...") 0458 self.dirmenu.AppendSeparator() 0459 self.dirmenu.Append(guihelper.ID_FV_BACKUP, "Backup directory ...") 0460 self.dirmenu.Append(guihelper.ID_FV_BACKUP_TREE, "Backup entire tree ...") 0461 self.dirmenu.Append(guihelper.ID_FV_RESTORE, "Restore ...") 0462 self.dirmenu.AppendSeparator() 0463 self.dirmenu.Append(guihelper.ID_FV_REFRESH, "Refresh") 0464 self.dirmenu.AppendSeparator() 0465 self.dirmenu.Append(guihelper.ID_FV_DELETE, "Delete") 0466 self.dirmenu.AppendSeparator() 0467 self.dirmenu.Append(guihelper.ID_FV_TOTAL_REFRESH, "Refresh Filesystem") 0468 self.dirmenu.Append(guihelper.ID_FV_OFFLINEPHONE, "Offline Phone") 0469 self.dirmenu.Append(guihelper.ID_FV_REBOOTPHONE, "Reboot Phone") 0470 self.dirmenu.Append(guihelper.ID_FV_MODEMMODE, "Go to modem mode") 0471 # generic menu 0472 self.genericmenu=wx.Menu() 0473 self.genericmenu.Append(guihelper.ID_FV_TOTAL_REFRESH, "Refresh Filesystem") 0474 self.genericmenu.Append(guihelper.ID_FV_OFFLINEPHONE, "Offline Phone") 0475 self.genericmenu.Append(guihelper.ID_FV_REBOOTPHONE, "Reboot Phone") 0476 self.genericmenu.Append(guihelper.ID_FV_MODEMMODE, "Go to modem mode") 0477 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_TOTAL_REFRESH, self.OnRefresh) 0478 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_OFFLINEPHONE, parent.OnPhoneOffline) 0479 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_REBOOTPHONE, parent.OnPhoneReboot) 0480 wx.EVT_MENU(self.genericmenu, guihelper.ID_FV_MODEMMODE, parent.OnModemMode) 0481 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_NEWSUBDIR, self.OnNewSubdir) 0482 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_NEWFILE, self.OnNewFile) 0483 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_DELETE, self.OnDirDelete) 0484 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_BACKUP, self.OnBackupDirectory) 0485 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_BACKUP_TREE, self.OnBackupTree) 0486 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_RESTORE, self.OnRestore) 0487 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_REFRESH, self.OnDirRefresh) 0488 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_TOTAL_REFRESH, self.OnRefresh) 0489 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_OFFLINEPHONE, parent.OnPhoneOffline) 0490 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_REBOOTPHONE, parent.OnPhoneReboot) 0491 wx.EVT_MENU(self.dirmenu, guihelper.ID_FV_MODEMMODE, parent.OnModemMode) 0492 wx.EVT_RIGHT_DOWN(self, self.OnRightDown) 0493 wx.EVT_RIGHT_UP(self, self.OnRightUp) 0494 self.image_list=wx.ImageList(16, 16) 0495 self.img_dir=self.image_list.Add(wx.ArtProvider_GetBitmap(guihelper.ART_FOLDER, 0496 wx.ART_OTHER, 0497 (16, 16))) 0498 self.img_dir_open=self.image_list.Add(wx.ArtProvider_GetBitmap(guihelper.ART_FOLDER_OPEN, 0499 wx.ART_OTHER, 0500 (16, 16))) 0501 self.SetImageList(self.image_list) 0502 self.add_files=[] 0503 self.add_target="" 0504 self.droptarget=fileview.MyFileDropTarget(self, True, True) 0505 self.SetDropTarget(self.droptarget) 0506 self.ResetView() 0507 0508 def ResetView(self): 0509 self.first_time=True 0510 self.DeleteAllItems() 0511 self.root=self.AddRoot("/") 0512 self.item=self.root 0513 self.SetPyData(self.root, None) 0514 self.SetItemHasChildren(self.root, True) 0515 self.SetItemImage(self.root, self.img_dir) 0516 self.SetItemImage(self.root, self.img_dir_open, which=wx.TreeItemIcon_Expanded) 0517 self.SetPyData(self.AppendItem(self.root, "Retrieving..."), None) 0518 self.selections=[] 0519 self.dragging=False 0520 self.skip_dir_list=0 0521 0522 def OnDropFiles(self, x, y, filenames): 0523 target=self 0524 t=self.mainwindow.GetCurrentActiveWidget() 0525 if isinstance(t, FileSystemDirectoryView): 0526 # changing target in dragndrop 0527 target=t 0528 # make sure that the files are being dropped onto a real directory 0529 item, flags = self.HitTest((x, y)) 0530 if item.IsOk(): 0531 self.SelectItem(item) 0532 self.add_target=self.itemtopath(item) 0533 self.add_files=filenames 0534 target.OnAddFiles() 0535 self.dragging=False 0536 0537 def OnDragOver(self, x, y, d): 0538 target=self 0539 t=self.mainwindow.GetCurrentActiveWidget() 0540 if isinstance(t, FileSystemDirectoryView): 0541 # changing target in dragndrop 0542 target=t 0543 # make sure that the files are being dropped onto a real directory 0544 item, flags = self.HitTest((x, y)) 0545 selections = self.GetSelections() 0546 if item.IsOk(): 0547 if selections != [item]: 0548 self.UnselectAll() 0549 self.SelectItem(item) 0550 return wx._misc.DragCopy 0551 elif selections: 0552 self.UnselectAll() 0553 return wx._misc.DragNone 0554 0555 def _saveSelection(self): 0556 self.selections = self.GetSelections() 0557 self.UnselectAll() 0558 0559 def _restoreSelection(self): 0560 self.UnselectAll() 0561 for i in self.selections: 0562 self.SelectItem(i) 0563 self.selections=[] 0564 0565 def OnEnter(self, x, y, d): 0566 self._saveSelection() 0567 self.dragging=True 0568 return d 0569 0570 def OnLeave(self): 0571 self.dragging=False 0572 self._restoreSelection() 0573 0574 def OnAddFiles(self): 0575 mw=self.mainwindow 0576 if not len(self.add_files): 0577 return 0578 for file in self.add_files: 0579 if file is None: 0580 continue 0581 if len(self.add_target): 0582 path=self.add_target+"/"+os.path.basename(file) 0583 else: 0584 path=os.path.basename(file) # you can't create files in root but I won't stop you 0585 contents=open(file, "rb").read() 0586 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 0587 gui.Callback(self.OnAddFilesResults, self.add_target) ) 0588 self.add_files.remove(file) 0589 # can only add one file at a time 0590 break 0591 0592 def OnAddFilesResults(self, parentdir, exception, _): 0593 mw=self.mainwindow 0594 if mw.HandleException(exception): return 0595 # add next file if there is one 0596 if not len(self.add_files): 0597 self.parent.ShowFiles(parentdir, True) 0598 else: 0599 self.OnAddFiles() 0600 0601 def OnRightUp(self, event): 0602 pt = event.GetPosition(); 0603 item, flags = self.HitTest(pt) 0604 if item.IsOk(): 0605 self.SelectItem(item) 0606 self.PopupMenu(self.dirmenu, pt) 0607 else: 0608 self.SelectItem(self.item) 0609 self.PopupMenu(self.genericmenu, pt) 0610 0611 def OnRightDown(self, _): 0612 # You have to capture right down otherwise it doesn't feed you right up 0613 pass 0614 0615 def OnItemSelected(self,_): 0616 if not self.dragging and not self.first_time: 0617 item=self.GetSelection() 0618 if item.IsOk() and item != self.item: 0619 path=self.itemtopath(item) 0620 self.parent.ShowFiles(path) 0621 if not self.skip_dir_list: 0622 self.OnDirListing(path) 0623 self.item=item 0624 0625 def OnItemExpanded(self, event): 0626 if not self.skip_dir_list: 0627 item=event.GetItem() 0628 if self.first_time: 0629 self.GetFullFS() 0630 else: 0631 path=self.itemtopath(item) 0632 self.OnDirListing(path) 0633 0634 def AddDirectory(self, location, name): 0635 new_item=self.AppendItem(location, name) 0636 self.SetPyData(new_item, None) 0637 self.SetItemImage(new_item, self.img_dir) 0638 self.SetItemImage(new_item, self.img_dir_open, which=wx.TreeItemIcon_Expanded) 0639 # workaround for bug, + does not get displayed if this is the first child 0640 if self.GetChildrenCount(location, False) == 1 and not self.IsExpanded(location): 0641 self.skip_dir_list+=1 0642 self.Expand(location) 0643 self.Collapse(location) 0644 self.skip_dir_list-=1 0645 return new_item 0646 0647 def RemoveDirectory(self, parent, item): 0648 # if this is the last item in the parent we need to collapse the parent 0649 if self.GetChildrenCount(parent, False) == 1: 0650 self.Collapse(parent) 0651 self.Delete(item) 0652 0653 def GetFullFS(self): 0654 mw=self.mainwindow 0655 mw.OnBusyStart() 0656 mw.GetStatusBar().progressminor(0, 100, 'Reading Phone File System ...') 0657 mw.MakeCall( gui.Request(mw.wt.fulldirlisting), 0658 gui.Callback(self.OnFullDirListingResults) ) 0659 0660 def OnFullDirListingResults(self, exception, result): 0661 mw=self.mainwindow 0662 mw.OnBusyEnd() 0663 if mw.HandleException(exception): 0664 self.Collapse(self.root) 0665 return 0666 self.first_time=False 0667 self.skip_dir_list+=1 0668 self.SelectItem(self.root) 0669 self.DeleteChildren(self.root) 0670 keys=result.keys() 0671 keys.sort() 0672 # build up the tree 0673 for k in keys: 0674 path, dir=os.path.split(k) 0675 item=self.pathtoitem(path) 0676 self.AddDirectory(item, dir) 0677 self.skip_dir_list-=1 0678 self.parent.ShowFiles("") 0679 0680 def OnDirListing(self, path): 0681 mw=self.mainwindow 0682 mw.MakeCall( gui.Request(mw.wt.singledirlisting, path), 0683 gui.Callback(self.OnDirListingResults, path) ) 0684 0685 def OnDirListingResults(self, path, exception, result): 0686 mw=self.mainwindow 0687 if mw.HandleException(exception): return 0688 item=self.pathtoitem(path) 0689 l=[] 0690 child,cookie=self.GetFirstChild(item) 0691 for dummy in range(0,self.GetChildrenCount(item,False)): 0692 l.append(child) 0693 child,cookie=self.GetNextChild(item,cookie) 0694 # we now have a list of children in l 0695 sort=False 0696 for file in result: 0697 children=True 0698 f=guihelper.basename(file) 0699 found=None 0700 for i in l: 0701 if self.GetItemText(i)==f: 0702 found=i 0703 break 0704 if found is None: 0705 # this only happens if the phone has added the directory 0706 # after we got the initial file view, unusual but possible 0707 found=self.AddDirectory(item, f) 0708 self.OnDirListing(file) 0709 sort=True 0710 for i in l: # remove all children not present in result 0711 if not result.has_key(self.itemtopath(i)): 0712 self.RemoveDirectory(item, i) 0713 if sort: 0714 self.SortChildren(item) 0715 0716 def OnNewSubdir(self, _): 0717 with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, "Subdirectory name?", "Create Subdirectory", "newfolder"), 0718 True) as (dlg, retcode): 0719 if retcode==wx.ID_OK: 0720 item=self.GetSelection() 0721 parent=self.itemtopath(item) 0722 if len(parent): 0723 path=parent+"/"+dlg.GetValue() 0724 else: 0725 path=dlg.GetValue() 0726 mw=self.mainwindow 0727 mw.MakeCall( gui.Request(mw.wt.mkdir, path), 0728 gui.Callback(self.OnNewSubdirResults, path) ) 0729 0730 def OnNewSubdirResults(self, new_path, exception, _): 0731 mw=self.mainwindow 0732 if mw.HandleException(exception): return 0733 path, dir=os.path.split(new_path) 0734 item=self.pathtoitem(path) 0735 self.AddDirectory(item, dir) 0736 self.SortChildren(item) 0737 self.Expand(item) 0738 # requery the phone just incase 0739 self.OnDirListing(path) 0740 0741 def OnNewFile(self,_): 0742 parent=self.itemtopath(self.GetSelection()) 0743 with guihelper.WXDialogWrapper(wx.FileDialog(self, style=wx.OPEN|wx.HIDE_READONLY|wx.CHANGE_DIR), 0744 True) as (dlg, retcode): 0745 if retcode==wx.ID_OK: 0746 infile=dlg.GetPath() 0747 contents=open(infile, "rb").read() 0748 if len(parent): 0749 path=parent+"/"+os.path.basename(dlg.GetPath()) 0750 else: 0751 path=os.path.basename(dlg.GetPath()) # you can't create files in root but I won't stop you 0752 mw=self.mainwindow 0753 mw.MakeCall( gui.Request(mw.wt.writefile, path, contents), 0754 gui.Callback(self.OnNewFileResults, parent) ) 0755 0756 def OnNewFileResults(self, parentdir, exception, _): 0757 mw=self.mainwindow 0758 if mw.HandleException(exception): return 0759 self.parent.ShowFiles(parentdir, True) 0760 0761 def OnDirDelete(self, _): 0762 path=self.itemtopath(self.GetSelection()) 0763 mw=self.mainwindow 0764 mw.MakeCall( gui.Request(mw.wt.rmdirs, path), 0765 gui.Callback(self.OnDirDeleteResults, path) ) 0766 0767 def OnDirDeleteResults(self, path, exception, _): 0768 mw=self.mainwindow 0769 if mw.HandleException(exception): return 0770 # remove the directory from the view 0771 parent, dir=os.path.split(path) 0772 parent_item=self.pathtoitem(parent) 0773 del_item=self.pathtoitem(path) 0774 self.RemoveDirectory(parent_item, del_item) 0775 # requery the phone just incase 0776 self.OnDirListing(parent) 0777 0778 def OnBackupTree(self, _): 0779 self.OnBackup(recurse=100) 0780 0781 def OnBackupDirectory(self, _): 0782 self.OnBackup() 0783 0784 def OnBackup(self, recurse=0): 0785 path=self.itemtopath(self.GetSelection()) 0786 mw=self.mainwindow 0787 mw.MakeCall( gui.Request(mw.wt.getbackup, path, recurse), 0788 gui.Callback(self.OnBackupResults, path) ) 0789 0790 def OnBackupResults(self, path, exception, backup): 0791 mw=self.mainwindow 0792 if mw.HandleException(exception): return 0793 bn=guihelper.basename(path) 0794 if len(bn)<1: 0795 bn="root" 0796 bn+=".zip" 0797 ext="Zip files|*.zip|All Files|*" 0798 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Save File As", defaultFile=bn, wildcard=ext, 0799 style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR), 0800 True) as (dlg, retcode): 0801 if retcode==wx.ID_OK: 0802 file(dlg.GetPath(), "wb").write(backup) 0803 0804 def OnRestore(self, _): 0805 ext="Zip files|*.zip|All Files|*" 0806 path=self.itemtopath(self.GetSelection()) 0807 bn=guihelper.basename(path) 0808 if len(bn)<1: 0809 bn="root" 0810 bn+=".zip" 0811 ext="Zip files|*.zip|All Files|*" 0812 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Open backup file", defaultFile=bn, wildcard=ext, 0813 style=wx.OPEN|wx.HIDE_READONLY|wx.CHANGE_DIR), 0814 True) as (dlg, retcode): 0815 if retcode==wx.ID_OK: 0816 name=dlg.GetPath() 0817 if not zipfile.is_zipfile(name): 0818 with guihelper.WXDialogWrapper(guiwidgets.AlertDialogWithHelp(self.mainwindow, name+" is not a valid zipfile.", "Zip file required", 0819 lambda _: wx.GetApp().displayhelpid(helpids.ID_NOT_A_ZIPFILE), 0820 style=wx.OK|wx.ICON_ERROR), 0821 True): 0822 return 0823 zipf=zipfile.ZipFile(name, "r") 0824 xx=zipf.testzip() 0825 if xx is not None: 0826 with guihelper.WXDialogWrapper(guiwidgets.AlertDialogWithHelp(self.mainwindow, name+" has corrupted contents. Use a repair utility to fix it", 0827 "Zip file corrupted", 0828 lambda _: wx.GetApp().displayhelpid(helpids.ID_ZIPFILE_CORRUPTED), 0829 style=wx.OK|wx.ICON_ERROR), 0830 True): 0831 return 0832 0833 RestoreDialog(self.mainwindow, "Restore files", zipf, path, self.OnRestoreOK).Show(True) 0834 0835 def OnRestoreOK(self, zipf, names, parentdir): 0836 if len(names)==0: 0837 wx.MessageBox("You didn't select any files to restore!", "No files selected", 0838 wx.OK|wx.ICON_EXCLAMATION) 0839 return 0840 l=[] 0841 for zipname, fsname in names: 0842 l.append( (fsname, zipf.read(zipname)) ) 0843 0844 mw=self.mainwindow 0845 mw.MakeCall( gui.Request(mw.wt.restorefiles, l), 0846 gui.Callback(self.OnRestoreResults, parentdir) ) 0847 0848 def OnRestoreResults(self, parentdir, exception, results): 0849 mw=self.mainwindow 0850 if mw.HandleException(exception): return 0851 ok=filter(lambda s: s[0], results) 0852 fail=filter(lambda s: not s[0], results) 0853 0854 # re-read the filesystem (if anything was restored) 0855 if len(parentdir): 0856 dirs=[] 0857 for _, name in results: 0858 while(len(name)>len(parentdir)): 0859 name=guihelper.dirname(name) 0860 if name not in dirs: 0861 dirs.append(name) 0862 dirs.sort() 0863 for d in dirs: 0864 self.OnDirListing(d) 0865 0866 self.OnDirListing(parentdir) 0867 0868 if len(ok) and len(fail)==0: 0869 dlg=wx.MessageDialog(mw, "All files restored ok", "All files restored", 0870 wx.OK|wx.ICON_INFORMATION) 0871 dlg.Show(True) 0872 return 0873 if len(fail) and len(ok)==0: 0874 wx.MessageBox("All files failed to restore", "No files restored", 0875 wx.OK|wx.ICON_ERROR) 0876 return 0877 0878 op="Failed to restore some files. Check the log for reasons.:\n\n" 0879 for s,n in fail: 0880 op+=" "+n+"\n" 0881 wx.MessageBox(op, "Some restores failed", wx.OK|wx.ICON_ERROR) 0882 0883 def OnDirRefresh(self, _): 0884 path=self.itemtopath(self.GetSelection()) 0885 self.parent.ShowFiles(path, True) 0886 self.OnDirListing(path) 0887 0888 def OnRefresh(self, _): 0889 self.GetFullFS() 0890 0891 def itemtopath(self, item): 0892 if item==self.root: return "" 0893 res=self.GetItemText(item) 0894 while True: 0895 parent=self.GetItemParent(item) 0896 if parent==self.root: 0897 return res 0898 item=parent 0899 res=self.GetItemText(item)+"/"+res 0900 # can't get here, but pychecker doesn't seem to realise 0901 assert False 0902 return "" 0903 0904 def pathtoitem(self, path): 0905 if path=="": return self.root 0906 dirs=path.split('/') 0907 node=self.root 0908 for n in range(0, len(dirs)): 0909 foundnode=None 0910 child,cookie=self.GetFirstChild(node) 0911 for dummy in range(0, self.GetChildrenCount(node, False)): 0912 d=self.GetItemText(child) 0913 if d==dirs[n]: 0914 node=child 0915 foundnode=node 0916 break 0917 child,cookie=self.GetNextChild(node,cookie) 0918 if foundnode is not None: 0919 continue 0920 # make the node 0921 node=self.AppendItem(node, dirs[n]) 0922 self.SetPyData(node, None) 0923 return node 0924 0925 class RestoreDialog(wx.Dialog): 0926 """A dialog that lists all the files that will be restored""" 0927 0928 def __init__(self, parent, title, zipf, path, okcb): 0929 """Constructor 0930 0931 @param path: Placed before names in the archive. Should not include a 0932 trailing slash. 0933 """ 0934 wx.Dialog.__init__(self, parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 0935 vbs=wx.BoxSizer(wx.VERTICAL) 0936 vbs.Add( wx.StaticText(self, -1, "Choose files to restore"), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0937 0938 nl=zipf.namelist() 0939 nl.sort() 0940 0941 prefix=path 0942 if len(prefix)=="/" or prefix=="": 0943 prefix="" 0944 else: 0945 prefix+="/" 0946 0947 nnl=map(lambda i: prefix+i, nl) 0948 0949 self.clb=wx.CheckListBox(self, -1, choices=nnl, style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB, size=wx.Size(200,300)) 0950 0951 for i in range(len(nnl)): 0952 self.clb.Check(i, True) 0953 0954 vbs.Add( self.clb, 1, wx.EXPAND|wx.ALL, 5) 0955 0956 vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 0957 0958 vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTER|wx.ALL, 5) 0959 0960 self.SetSizer(vbs) 0961 self.SetAutoLayout(True) 0962 vbs.Fit(self) 0963 0964 wx.EVT_BUTTON(self, wx.ID_HELP, lambda _: wx.GetApp().displayhelpid(helpids.ID_RESTOREDIALOG)) 0965 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOK) 0966 self.okcb=okcb 0967 self.zipf=zipf 0968 self.nl=zip(nl, nnl) 0969 self.path=path 0970 0971 def OnOK(self, _): 0972 names=[] 0973 for i in range(len(self.nl)): 0974 if self.clb.IsChecked(i): 0975 names.append(self.nl[i]) 0976 self.okcb(self.zipf, names, self.path) 0977 self.Show(False) 0978 self.Destroy() 0979
Generated by PyXR 0.9.4