PyXR

c:\projects\bitpim\src \ filesystem.py



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