RE: Problem with UNICODE filenames still not fixed :( (part 3 of 3)
"Simon C" <[email protected]>
| Newsgroups | gmane.comp.mobile.bitpim.devel |
|---|---|
| Message-ID | <000801c5cc3b$5e75c380$6400a8c0@HOME> |
Part 3 of 3 Simon
guiwidgets.py
(text/plain, 79.3 KB)
#!/usr/bin/env python ### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### $Id: guiwidgets.py,v 1.268 2005/09/29 12:17:02 sawecw Exp $ """Most of the graphical user interface elements making up BitPim""" # standard modules import os import sys import time import copy import StringIO import getpass import sha,md5 import zlib import base64 import thread import Queue import shutil import time # wx. modules import wx import wx.html import wx.lib.mixins.listctrl import wx.lib.intctrl import wx.lib.newevent # my modules import common import helpids import comscan import usbscan import comdiagnose import analyser import guihelper import pubsub import bphtml import bitflingscan import aggregatedisplay import phone_media_codec import pubsub ### ### BitFling cert stuff ### BitFlingCertificateVerificationEvent, EVT_BITFLINGCERTIFICATEVERIFICATION = wx.lib.newevent.NewEvent() #### #### A simple text widget that does nice pretty logging. #### class LogWindow(wx.Panel): theanalyser=None def __init__(self, parent): wx.Panel.__init__(self,parent, -1) # have to use rich2 otherwise fixed width font isn't used on windows self.tb=wx.TextCtrl(self, 1, style=wx.TE_MULTILINE| wx.TE_RICH2|wx.TE_DONTWRAP|wx.TE_READONLY) f=wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL ) ta=wx.TextAttr(font=f) self.tb.SetDefaultStyle(ta) self.sizer=wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.tb, 1, wx.EXPAND) self.SetSizer(self.sizer) self.SetAutoLayout(True) self.sizer.Fit(self) wx.EVT_IDLE(self, self.OnIdle) self.outstandingtext=StringIO.StringIO() wx.EVT_KEY_UP(self.tb, self.OnKeyUp) def Clear(self): self.tb.Clear() def OnSelectAll(self, _): self.tb.SetSelection(-1, -1) def OnIdle(self,_): if self.outstandingtext.tell(): # this code is written to be re-entrant newt=self.outstandingtext.getvalue() self.outstandingtext.seek(0) self.outstandingtext.truncate() self.tb.AppendText(newt) def log(self, str, nl=True): now=time.time() t=time.localtime(now) self.outstandingtext.write("%d:%02d:%02d.%03d " % ( t[3], t[4], t[5], int((now-int(now))*1000))) self.outstandingtext.write(str) if nl: self.outstandingtext.write("\n") def logdata(self, str, data, klass=None): o=self.outstandingtext self.log(str, nl=False) if data is not None: o.write(" Data - "+`len(data)`+" bytes\n") if klass is not None: try: o.write("<#! %s.%s !#>\n" % (klass.__module__, klass.__name__)) except: klass=klass.__class__ o.write("<#! %s.%s !#>\n" % (klass.__module__, klass.__name__)) o.write(common.datatohexstring(data)) o.write("\n") def OnKeyUp(self, evt): keycode=evt.GetKeyCode() if keycode==ord('P') and evt.ControlDown() and evt.AltDown(): # analyse what was selected data=self.tb.GetStringSelection() # or the whole buffer if it was nothing if data is None or len(data)==0: data=self.tb.GetValue() try: self.theanalyser.Show() except: self.theanalyser=None if self.theanalyser is None: self.theanalyser=analyser.Analyser(data=data) self.theanalyser.Show() self.theanalyser.newdata(data) evt.Skip() ### ### Dialog asking what you want to sync ### class GetPhoneDialog(wx.Dialog): # sync sources ("Pretty Name", "name used to query profile") sources= ( ('PhoneBook', 'phonebook'), ('Calendar', 'calendar'), ('Wallpaper', 'wallpaper'), ('Ringtone', 'ringtone'), ('Memo', 'memo'), ('Todo', 'todo'), ('SMS', 'sms'), ('Call History', 'call_history')) # actions ("Pretty Name", "name used to query profile") actions = ( ("Get", "read"), ) NOTREQUESTED=0 MERGE=1 OVERWRITE=2 # type of action ("pretty name", "name used to query profile") types= ( ("Add", MERGE), ("Replace All", OVERWRITE)) HELPID=helpids.ID_GET_PHONE_DATA def __init__(self, frame, title, id=-1): wx.Dialog.__init__(self, frame, id, title, style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE) gs=wx.FlexGridSizer(2+len(self.sources), 1+len(self.types),5 ,10) gs.AddGrowableCol(1) gs.AddMany( [ (wx.StaticText(self, -1, "Source"), 0, wx.EXPAND),]) for pretty,_ in self.types: gs.Add(wx.StaticText(self, -1, pretty), 0, wx.ALIGN_CENTRE) self.cb=[] self.rb=[] for desc, source in self.sources: self.cb.append(wx.CheckBox(self, wx.NewId(), desc)) wx.EVT_CHECKBOX(self, self.cb[-1].GetId(), self.DoOkStatus) gs.Add(self.cb[-1], 0, wx.EXPAND) first=True for tdesc,tval in self.types: if first: style=wx.RB_GROUP first=0 else: style=0 self.rb.append( wx.RadioButton(self, -1, "", style=style) ) if not self._dowesupport(source, self.actions[0][1], tval): self.rb[-1].Enable(False) self.rb[-1].SetValue(False) gs.Add(self.rb[-1], 0, wx.ALIGN_CENTRE) bs=wx.BoxSizer(wx.VERTICAL) bs.Add(gs, 0, wx.EXPAND|wx.ALL, 10) bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) but=self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP) bs.Add(but, 0, wx.EXPAND|wx.ALL, 10) self.SetSizer(bs) self.SetAutoLayout(True) bs.Fit(self) wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) def _setting(self, type): for index in range(len(self.sources)): if self.sources[index][1]==type: if not self.cb[index].GetValue(): print type,"not requested" return self.NOTREQUESTED for i in range(len(self.types)): if self.rb[index*len(self.types)+i].GetValue(): print type,self.types[i][1] return self.types[i][1] assert False, "No selection for "+type assert False, "No such type "+type def GetPhoneBookSetting(self): return self._setting("phonebook") def GetCalendarSetting(self): return self._setting("calendar") def GetWallpaperSetting(self): return self._setting("wallpaper") def GetRingtoneSetting(self): return self._setting("ringtone") def GetMemoSetting(self): return self._setting("memo") def GetTodoSetting(self): return self._setting("todo") def GetSMSSetting(self): return self._setting("sms") def GetCallHistorySetting(self): return self._setting("call_history") def OnHelp(self,_): wx.GetApp().displayhelpid(self.HELPID) # this is what BitPim itself supports - the phones may support a subset _notsupported=( # ('phonebook', 'read', MERGE), # sort of is ('calendar', 'read', MERGE), ('wallpaper', 'read', MERGE), ('ringtone', 'read', MERGE), ('memo', 'read', MERGE), ('todo', 'read', MERGE)) def _dowesupport(self, source, action, type): if (source,action,type) in self._notsupported: return False return True def UpdateWithProfile(self, profile): for cs in range(len(self.sources)): source=self.sources[cs][1] # we disable the checkbox self.cb[cs].Enable(False) # are any radio buttons enabled count=0 for i in range(len(self.types)): assert len(self.types)==2 if self.types[i][1]==self.MERGE: type="MERGE" elif self.types[i][1]==self.OVERWRITE: type="OVERWRITE" else: assert False continue if self._dowesupport(source, self.actions[0][1], self.types[i][1]) and \ profile.SyncQuery(source, self.actions[0][1], type): self.cb[cs].Enable(True) self.rb[cs*len(self.types)+i].Enable(True) if self.rb[cs*len(self.types)+i].GetValue(): count+=1 else: self.rb[cs*len(self.types)+i].Enable(False) self.rb[cs*len(self.types)+i].SetValue(False) if not self.cb[cs].IsEnabled(): # ensure checkbox is unchecked if not enabled self.cb[cs].SetValue(False) else: # ensure one radio button is checked if count!=1: done=False for i in range(len(self.types)): index=cs*len(self.types)+i if self.rb[index].IsEnabled(): self.rb[index].SetValue(not done) done=False def ShowModal(self): # we ensure the OK button is in the correct state self.DoOkStatus() return wx.Dialog.ShowModal(self) def DoOkStatus(self, evt=None): # ensure the OK button is in the right state enable=False for i in self.cb: if i.GetValue(): enable=True break self.FindWindowById(wx.ID_OK).Enable(enable) if evt is not None: evt.Skip() class SendPhoneDialog(GetPhoneDialog): HELPID=helpids.ID_SEND_PHONE_DATA # actions ("Pretty Name", "name used to query profile") actions = ( ("Send", "write"), ) def __init__(self, frame, title, id=-1): GetPhoneDialog.__init__(self, frame, title, id) # this is what BitPim itself doesn't supports - the phones may support less _notsupported=( ('call_history', 'write', None),) ### ### The master config dialog ### class ConfigDialog(wx.Dialog): phonemodels={ 'LG-VX3200': 'com_lgvx3200', 'LG-VX4400': 'com_lgvx4400', 'LG-VX4500': 'com_lgvx4500', 'LG-VX4600 (Telus Mobility)': 'com_lgvx4600', 'LG-VX4650 (Verizon Wireless)': 'com_lgvx4650', 'LG-VX6000': 'com_lgvx6000', 'LG-VX6100': 'com_lgvx6100', 'LG-VX7000': 'com_lgvx7000', 'LG-VX8000 (Verizon Wireless)': 'com_lgvx8000', 'LG-VX8100 (Verizon Wireless)': 'com_lgvx8100', 'LG-TM520': 'com_lgtm520', 'LG-VX10': 'com_lgtm520', 'MM-7400': 'com_sanyo7400', 'PM-8200': 'com_sanyo8200', 'RL-4920': 'com_sanyo4920', 'SCP-4900': 'com_sanyo4900', 'SCP-5300': 'com_sanyo5300', 'SCP-5400': 'com_sanyo5400', 'SCP-5500': 'com_sanyo5500', 'SCP-7200': 'com_sanyo7200', 'SCP-7300': 'com_sanyo7300', 'SCP-8100': 'com_sanyo8100', 'SCP-8100 (Bell Mobility)': 'com_sanyo8100_bell', 'SCH-A310': 'com_samsungscha310', 'SPH-A460': 'com_samsungspha460', 'SPH-A620 (VGA1000)': 'com_samsungspha620', 'SPH-N200': 'com_samsungsphn200', 'SCH-A650': 'com_samsungscha650', 'SCH-A670': 'com_samsungscha670', 'SK6100 (Pelephone)' : 'com_sk6100', 'Other CDMA phone': 'com_othercdma', } if __debug__: phonemodels.update( {'Audiovox CDM-8900': 'com_audiovoxcdm8900', # phone is too fragile for normal use 'LG-PM325 (Sprint)': 'com_lgpm325', }) update_choices=('Never', 'Daily', 'Weekly', 'Monthly') setme="<setme>" ID_DIRBROWSE=wx.NewId() ID_COMBROWSE=wx.NewId() ID_RETRY=wx.NewId() ID_BITFLING=wx.NewId() def __init__(self, mainwindow, frame, title="BitPim Settings", id=-1): wx.Dialog.__init__(self, frame, id, title, style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE) self.mw=mainwindow self.bitflingresponsequeues={} gs=wx.GridBagSizer(10, 10) gs.AddGrowableCol(1) # safemode gs.Add( wx.StaticText(self, -1, "Read Only"), pos=(0,0), flag=wx.ALIGN_CENTER_VERTICAL) self.safemode=wx.CheckBox(self, wx.NewId(), "Block writing anything to the phone") gs.Add( self.safemode, pos=(0,1), flag=wx.ALIGN_CENTER_VERTICAL) # where we store our files gs.Add( wx.StaticText(self, -1, "Disk storage"), pos=(1,0), flag=wx.ALIGN_CENTER_VERTICAL) self.diskbox=wx.TextCtrl(self, -1, self.setme, size=(400,-1)) gs.Add( self.diskbox, pos=(1,1), flag=wx.ALIGN_CENTER_VERTICAL) gs.Add( wx.Button(self, self.ID_DIRBROWSE, "Browse ..."), pos=(1,2), flag=wx.ALIGN_CENTER_VERTICAL) # phone type gs.Add( wx.StaticText(self, -1, "Phone Type"), pos=(2,0), flag=wx.ALIGN_CENTER_VERTICAL) keys=self.phonemodels.keys() keys.sort() self.phonebox=wx.ComboBox(self, -1, "LG-VX4400", style=wx.CB_DROPDOWN|wx.CB_READONLY,choices=keys) self.phonebox.SetValue("LG-VX4400") gs.Add( self.phonebox, pos=(2,1), flag=wx.ALIGN_CENTER_VERTICAL) # com port gs.Add( wx.StaticText(self, -1, "Com Port"), pos=(3,0), flag=wx.ALIGN_CENTER_VERTICAL) self.commbox=wx.TextCtrl(self, -1, self.setme, size=(200,-1)) gs.Add( self.commbox, pos=(3,1), flag=wx.ALIGN_CENTER_VERTICAL) gs.Add( wx.Button(self, self.ID_COMBROWSE, "Browse ..."), pos=(3,2), flag=wx.ALIGN_CENTER_VERTICAL) # Automatic check for update gs.Add(wx.StaticText(self, -1, 'Check for Update'), pos=(4,0), flag=wx.ALIGN_CENTER_VERTICAL) self.updatebox=wx.ComboBox(self, -1, self.update_choices[0], style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=self.update_choices) gs.Add(self.updatebox, pos=(4,1), flag=wx.ALIGN_CENTER_VERTICAL) # always start with the 'Today' tab gs.Add(wx.StaticText(self, -1, 'Startup'), pos=(5,0), flag=wx.ALIGN_CENTER_VERTICAL) self.startup=wx.CheckBox(self, wx.NewId(), 'Always start with the Today tab') gs.Add(self.startup, pos=(5,1), flag=wx.ALIGN_CENTER_VERTICAL) # bitfling if bitflingscan.IsBitFlingEnabled(): self.SetupBitFlingCertVerification() gs.Add( wx.StaticText( self, -1, "BitFling"), pos=(6,0), flag=wx.ALIGN_CENTER_VERTICAL) self.bitflingenabled=wx.CheckBox(self, self.ID_BITFLING, "Enabled") gs.Add(self.bitflingenabled, pos=(6,1), flag=wx.ALIGN_CENTER_VERTICAL) gs.Add( wx.Button(self, self.ID_BITFLING, "Settings ..."), pos=(6,2), flag=wx.ALIGN_CENTER_VERTICAL) wx.EVT_BUTTON(self, self.ID_BITFLING, self.OnBitFlingSettings) wx.EVT_CHECKBOX(self, self.ID_BITFLING, self.ApplyBitFlingSettings) if self.mw.config.Read("bitfling/password","<unconfigured>") \ == "<unconfigured>": self.mw.config.WriteInt("bitfling/enabled", 0) self.bitflingenabled.SetValue(False) self.bitflingenabled.Enable(False) else: self.bitflingenabled=None # crud at the bottom bs=wx.BoxSizer(wx.VERTICAL) bs.Add(gs, 0, wx.EXPAND|wx.ALL, 10) bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) but=self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP) bs.Add(but, 0, wx.CENTER|wx.ALL, 10) wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) wx.EVT_BUTTON(self, self.ID_DIRBROWSE, self.OnDirBrowse) wx.EVT_BUTTON(self, self.ID_COMBROWSE, self.OnComBrowse) wx.EVT_BUTTON(self, wx.ID_OK, self.OnOK) self.setdefaults() self.SetSizer(bs) self.SetAutoLayout(True) bs.Fit(self) # Retrieve saved settings... (we only care about position) set_size("ConfigDialog", self, screenpct=-1, aspect=3.5) wx.EVT_CLOSE(self, self.OnClose) def OnCancel(self, _): self.saveSize() def OnOK(self, _): self.saveSize() # validate directory dir=self.diskbox.GetValue() try: os.makedirs(dir) except: pass if os.path.isdir(dir): self.EndModal(wx.ID_OK) self.ApplyBitFlingSettings() return wx.TipWindow(self.diskbox, "No such directory - please correct") def OnHelp(self, _): wx.GetApp().displayhelpid(helpids.ID_SETTINGS_DIALOG) def OnDirBrowse(self, _): dlg=wx.DirDialog(self, defaultPath=self.diskbox.GetValue(), style=wx.DD_NEW_DIR_BUTTON) res=dlg.ShowModal() v=dlg.GetPath() dlg.Destroy() if res==wx.ID_OK: self.diskbox.SetValue(v) def OnComBrowse(self, _): self.saveSize() if self.mw.wt is not None: self.mw.wt.clearcomm() # remember its size # w=self.mw.config.ReadInt("combrowsewidth", 640) # h=self.mw.config.ReadInt("combrowseheight", 480) p=self.mw.config.ReadInt("combrowsesash", 200) dlg=CommPortDialog(self, __import__(self.phonemodels[self.phonebox.GetValue()]), defaultport=self.commbox.GetValue(), sashposition=p) # dlg.SetSize(wx.Size(w,h)) # dlg.Centre() res=dlg.ShowModal() v=dlg.GetPort() # sz=dlg.GetSize() # self.mw.config.WriteInt("combrowsewidth", sz.GetWidth()) # self.mw.config.WriteInt("combrowseheight", sz.GetHeight()) self.mw.config.WriteInt("combrowsesash", dlg.sashposition) dlg.Destroy() if res==wx.ID_OK: self.commbox.SetValue(v) def ApplyBitFlingSettings(self, _=None): if self.bitflingenabled is not None: if self.bitflingenabled.GetValue(): bitflingscan.flinger.configure(self.mw.config.Read("bitfling/username", "<unconfigured>"), bitflingscan.decode(self.mw.config.Read("bitfling/password", "<unconfigured>")), self.mw.config.Read("bitfling/host", "<unconfigured>"), self.mw.config.ReadInt("bitfling/port", 12652)) else: bitflingscan.flinger.unconfigure() def OnBitFlingSettings(self, _): dlg=BitFlingSettingsDialog(None, self.mw.config) if dlg.ShowModal()==wx.ID_OK: dlg.SaveSettings() dlg.Destroy() self.ApplyBitFlingSettings() if self.mw.config.Read("bitfling/password","<unconfigured>") \ != "<unconfigured>": self.bitflingenabled.Enable(True) def SetupBitFlingCertVerification(self): "Setup all the voodoo needed for certificate verification to happen, not matter which thread wants it" EVT_BITFLINGCERTIFICATEVERIFICATION(self, self._wrapVerifyBitFlingCert) bitflingscan.flinger.SetCertVerifier(self.dispatchVerifyBitFlingCert) bitflingscan.flinger.setthreadeventloop(wx.SafeYield) def dispatchVerifyBitFlingCert(self, addr, key): """Handle a certificate verification from any thread The request is handed to the main gui thread, and then we wait for the results""" print thread.get_ident(),"dispatchVerifyBitFlingCert called" q=self.bitflingresponsequeues.get(thread.get_ident(), None) if q is None: q=Queue.Queue() self.bitflingresponsequeues[thread.get_ident()]=q print thread.get_ident(), "Posting BitFlingCertificateVerificationEvent" wx.PostEvent(self, BitFlingCertificateVerificationEvent(addr=addr, key=key, q=q)) print thread.get_ident(), "After posting BitFlingCertificateVerificationEvent, waiting for response" res, exc = q.get() print thread.get_ident(), "Got response", res, exc if exc is not None: ex=exc[1] ex.gui_exc_info=exc[2] raise ex return res def _wrapVerifyBitFlingCert(self, evt): """Receive the event in the main gui thread for cert verification We unpack the parameters, call the verification method""" print "_wrapVerifyBitFlingCert" addr, hostkey, q = evt.addr, evt.key, evt.q self.VerifyBitFlingCert(addr, hostkey, q) def VerifyBitFlingCert(self, addr, key, q): print "VerifyBitFlingCert for", addr, "type",key.get_name() # ::TODO:: reject if not dsa # get fingerprint fingerprint=common.hexify(key.get_fingerprint()) # do we already know about it? existing=wx.GetApp().config.Read("bitfling/certificates/%s" % (addr[0],), "") if len(existing): fp=existing if fp==fingerprint: q.put( (True, None) ) return # throw up the dialog print "asking user" dlg=AcceptCertificateDialog(None, wx.GetApp().config, addr, fingerprint, q) dlg.ShowModal() def OnClose(self, evt): self.saveSize() # Don't destroy the dialong, just put it away... self.EndModal(wx.ID_CANCEL) def setfromconfig(self): if len(self.mw.config.Read("path", "")): self.diskbox.SetValue(self.mw.config.Read("path", "")) if len(self.mw.config.Read("lgvx4400port")): self.commbox.SetValue(self.mw.config.Read("lgvx4400port", "")) if self.mw.config.Read("phonetype", "") in self.phonemodels: self.phonebox.SetValue(self.mw.config.Read("phonetype")) if self.bitflingenabled is not None: self.bitflingenabled.SetValue(self.mw.config.ReadInt("bitfling/enabled", 0)) self.ApplyBitFlingSettings() self.safemode.SetValue(self.mw.config.ReadInt("Safemode", 0)) self.updatebox.SetValue(self.mw.config.Read("updaterate", self.update_choices[0])) self.startup.SetValue(self.mw.config.ReadInt("startwithtoday", 0)) def setdefaults(self): if self.diskbox.GetValue()==self.setme: if guihelper.IsMSWindows(): # we want subdir of my documents on windows # nice and painful from win32com.shell import shell, shellcon path=shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0) path=os.path.join(str(path), "bitpim") else: path=os.path.expanduser("~/.bitpim-files") self.diskbox.SetValue(path) if self.commbox.GetValue()==self.setme: comm="auto" self.commbox.SetValue(comm) def updatevariables(self): path=self.diskbox.GetValue() self.mw.configpath=path self.mw.ringerpath=self._fixup(os.path.join(path, "ringer")) self.mw.wallpaperpath=self._fixup(os.path.join(path, "wallpaper")) self.mw.phonebookpath=self._fixup(os.path.join(path, "phonebook")) self.mw.calendarpath=self._fixup(os.path.join(path, "calendar")) oldpath=self.mw.config.Read("path", "") self.mw.config.Write("path", path) self.mw.commportsetting=str(self.commbox.GetValue()) self.mw.config.Write("lgvx4400port", self.mw.commportsetting) if self.mw.wt is not None: self.mw.wt.clearcomm() # comm parameters (retry, timeouts, flow control etc) commparm={} commparm['retryontimeout']=self.mw.config.ReadInt("commretryontimeout", False) commparm['timeout']=self.mw.config.ReadInt('commtimeout', 3) commparm['hardwareflow']=self.mw.config.ReadInt('commhardwareflow', False) commparm['softwareflow']=self.mw.config.ReadInt('commsoftwareflow', False) commparm['baud']=self.mw.config.ReadInt('commbaud', 115200) self.mw.commparams=commparm # phone model self.mw.config.Write("phonetype", self.phonebox.GetValue()) self.mw.phonemodule=__import__(self.phonemodels[self.phonebox.GetValue()]) self.mw.phoneprofile=self.mw.phonemodule.Profile() pubsub.publish(pubsub.PHONE_MODEL_CHANGED, self.mw.phonemodule) # bitfling if self.bitflingenabled is not None: self.mw.bitflingenabled=self.bitflingenabled.GetValue() self.mw.config.WriteInt("bitfling/enabled", self.mw.bitflingenabled) # safemode - make sure you have to restart to disable self.mw.config.WriteInt("SafeMode", self.safemode.GetValue()) if self.safemode.GetValue(): wx.GetApp().SAFEMODE=True wx.GetApp().ApplySafeMode() # check for update rate self.mw.config.Write('updaterate', self.updatebox.GetValue()) # startup option self.mw.config.WriteInt('startwithtoday', self.startup.GetValue()) # ensure config is saved self.mw.config.Flush() self.mw.EnsureDatabase(path, oldpath) # update the status bar self.mw.SetPhoneModelStatus() # update the cache path self.mw.update_cache_path() def _fixup(self, path): # os.path.join screws up adding root directory of a drive to # a directory. eg join("c:\", "foo") gives "c:\\foo" whch # is invalid. This function fixes that if len(path)>=3: if path[1]==':' and path[2]=='\\' and path[3]=='\\': return path[0:2]+path[3:] return path def needconfig(self): # Set base config self.setfromconfig() # do we know the phone? if self.mw.config.Read("phonetype", "") not in self.phonemodels: return True # are any at unknown settings if self.diskbox.GetValue()==self.setme or \ self.commbox.GetValue()==self.setme: # fill in and set defaults self.setdefaults() self.updatevariables() # any still unset? if self.diskbox.GetValue()==self.setme or \ self.commbox.GetValue()==self.setme: return True # does data directory exist? try: os.makedirs(self.diskbox.GetValue()) except: pass if not os.path.isdir(self.diskbox.GetValue()): return True return False def ShowModal(self): self.setfromconfig() ec=wx.Dialog.ShowModal(self) if ec==wx.ID_OK: self.updatevariables() return ec def saveSize(self): save_size("ConfigDialog", self.GetRect()) ### ### The select a comm port dialog box ### class CommPortDialog(wx.Dialog): ID_LISTBOX=1 ID_TEXTBOX=2 ID_REFRESH=3 ID_SASH=4 ID_SAVE=5 def __init__(self, parent, selectedphone, id=-1, title="Choose a comm port", defaultport="auto", sashposition=0): wx.Dialog.__init__(self, parent, id, title, style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) self.parent=parent self.port=defaultport self.sashposition=sashposition self.selectedphone=selectedphone p=self # parent widget # the listbox and textbox in a splitter splitter=wx.SplitterWindow(p, self.ID_SASH, style=wx.SP_3D|wx.SP_LIVE_UPDATE) self.lb=wx.ListBox(splitter, self.ID_LISTBOX, style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB) self.tb=wx.html.HtmlWindow(splitter, self.ID_TEXTBOX, size=wx.Size(400,400)) # default style is auto scrollbar splitter.SplitHorizontally(self.lb, self.tb, sashposition) # the buttons buttsizer=wx.GridSizer(1, 5) buttsizer.Add(wx.Button(p, wx.ID_OK, "OK"), 0, wx.ALL, 10) buttsizer.Add(wx.Button(p, self.ID_REFRESH, "Refresh"), 0, wx.ALL, 10) buttsizer.Add(wx.Button(p, self.ID_SAVE, "Save..."), 0, wx.ALL, 10) buttsizer.Add(wx.Button(p, wx.ID_HELP, "Help"), 0, wx.ALL, 10) buttsizer.Add(wx.Button(p, wx.ID_CANCEL, "Cancel"), 0, wx.ALL, 10) # vertical join of the two vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(splitter, 1, wx.EXPAND) vbs.Add(buttsizer, 0, wx.CENTER) # hook into self p.SetSizer(vbs) p.SetAutoLayout(True) vbs.Fit(p) # update dialog wx.CallAfter(self.OnRefresh) # hook in all the widgets wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel) wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) wx.EVT_BUTTON(self, self.ID_REFRESH, self.OnRefresh) wx.EVT_BUTTON(self, self.ID_SAVE, self.OnSave) wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) wx.EVT_LISTBOX(self, self.ID_LISTBOX, self.OnListBox) wx.EVT_LISTBOX_DCLICK(self, self.ID_LISTBOX, self.OnListBox) wx.EVT_SPLITTER_SASH_POS_CHANGED(self, self.ID_SASH, self.OnSashChange) # Retrieve saved settings... Use 40% of screen if not specified set_size("CommDialog", self, screenpct=60) wx.EVT_CLOSE(self, self.OnClose) def OnSashChange(self, _=None): self.sashposition=self.FindWindowById(self.ID_SASH).GetSashPosition() def OnRefresh(self, _=None): self.tb.SetPage("<p><b>Refreshing</b> ...") self.lb.Clear() self.Update() ports=comscan.comscan()+usbscan.usbscan() if bitflingscan.IsBitFlingEnabled(): ports=ports+bitflingscan.flinger.scan() self.portinfo=comdiagnose.diagnose(ports, self.selectedphone) if len(self.portinfo): self.portinfo=[ ("Automatic", "auto", "<p>BitPim will try to detect the correct port automatically when accessing your phone" ) ]+\ self.portinfo self.lb.Clear() sel=-1 for name, actual, description in self.portinfo: if sel<0 and self.GetPort()==actual: sel=self.lb.GetCount() self.lb.Append(name) if sel<0: sel=0 if self.lb.GetCount(): self.lb.SetSelection(sel) self.OnListBox() else: self.FindWindowById(wx.ID_OK).Enable(False) self.tb.SetPage("<html><body>You do not have any com/serial ports on your system</body></html>") def OnListBox(self, _=None): # enable/disable ok button p=self.portinfo[self.lb.GetSelection()] if p[1] is None: self.FindWindowById(wx.ID_OK).Enable(False) else: self.port=p[1] self.FindWindowById(wx.ID_OK).Enable(True) self.tb.SetPage(p[2]) def OnSave(self, _): html=StringIO.StringIO() print >>html, "<html><head><title>BitPim port listing - %s</title></head>" % (time.ctime(), ) print >>html, "<body><h1>BitPim port listing - %s</h1><table>" % (time.ctime(),) for long,actual,desc in self.portinfo: if actual is None or actual=="auto": continue print >>html, '<tr bgcolor="#77ff77"><td colspan=2>%s</td><td>%s</td></tr>' % (long,actual) print >>html, "<tr><td colspan=3>%s</td></tr>" % (desc,) print >>html, "<tr><td colspan=3><hr></td></tr>" print >>html, "</table></body></html>" dlg=wx.FileDialog(self, "Save port details as", defaultFile="bitpim-ports.html", wildcard="HTML files (*.html)|*.html", style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR) if dlg.ShowModal()==wx.ID_OK: open(dlg.GetPath(), "wt").write(html.getvalue()) dlg.Destroy() def OnCancel(self, _): self.saveSize() self.EndModal(wx.ID_CANCEL) def OnOk(self, _): self.saveSize() self.EndModal(wx.ID_OK) def OnHelp(self, _): wx.GetApp().displayhelpid(helpids.ID_COMMSETTINGS_DIALOG) def OnClose(self, evt): self.saveSize() # Don't destroy the dialong, just put it away... self.EndModal(wx.ID_CANCEL) def GetPort(self): return self.port def saveSize(self): save_size("CommDialog", self.GetRect()) ### ### Accept certificate dialog ### class AcceptCertificateDialog(wx.Dialog): def __init__(self, parent, config, addr, fingerprint, q): parent=self.FindAGoodParent(parent) wx.Dialog.__init__(self, parent, -1, "Accept certificate?", style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) self.config=config self.q=q self.addr=addr self.fingerprint=fingerprint hbs=wx.BoxSizer(wx.HORIZONTAL) hbs.Add(wx.StaticText(self, -1, "Host:"), 0, wx.ALL, 5) hbs.Add(wx.StaticText(self, -1, addr[0]), 0, wx.ALL, 5) hbs.Add(wx.StaticText(self, -1, " Fingerprint:"), 0, wx.ALL, 5) hbs.Add(wx.StaticText(self, -1, fingerprint), 1, wx.ALL, 5) vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) but=self.CreateButtonSizer(wx.YES|wx.NO|wx.HELP) vbs.Add(but, 0, wx.ALIGN_CENTER|wx.ALL, 10) self.SetSizer(vbs) vbs.Fit(self) wx.EVT_BUTTON(self, wx.ID_YES, self.OnYes) wx.EVT_BUTTON(self, wx.ID_NO, self.OnNo) wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnNo) def OnYes(self, _): wx.GetApp().config.Write("bitfling/certificates/%s" % (self.addr[0],), self.fingerprint) wx.GetApp().config.Flush() if self.IsModal(): self.EndModal(wx.ID_YES) else: self.Show(False) wx.CallAfter(self.Destroy) print "returning true from AcceptCertificateDialog" self.q.put( (True, None) ) def OnNo(self, _): if self.IsModal(): self.EndModal(wx.ID_NO) else: self.Show(False) wx.CallAfter(self.Destroy) print "returning false from AcceptCertificateDialog" self.q.put( (False, None) ) def FindAGoodParent(self, suggestion): win=wx.Window_FindFocus() while win is not None: try: if win.IsModal(): print "FindAGoodParent is",win return win except AttributeError: parent=win.GetParent() win=parent return suggestion ### ### BitFling settings dialog ### class BitFlingSettingsDialog(wx.Dialog): ID_USERNAME=wx.NewId() ID_PASSWORD=wx.NewId() ID_HOST=wx.NewId() ID_PORT=wx.NewId() ID_TEST=wx.NewId() passwordsentinel="@+_-3@<," def __init__(self, parent, config): wx.Dialog.__init__(self, parent, -1, "Edit BitFling settings", style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) self.config=config gs=wx.FlexGridSizer(1, 2, 5, 5) gs.AddGrowableCol(1) gs.AddMany([ (wx.StaticText(self, -1, "Username"), 0, wx.ALIGN_CENTER_VERTICAL), (wx.TextCtrl(self, self.ID_USERNAME), 1, wx.EXPAND), (wx.StaticText(self, -1, "Password"), 0, wx.ALIGN_CENTER_VERTICAL), (wx.TextCtrl(self, self.ID_PASSWORD, style=wx.TE_PASSWORD), 1, wx.EXPAND), (wx.StaticText(self, -1, "Host"), 0, wx.ALIGN_CENTER_VERTICAL), (wx.TextCtrl(self, self.ID_HOST), 1, wx.EXPAND), (wx.StaticText(self, -1, "Port"), 0, wx.ALIGN_CENTER_VERTICAL), (wx.lib.intctrl.IntCtrl(self, self.ID_PORT, value=12652, min=1, max=65535), 0) ]) vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(gs, 0, wx.EXPAND|wx.ALL, 5) vbs.Add((1,1), 1, wx.EXPAND) vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 10) gs=wx.GridSizer(1,4, 5,5) gs.Add(wx.Button(self, wx.ID_OK, "OK")) gs.Add(wx.Button(self, self.ID_TEST, "Test")) gs.Add(wx.Button(self, wx.ID_HELP, "Help")) gs.Add(wx.Button(self, wx.ID_CANCEL, "Cancel")) vbs.Add(gs, 0, wx.ALIGN_CENTER|wx.ALL, 10) self.SetSizer(vbs) vbs.Fit(self) set_size("BitFlingConfigDialog", self, -20, 0.5) # event handlers wx.EVT_BUTTON(self, self.ID_TEST, self.OnTest) # fill in data self.FindWindowById(self.ID_USERNAME).SetValue(config.Read("bitfling/username", getpass.getuser())) if len(config.Read("bitfling/password", "")): self.FindWindowById(self.ID_PASSWORD).SetValue(self.passwordsentinel) self.FindWindowById(self.ID_HOST).SetValue(config.Read("bitfling/host", "")) self.FindWindowById(self.ID_PORT).SetValue(config.ReadInt("bitfling/port", 12652)) def ShowModal(self): res=wx.Dialog.ShowModal(self) save_size("BitFlingConfigDialog", self.GetRect()) return res def GetSettings(self): username=self.FindWindowById(self.ID_USERNAME).GetValue() pwd=self.FindWindowById(self.ID_PASSWORD).GetValue() if pwd==self.passwordsentinel: pwd=bitflingscan.decode(self.config.Read("bitfling/password", self.passwordsentinel)) host=self.FindWindowById(self.ID_HOST).GetValue() port=self.FindWindowById(self.ID_PORT).GetValue() return username, pwd, host, port def SaveSettings(self): "Copy settings from dialog fields into config object" username,pwd,host,port=self.GetSettings() self.config.Write("bitfling/username", username) self.config.Write("bitfling/password", bitflingscan.encode(pwd)) self.config.Write("bitfling/host", host) self.config.WriteInt("bitfling/port", port) def OnTest(self, _): wx.CallAfter(self._OnTest) def _OnTest(self, _=None): try: bitflingscan.flinger.configure(*self.GetSettings()) res=bitflingscan.flinger.getversion() dlg=wx.MessageDialog(self, "Succeeded. Remote version is %s" % (res,) , "Success", wx.OK|wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() except Exception,ex: res="Failed: %s: %s" % sys.exc_info()[:2] if hasattr(ex, "gui_exc_info"): print common.formatexception( ex.gui_exc_info) else: print common.formatexception() dlg=wx.MessageDialog(self, res, "Failed", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() ### ### File viewer ### media_codec=phone_media_codec.codec_name class MyFileDropTarget(wx.FileDropTarget): def __init__(self, target): wx.FileDropTarget.__init__(self) self.target=target def OnDropFiles(self, x, y, filenames): return self.target.OnDropFiles(x,y,filenames) class FileView(wx.Panel): # Various DC objects used for drawing the items. We have to calculate them in the constructor as # the app object hasn't been constructed when this file is imported. item_selection_brush=None item_selection_pen=None item_line_font=None item_term="..." item_guardspace=None # Files we should ignore skiplist= ( 'desktop.ini', 'thumbs.db', 'zbthumbnail.info' ) # how much data do we want in call to getdata NONE=0 SELECTED=1 ALL=2 # maximum length of a filename maxlen=-1 # set via phone profile # acceptable characters in a filename filenamechars=None # set via phone profile def __init__(self, mainwindow, parent, watermark=None): wx.Panel.__init__(self,parent,style=wx.CLIP_CHILDREN) if not hasattr(self, "organizemenu"): self.organizemenu=None # item attributes if self.item_selection_brush is None: self.item_selection_brush=wx.TheBrushList.FindOrCreateBrush("MEDIUMPURPLE2", wx.SOLID) self.item_selection_pen=wx.ThePenList.FindOrCreatePen("MEDIUMPURPLE2", 1, wx.SOLID) f1=wx.TheFontList.FindOrCreateFont(10, wx.SWISS, wx.NORMAL, wx.BOLD) f2=wx.TheFontList.FindOrCreateFont(10, wx.SWISS, wx.NORMAL, wx.NORMAL) self.item_line_font=[f1, f2, f2, f2] dc=wx.MemoryDC() dc.SelectObject(wx.EmptyBitmap(100,100)) self.item_guardspace=dc.GetTextExtent(self.item_term)[0] del dc # no redraw ickiness # wx.EVT_ERASE_BACKGROUND(self, lambda evt: None) self.mainwindow=mainwindow self.thedir=None self.wildcard="I forgot to set wildcard in derived class|*" self.__dragging=False # use the aggregatedisplay to do the actual item display self.aggdisp=aggregatedisplay.Display(self, self, watermark) # we are our own datasource vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(self.aggdisp, 1, wx.EXPAND|wx.ALL, 2) self.SetSizer(vbs) timerid=wx.NewId() self.thetimer=wx.Timer(self, timerid) wx.EVT_TIMER(self, timerid, self.OnTooltipTimer) self.motionpos=None wx.EVT_MOUSE_EVENTS(self.aggdisp, self.OnMouseEvent) self.tipwindow=None if guihelper.IsMSWindows(): # turn on drag-and-drag for windows wx.EVT_MOTION(self.aggdisp, self.OnStartDrag) # Menus self.itemmenu=wx.Menu() self.itemmenu.Append(guihelper.ID_FV_OPEN, "Open") self.itemmenu.Append(guihelper.ID_FV_SAVE, "Save ...") self.itemmenu.AppendSeparator() if guihelper.IsMSWindows(): self.itemmenu.Append(guihelper.ID_FV_COPY, "Copy") self.itemmenu.Append(guihelper.ID_FV_DELETE, "Delete") self.itemmenu.Append(guihelper.ID_FV_RENAME, "Rename") self.itemmenu.AppendSeparator() # self.itemmenu.Append(guihelper.ID_FV_RENAME, "Rename") self.itemmenu.Append(guihelper.ID_FV_REFRESH, "Refresh") self.bgmenu=wx.Menu() if self.organizemenu is not None: self.bgmenu.AppendMenu(wx.NewId(), "Organize by", self.organizemenu) self.bgmenu.Append(guihelper.ID_FV_ADD, "Add ...") self.bgmenu.Append(guihelper.ID_FV_PASTE, "Paste") self.bgmenu.Append(guihelper.ID_FV_REFRESH, "Refresh") wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_OPEN, self.OnLaunch) wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_SAVE, self.OnSave) if guihelper.IsMSWindows(): wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_COPY, self.OnCopy) wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_DELETE, self.OnDelete) wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_RENAME, self.OnRename) wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_REFRESH, lambda evt: self.OnRefresh()) wx.EVT_MENU(self.bgmenu, guihelper.ID_FV_ADD, self.OnAdd) wx.EVT_MENU(self.bgmenu, guihelper.ID_FV_PASTE, self.OnPaste) wx.EVT_MENU(self.bgmenu, guihelper.ID_FV_REFRESH, lambda evt: self.OnRefresh) wx.EVT_RIGHT_UP(self.aggdisp, self.OnRightClick) aggregatedisplay.EVT_ACTIVATE(self.aggdisp, self.aggdisp.GetId(), self.OnLaunch) self.droptarget=MyFileDropTarget(self) self.SetDropTarget(self.droptarget) def OnRightClick(self, evt): """Popup the right click context menu @param widget: which widget to popup in @param position: position in widget @param onitem: True if the context menu is for an item """ if len(self.aggdisp.GetSelection()): menu=self.itemmenu item=self.GetSelectedItems()[0] menu.Enable(guihelper.ID_FV_RENAME, len(self.GetSelectedItems())==1) # we always launch on mac if not guihelper.IsMac(): menu.FindItemById(guihelper.ID_FV_OPEN).Enable(guihelper.GetOpenCommand(item.fileinfo.mimetypes, item.filename) is not None) else: menu=self.bgmenu menu.Enable(guihelper.ID_FV_PASTE, self.CanPaste()) if menu is None: return self.aggdisp.PopupMenu(menu, evt.GetPosition()) def OnLaunch(self, _): item=self.GetSelectedItems()[0] if guihelper.IsMac(): import findertools findertools.launch(item.filename) return cmd=guihelper.GetOpenCommand(item.fileinfo.mimetypes, item.filename) if cmd is None: wx.Bell() else: wx.Execute(cmd, wx.EXEC_ASYNC) if guihelper.IsMSWindows(): # drag-and-drop files only works in Windows def OnStartDrag(self, evt): evt.Skip() if not evt.LeftIsDown(): return items=self.GetSelectedItems() if not len(items): return drag_source=wx.DropSource(self) file_names=wx.FileDataObject() for item in items: file_names.AddFile(item.filename) drag_source.SetData(file_names) self.__dragging=True res=drag_source.DoDragDrop(wx.Drag_AllowMove) self.__dragging=False # check of any of the files have been removed, # can't trust result returned by DoDragDrop for item in items: # this used to use os.access function, but it does not # support unicode filenames if not os.path.isfile(item.filename): item.RemoveFromIndex() def OnMouseEvent(self, evt): self.motionpos=evt.GetPosition() evt.Skip() self.thetimer.Stop() if evt.AltDown() or evt.MetaDown() or evt.ControlDown() or evt.ShiftDown() or evt.Dragging() or evt.IsButton(): return self.thetimer.Start(1750, wx.TIMER_ONE_SHOT) def OnTooltipTimer(self, _): x,y=self.aggdisp.CalcUnscrolledPosition(*self.motionpos) res=self.aggdisp.HitTest(x,y) if res.item is not None: try: self.tipwindow.Destroy() except: pass self.tipwindow=res.item.DisplayTooltip(self.aggdisp, res.itemrectscrolled) def OnRefresh(self): self.aggdisp.UpdateItems() def GetSelectedItems(self): return [item for _,_,_,item in self.aggdisp.GetSelection()] def GetAllItems(self): return [item for _,_,_,item in self.aggdisp.GetAllItems()] def OnSelectAll(self, _): self.aggdisp.SelectAll() def EndSelectedFilesContext(self, context, deleteitems=False): # We have a fun additional problem. By default Windows # returns a code that it is copying, when in fact it is # moving. Consequently we do a delete if either the # source file is gone, or deleteitems is true if not deleteitems: for item in context: if not os.path.exists(item.filename): print "Forcing EndSelectedFilesContext to delete mode even though not specified" deleteitems=True break if deleteitems: for item in context: if os.path.exists(item.filename): os.remove(item.filename) for item in context: item.RemoveFromIndex() self.OnRefresh() def OnSave(self, _): # If one item is selected we ask for a filename to save. If # multiple then we ask for a directory, and users don't get # the choice to affect the names of files. Note that we don't # allow users to select a different format for the file - we # just copy it as is. items=self.GetSelectedItems() if len(items)==1: ext=getext(items[0].name) if ext=="": ext="*" else: ext="*."+ext dlg=wx.FileDialog(self, "Save item", wildcard=ext, defaultFile=items[0].name, style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR) if dlg.ShowModal()==wx.ID_OK: shutil.copyfile(items[0].filename, dlg.GetPath()) dlg.Destroy() else: dlg=wx.DirDialog(self, "Save items to", style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON) if dlg.ShowModal()==wx.ID_OK: for item in items: shutil.copyfile(item.filename, os.path.join(dlg.GetPath(), basename(item.filename))) dlg.Destroy() if guihelper.IsMSWindows(): def OnCopy(self, _): items=self.GetSelectedItems() if not len(items): # nothing selected return file_names=wx.FileDataObject() for item in items: file_names.AddFile(item.filename) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(file_names) wx.TheClipboard.Close() def CanCopy(self): return len(self.GetSelectedItems()) def OnPaste(self, _=None): if not wx.TheClipboard.Open(): # can't access the clipboard return if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_FILENAME)): file_names=wx.FileDataObject() has_data=wx.TheClipboard.GetData(file_names) else: has_data=False wx.TheClipboard.Close() if has_data: self.OnAddFiles(file_names.GetFilenames()) def CanPaste(self): """ Return True if can accept clipboard data, False otherwise """ if not wx.TheClipboard.Open(): return False r=wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_FILENAME)) wx.TheClipboard.Close() return r def OnDelete(self,_): items=self.GetSelectedItems() for item in items: os.remove(item.filename) for item in items: item.RemoveFromIndex() self.OnRefresh() def genericpopulatefs(self, dict, key, indexkey, version): try: os.makedirs(self.thedir) except: pass if not os.path.isdir(self.thedir): raise Exception("Bad directory for "+key+" '"+self.thedir+"'") # delete all files we don't know about if 'key' contains replacements if dict.has_key(key): print key,"present - updating disk" for f in os.listdir(self.thedir): # delete them all except windows magic ones which we ignore if f.lower() not in self.skiplist: os.remove(os.path.join(self.thedir, f)) d=dict[key] for i in d: open(os.path.join(self.thedir, i.encode(media_codec)), "wb").write(d[i]) d={} d[indexkey]=dict[indexkey] common.writeversionindexfile(os.path.join(self.thedir, "index.idx"), d, version) return dict def genericgetfromfs(self, result, key, indexkey, currentversion): try: os.makedirs(self.thedir) except: pass if not os.path.isdir(self.thedir): raise Exception("Bad directory for "+key+" '"+self.thedir+"'") dict={} for file in os.listdir(self.thedir): if file=='index.idx': d={} d['result']={} common.readversionedindexfile(os.path.join(self.thedir, file), d, self.versionupgrade, currentversion) result.update(d['result']) elif file.lower() in self.skiplist: # ignore windows detritus continue elif key is not None: dict[file.decode(media_codec)]=open(os.path.join(self.thedir, file), "rb").read() if key is not None: result[key]=dict if indexkey not in result: result[indexkey]={} return result def OnDropFiles(self, _, dummy, filenames): # There is a bug in that the most recently created tab # in the notebook that accepts filedrop receives these # files, not the most visible one. We find the currently # viewed tab in the notebook and send the files there if self.__dragging: # I'm the drag source, forget 'bout it ! return target=self # fallback t=self.mainwindow.nb.GetPage(self.mainwindow.nb.GetSelection()) if isinstance(t, FileView): # changing target in dragndrop target=t target.OnAddFiles(filenames) def OnAdd(self, _=None): dlg=wx.FileDialog(self, "Choose files", style=wx.OPEN|wx.MULTIPLE, wildcard=self.wildcard) if dlg.ShowModal()==wx.ID_OK: self.OnAddFiles(dlg.GetPaths()) dlg.Destroy() def CanRename(self): return len(self.GetSelectedItems())==1 # subclass needs to define this media_notification_type=None def OnRename(self, _=None): items=self.GetSelectedItems() if len(items)!=1: # either none or more than 1 items selected return old_name=items[0].name dlg=wx.TextEntryDialog(self, "Enter a new name:", "Item Rename", old_name) if dlg.ShowModal()==wx.ID_OK: new_name=dlg.GetValue() if len(new_name) and new_name!=old_name: old_file_name=items[0].filename new_file_name=self.getshortenedbasename(new_name) try: os.rename(old_file_name, new_file_name) items[0].RenameInIndex(os.path.basename( str(new_file_name).decode(media_codec))) pubsub.publish(pubsub.MEDIA_NAME_CHANGED, data={ pubsub.media_change_type: self.media_notification_type, pubsub.media_old_name: old_file_name, pubsub.media_new_name: new_file_name }) except: pass dlg.Destroy() def OnAddFiles(self,_): raise Exception("not implemented") def decodefilename(self, filename): path,filename=os.path.split(filename) decoded_file=str(filename).decode(media_codec) return os.path.join(path, filename) def getshortenedbasename(self, filename, newext=''): filename=basename(filename) if not 'A' in self.filenamechars: filename=filename.lower() if not 'a' in self.filenamechars: filename=filename.upper() if len(newext): filename=stripext(filename) filename="".join([x for x in filename if x in self.filenamechars]) filename=filename.replace(" "," ").replace(" ", " ") # remove double spaces if len(newext): filename+='.'+newext if len(filename)>self.maxlen: chop=len(filename)-self.maxlen filename=stripext(filename)[:-chop].strip()+'.'+getext(filename) return os.path.join(self.thedir, filename.encode(media_codec)) def genericgetdata(self,dict,want, mediapath, mediakey, mediaindexkey): # this was originally written for wallpaper hence using the 'wp' variable dict.update(self._data) items=None if want==self.SELECTED: items=self.GetSelectedItems() if len(items)==0: want=self.ALL if want==self.ALL: items=self.GetAllItems() if items is not None: wp={} i=0 for item in items: data=open(item.filename, "rb").read() wp[i]={'name': item.name, 'data': data} v=item.origin if v is not None: wp[i]['origin']=v i+=1 dict[mediakey]=wp return dict def log(self, log_str): self.mainwindow.log(log_str) class FileViewDisplayItem(object): datakey="Someone forgot to set me" PADDING=3 def __init__(self, view, key, mediapath): self.view=view self.key=key self.thumbsize=10,10 self.mediapath=mediapath self.setvals() self.lastw=None def setvals(self): me=self.view._data[self.datakey][self.key] self.name=me['name'] self.origin=me.get('origin', None) self.filename=os.path.join(self.mediapath, self.name.encode(media_codec)) self.fileinfo=self.view.GetFileInfo(self.filename) self.size=self.fileinfo.size self.short=self.fileinfo.shortdescription() self.long=self.fileinfo.longdescription() self.thumb=None self.selbbox=None self.lines=[self.name, self.short, '%.1f kb' % (self.size/1024.0,)] if self.origin: self.lines.append(self.origin) def setthumbnailsize(self, thumbnailsize): self.thumbnailsize=thumbnailsize self.thumb=None self.selbox=None def Draw(self, dc, width, height, selected): if self.thumb is None: self.thumb=self.view.GetItemThumbnail(self.name, self.thumbnailsize[0], self.thumbnailsize[1]) redrawbbox=False if selected: if self.lastw!=width or self.selbbox is None: redrawbbox=True else: oldb=dc.GetBrush() oldp=dc.GetPen() dc.SetBrush(self.view.item_selection_brush) dc.SetPen(self.view.item_selection_pen) dc.DrawRectangle(*self.selbbox) dc.SetBrush(oldb) dc.SetPen(oldp) dc.DrawBitmap(self.thumb, self.PADDING+self.thumbnailsize[0]/2-self.thumb.GetWidth()/2, self.PADDING, True) xoff=self.PADDING+self.thumbnailsize[0]+self.PADDING yoff=self.PADDING*2 widthavailable=width-xoff-self.PADDING maxw=0 old=dc.GetFont() for i,line in enumerate(self.lines): dc.SetFont(self.view.item_line_font[i]) w,h=DrawTextWithLimit(dc, xoff, yoff, line, widthavailable, self.view.item_guardspace, self.view.item_term) maxw=max(maxw,w) yoff+=h dc.SetFont(old) self.lastw=width self.selbbox=(0,0,xoff+maxw+self.PADDING,max(yoff+self.PADDING,self.thumb.GetHeight()+self.PADDING*2)) if redrawbbox: return self.Draw(dc, width, height, selected) return self.selbbox def DisplayTooltip(self, parent, rect): res=["Name: "+self.name, "Origin: "+(self.origin, "default")[self.origin is None], 'File size: %.1f kb (%d bytes)' % (self.size/1024.0, self.size), "\n"+self.datatype+" information:\n", self.long] # tipwindow takes screen coordinates so we have to transform x,y=parent.ClientToScreen(rect[0:2]) return wx.TipWindow(parent, "\n".join(res), 1024, wx.Rect(x,y,rect[2], rect[3])) def RemoveFromIndex(self): del self.view._data[self.datakey][self.key] self.view.modified=True self.view.OnRefresh() def RenameInIndex(self, new_name): self.view._data[self.datakey][self.key]['name']=new_name self.view.modified=True self.view.OnRefresh() ### ### Various platform independent filename functions ### basename=common.basename stripext=common.stripext getext=common.getext ### ### A dialog showing a message in a fixed font, with a help button ### class MyFixedScrolledMessageDialog(wx.Dialog): """A dialog displaying a readonly text control with a fixed width font""" def __init__(self, parent, msg, caption, helpid, pos = wx.DefaultPosition, size = (850,600)): wx.Dialog.__init__(self, parent, -1, caption, pos, size, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) text=wx.TextCtrl(self, 1, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP ) # Fixed width font f=wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL ) ta=wx.TextAttr(font=f) text.SetDefaultStyle(ta) text.AppendText(msg) # if i supply this in constructor then the font doesn't take text.SetInsertionPoint(0) text.ShowPosition(text.XYToPosition(0,0)) # vertical sizer vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(text, 1, wx.EXPAND|wx.ALL, 10) # buttons vbs.Add(self.CreateButtonSizer(wx.OK|wx.HELP), 0, wx.ALIGN_RIGHT|wx.ALL, 10) # plumb self.SetSizer(vbs) self.SetAutoLayout(True) wx.EVT_BUTTON(self, wx.ID_HELP, lambda _,helpid=helpid: wx.GetApp().displayhelpid(helpid)) ### ### Dialog that deals with exceptions ### import StringIO class ExceptionDialog(wx.Dialog): def __init__(self, parent, exception, title="Exception"): wx.Dialog.__init__(self, parent, title=title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.THICK_FRAME|wx.MAXIMIZE_BOX, size=(740, 580)) self.maintext=wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2|wx.HSCROLL) vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(self.maintext, 1, wx.EXPAND|wx.ALL, 5) buttsizer=wx.GridSizer(1, 3) buttsizer.Add(wx.Button(self, wx.ID_CANCEL, "Abort BitPim"), 0, wx.ALL, 10) buttsizer.Add(wx.Button(self, wx.ID_HELP, "Help"), 0, wx.ALL, 10) buttsizer.Add(wx.Button(self, wx.ID_OK, "Continue"), 0, wx.ALL, 10) vbs.Add(buttsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 5) wx.EVT_BUTTON(self, wx.ID_CANCEL, self.abort) wx.EVT_BUTTON(self, wx.ID_HELP, lambda _: wx.GetApp().displayhelpid(helpids.ID_EXCEPTION_DIALOG)) self.SetSizer(vbs) self._text="" self.addexception(exception) def abort(self,_): import os os._exit(1) def addexception(self, exception): s=StringIO.StringIO() s.write("An unexpected exception has occurred.\nPlease see the help for details on what to do.\n\n") if hasattr(exception, 'gui_exc_info'): s.write(common.formatexception(exception.gui_exc_info)) else: s.write("Exception with no extra info.\n%s\n" % (exception.str(),)) self._text=s.getvalue() self.maintext.SetValue(self._text) def getexceptiontext(self): return self._text ### ### Too much freaking effort for a simple statusbar. Mostly copied from the demo. ### class MyStatusBar(wx.StatusBar): __total_panes=3 __version_index=2 __phone_model_index=2 __app_status_index=0 __gauge_index=1 __major_progress_index=2 __minor_progress_index=2 __help_str_index=2 __general_pane=2 __pane_width=[50, 180, -1] def __init__(self, parent, id=-1): wx.StatusBar.__init__(self, parent, id) self.__major_progress_text=self.__version_text=self.__phone_text='' self.sizechanged=False wx.EVT_SIZE(self, self.OnSize) wx.EVT_IDLE(self, self.OnIdle) self.gauge=wx.Gauge(self, 1000, 1) self.SetFieldsCount(self.__total_panes) self.SetStatusWidths(self.__pane_width) self.Reposition() def OnSize(self,_): self.sizechanged=True def OnIdle(self,_): if not len(self.GetStatusText(self.__general_pane)): self.__set_version_phone_text() if self.sizechanged: try: self.Reposition() except: # this works around a bug in wx (on Windows only) # where we get a bogus exception. See SF bug # 873155 pass def Reposition(self): self.sizeChanged = False rect=self.GetFieldRect(self.__gauge_index) self.gauge.SetPosition(wx.Point(rect.x+2, rect.y+2)) self.gauge.SetSize(wx.Size(rect.width-4, rect.height-4)) def progressminor(self, pos, max, desc=""): self.gauge.SetRange(max) self.gauge.SetValue(pos) if len(self.__major_progress_text): s=self.__major_progress_text if len(desc): s+=' - '+desc else: s=desc self.SetStatusText(s, self.__minor_progress_index) def progressmajor(self, pos, max, desc=""): if len(desc) and max: self.__major_progress_text="%d/%d %s" % (pos+1, max, desc) else: self.__major_progress_text=desc self.progressminor(0,1) def GetHelpPane(self): return self.__help_str_index def set_app_status(self, str=''): self.SetStatusText(str, self.__app_status_index) def set_phone_model(self, str=''): self.__phone_text=str self.__set_version_phone_text() def set_versions(self, current, latest=''): s='BitPim '+current if len(latest): s+='/Latest '+latest else: s+='/Latest <Unknown>' self.__version_text=s self.__set_version_phone_text() def __set_version_phone_text(self): if guihelper.IsMac(): s = self.__version_text+' '+self.__phone_text else: s = self.__version_text+'\t'+self.__phone_text self.SetStatusText(s, self.__general_pane) ### ### A MessageBox with a help button ### class AlertDialogWithHelp(wx.Dialog): """A dialog box with Ok button and a help button""" def __init__(self, parent, message, caption, helpfn, style=wx.DEFAULT_DIALOG_STYLE, icon=wx.ICON_EXCLAMATION): wx.Dialog.__init__(self, parent, -1, caption, style=style|wx.DEFAULT_DIALOG_STYLE) p=self # parent widget # horiz sizer for bitmap and text hbs=wx.BoxSizer(wx.HORIZONTAL) hbs.Add(wx.StaticBitmap(p, -1, wx.ArtProvider_GetBitmap(self.icontoart(icon), wx.ART_MESSAGE_BOX)), 0, wx.CENTER|wx.ALL, 10) hbs.Add(wx.StaticText(p, -1, message), 1, wx.CENTER|wx.ALL, 10) # the buttons buttsizer=self.CreateButtonSizer(wx.HELP|style) # Both vertical vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 10) vbs.Add(buttsizer, 0, wx.CENTER|wx.ALL, 10) # wire it in self.SetSizer(vbs) self.SetAutoLayout(True) vbs.Fit(self) wx.EVT_BUTTON(self, wx.ID_HELP, helpfn) def icontoart(self, id): if id&wx.ICON_EXCLAMATION: return wx.ART_WARNING if id&wx.ICON_INFORMATION: return wx.ART_INFORMATION # ::TODO:: rest of these # fallthru return wx.ART_INFORMATION ### ### Yet another dialog with user selectable buttons ### class AnotherDialog(wx.Dialog): """A dialog box with user supplied buttons""" def __init__(self, parent, message, caption, buttons, helpfn=None, style=wx.DEFAULT_DIALOG_STYLE, icon=wx.ICON_EXCLAMATION): """Constructor @param message: Text displayed in body of dialog @param caption: Title of dialog @param buttons: A list of tuples. Each tuple is a string and an integer id. The result of calling ShowModal() is the id @param helpfn: The function called if the user presses the help button (wx.ID_HELP) """ wx.Dialog.__init__(self, parent, -1, caption, style=style) p=self # parent widget # horiz sizer for bitmap and text hbs=wx.BoxSizer(wx.HORIZONTAL) hbs.Add(wx.StaticBitmap(p, -1, wx.ArtProvider_GetBitmap(self.icontoart(icon), wx.ART_MESSAGE_BOX)), 0, wx.CENTER|wx.ALL, 10) hbs.Add(wx.StaticText(p, -1, message), 1, wx.CENTER|wx.ALL, 10) # the buttons buttsizer=wx.BoxSizer(wx.HORIZONTAL) for label,id in buttons: buttsizer.Add( wx.Button(self, id, label), 0, wx.ALL|wx.ALIGN_CENTER, 5) if id!=wx.ID_HELP: wx.EVT_BUTTON(self, id, self.OnButton) else: wx.EVT_BUTTON(self, wx.ID_HELP, helpfn) # Both vertical vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 10) vbs.Add(buttsizer, 0, wx.CENTER|wx.ALL, 10) # wire it in self.SetSizer(vbs) self.SetAutoLayout(True) vbs.Fit(self) def OnButton(self, event): self.EndModal(event.GetId()) def icontoart(self, id): if id&wx.ICON_EXCLAMATION: return wx.ART_WARNING if id&wx.ICON_INFORMATION: return wx.ART_INFORMATION # ::TODO:: rest of these # fallthru return wx.ART_INFORMATION ### ### Utility code ### def DrawTextWithLimit(dc, x, y, text, widthavailable, guardspace, term="..."): """Draws text and if it will overflow the width available, truncates and puts ... at the end @param x: start position for text @param y: start position for text @param text: the string to draw @param widthavailable: the total amount of space available @param guardspace: if the text is longer than widthavailable then this amount of space is reclaimed from the right handside and term put there instead. Consequently this value should be at least the width of term @param term: the string that is placed in the guardspace if it gets truncated. Make sure guardspace is at least the width of this string! @returns: The extent of the text that was drawn in the end as a tuple of (width, height) """ w,h=dc.GetTextExtent(text) if w<widthavailable: dc.DrawText(text,x,y) return w,h extents=dc.GetPartialTextExtents(text) limit=widthavailable-guardspace # find out how many chars in we have to go before hitting limit for i,offset in enumerate(extents): if offset>limit: break # back off 1 in case the new text's a tad long if i: i-=1 text=text[:i]+term w,h=dc.GetTextExtent(text) assert w<=widthavailable dc.DrawText(text, x, y) return w,h ### ### Window geometry/positioning memory ### def set_size(confname, window, screenpct=50, aspect=1.0): """Sets remembered/calculated dimensions/position for window @param confname: subkey to store/get this windows's settings from @param window: the window object itself @param screenpct: percentage of the screen the window should occupy. If this value is negative then the window will not be resized, only repositioned (unless the current size is silly) @param aspect: aspect ratio. If greater than one then it is how much wider than tall the window is, and if less than one then the other way round """ confobj=wx.GetApp().config # frig confname confname="windows/"+confname # Get screen size, scale according to percentage supplied screenSize = wx.GetClientDisplayRect() if (aspect >= 1): newWidth = screenSize.width * abs(screenpct) / 100 newHeight = screenSize.height * abs(screenpct) / aspect / 100 else: newWidth = screenSize.width * abs(screenpct) * aspect / 100 newHeight = screenSize.height * abs(screenpct) / 100 if screenpct<=0: rs_width,rs_height=window.GetSizeTuple() else: # Retrieve values (if any) from config database for this config object rs_width = confobj.ReadInt(confname + "/width", int(newWidth)) rs_height = confobj.ReadInt(confname + "/height", int(newHeight)) # suitable magic number to show not configured. it is an exercise for the reader # why it isn't -65536 (hint: virtual desktops) unconfigured=-65245 rs_x = confobj.ReadInt(confname + "/x", unconfigured) rs_y = confobj.ReadInt(confname + "/y", unconfigured) # Check for small window if rs_height < 96: rs_height = newHeight if rs_width < 96: rs_width = newWidth # Make sure window is no larger than about screen size # # determine ratio of original oversized window so we keep the ratio if we resize... rs_aspect = rs_width/rs_height if rs_aspect >= 1: if rs_width > screenSize.width: rs_width = screenSize.width if rs_height > (screenSize.height): rs_height = (screenSize.height / rs_aspect) - screenSize.y else: if rs_width > screenSize.width: rs_width = screenSize.width * rs_aspect if rs_height > screenSize.height - screenSize.y: rs_height = screenSize.height - screenSize.y # Off the screen? Just pull it back a little bit so it's visible.... if rs_x!=unconfigured and rs_x > screenSize.width: rs_x = screenSize.width - 50 if rs_x!=unconfigured and rs_x + rs_width < screenSize.x: rs_x = screenSize.x if rs_y!=unconfigured and rs_y > screenSize.height: rs_y = screenSize.height - 50 if rs_y!=unconfigured and rs_y + rs_height < screenSize.y: rs_y = screenSize.y if screenpct<=0 and (rs_width,rs_height)==window.GetSizeTuple(): # set position only, and no need to resize if rs_x!=unconfigured and rs_y!=unconfigured: print "setting %s to position %d, %d" % (confname, rs_x, rs_y) window.SetPosition(wx.Point(rs_x, rs_y)) else: if rs_x==unconfigured or rs_y==unconfigured: print "setting %s to size %d x %d" % (confname, rs_width, rs_height) window.SetSize(wx.Size(rs_width, rs_height)) else: print "setting %s to position %d, %d - size %d x %d" % (confname, rs_x, rs_y, rs_width, rs_height) window.SetDimensions(rs_x, rs_y, rs_width, rs_height) def save_size(confname, myRect): """Saves size to config. L{set_size} @param confname: Same string as in set_size @param myRect: Window size you want remembered, typically window.GetRect() """ confobj=wx.GetApp().config confname="windows/"+confname x = myRect.x y = myRect.y width = myRect.width height = myRect.height confobj.WriteInt(confname + "/x", x) confobj.WriteInt(confname + "/y", y) confobj.WriteInt(confname + "/width", width) confobj.WriteInt(confname + "/height", height) confobj.Flush() class LogProgressDialog(wx.ProgressDialog): """ display log string and progress bar at the same time """ def __init__(self, title, message, maximum=100, parent=None, style=wx.PD_AUTO_HIDE|wx.PD_APP_MODAL): super(LogProgressDialog, self).__init__(title, message, maximum, parent, style) self.__progress_value=0 def Update(self, value, newmsg='', skip=None): self.__progress_value=value super(LogProgressDialog, self).Update(value, newmsg, skip) def log(self, msgstr): super(LogProgressDialog, self).Update(self.__progress_value, msgstr) class AskPhoneNameDialog(wx.Dialog): def __init__(self, parent, message, caption="Enter phone owner's name"): """ Ask a user to enter an owner's name of a phone. Similar to the wx.TextEntryDialog but has 3 buttons, Ok, No Thanks, and Maybe latter. """ super(AskPhoneNameDialog, self).__init__(parent, -1, caption) vbs=wx.BoxSizer(wx.VERTICAL) vbs.Add(wx.StaticText(self, -1, message), 0, wx.ALL, 5) self.__text_ctrl=wx.TextCtrl(self, -1, style=wx.TE_PROCESS_ENTER) vbs.Add(self.__text_ctrl, 0, wx.EXPAND|wx.ALL, 5) vbs.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5) hbs=wx.BoxSizer(wx.HORIZONTAL) ok_btn=wx.Button(self, wx.ID_OK, 'OK') hbs.Add(ok_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) cancel_btn=wx.Button(self, wx.ID_CANCEL, 'No Thanks') hbs.Add(cancel_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) maybe_btn=wx.Button(self, wx.NewId(), 'Maybe next time') hbs.Add(maybe_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) vbs.Add(hbs, 1, wx.ALL, 5) wx.EVT_BUTTON(self, maybe_btn.GetId(), self.__OnMaybe) wx.EVT_TEXT_ENTER(self, self.__text_ctrl.GetId(), self.__OnTextEnter) self.SetSizer(vbs) self.SetAutoLayout(True) vbs.Fit(self) def GetValue(self): return self.__text_ctrl.GetValue() def __OnMaybe(self, evt): self.EndModal(evt.GetId()) def __OnTextEnter(self, _): self.EndModal(wx.ID_OK) class HistoricalDataDialog(wx.Dialog): Current_Data=0 Historical_Data=1 _Historical_Date=1 _Historical_Event=2 def __init__(self, parent, caption='Historical Data Selection', current_choice=Current_Data, historical_date=None, historical_events=None): super(HistoricalDataDialog, self).__init__(parent, -1, caption) vbs=wx.BoxSizer(wx.VERTICAL) hbs=wx.BoxSizer(wx.HORIZONTAL) self.data_selector=wx.RadioBox(self, wx.NewId(), 'Data Selection:', choices=('Current', 'Historical Date', 'Historical Event'), style=wx.RA_SPECIFY_ROWS) self.data_selector.SetSelection(current_choice) wx.EVT_RADIOBOX(self, self.data_selector.GetId(), self.OnSelectData) hbs.Add(self.data_selector, 0, wx.ALL, 5) static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Historical Date:'), wx.VERTICAL) self.data_date=wx.DatePickerCtrl(self, style=wx.DP_DROPDOWN | wx.DP_SHOWCENTURY) if historical_date is not None: self.data_date.SetValue(wx.DateTimeFromTimeT(historical_date)) self.data_date.Enable(current_choice==self._Historical_Date) static_bs.Add(self.data_date, 1, wx.EXPAND, 0) hbs.Add(static_bs, 0, wx.ALL, 5) # historical events static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Historical Events:'), wx.VERTICAL) self.hist_events=wx.ListBox(self, -1, style=wx.LB_SINGLE) if historical_events: self._populate_historical_events(historical_events) self.hist_events.Enable(current_choice==self._Historical_Event) static_bs.Add(self.hist_events, 1, wx.EXPAND, 0) hbs.Add(static_bs, 0, wx.ALL, 5) vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5) vbs.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5) vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL), 0, wx.ALIGN_CENTER|wx.ALL, 5) self.SetSizer(vbs) self.SetAutoLayout(True) vbs.Fit(self) def OnSelectData(self, evt): self.data_date.Enable(evt.GetInt()==self._Historical_Date) self.hist_events.Enable(evt.GetInt()==self._Historical_Event) def GetValue(self): choice=self.data_selector.GetSelection() if choice==self.Current_Data: mode=self.Current_Data time_t=None elif choice==self._Historical_Date: dt=self.data_date.GetValue() dt.SetHour(23) dt.SetMinute(59) dt.SetSecond(59) mode=self.Historical_Data time_t=dt.GetTicks() else: sel=self.hist_events.GetSelection() if sel==wx.NOT_FOUND: mode=self.Current_Data time_t=None else: mode=self.Historical_Data time_t=self.hist_events.GetClientData(sel) return mode, time_t def _populate_historical_events(self, historical_events): keys=historical_events.keys() keys.sort() keys.reverse() for k in keys: # build the string self.hist_events.Append('%s %02d-Adds %02d-Dels %02d-Mods'%\ (time.strftime('%b %d, %y %H:%M:%S', time.localtime(k)), historical_events[k]['add'], historical_events[k]['del'], historical_events[k]['mod']), k)
guiwidgets.py.diff
(application/octet-stream, 1.2 KB)
Index: guiwidgets.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/guiwidgets.py,v
retrieving revision 1.268
diff -u -r1.268 guiwidgets.py
--- guiwidgets.py 29 Sep 2005 12:17:02 -0000 1.268
+++ guiwidgets.py 8 Oct 2005 18:47:26 -0000
@@ -1188,8 +1188,9 @@
# check of any of the files have been removed,
# can't trust result returned by DoDragDrop
for item in items:
- if not os.access(item.filename, os.F_OK):
- # item has been moved, remove from index
+ # this used to use os.access function, but it does not
+ # support unicode filenames
+ if not os.path.isfile(item.filename):
item.RemoveFromIndex()
def OnMouseEvent(self, evt):
@@ -1407,6 +1408,11 @@
def OnAddFiles(self,_):
raise Exception("not implemented")
+ def decodefilename(self, filename):
+ path,filename=os.path.split(filename)
+ decoded_file=str(filename).decode(media_codec)
+ return os.path.join(path, filename)
+
def getshortenedbasename(self, filename, newext=''):
filename=basename(filename)
if not 'A' in self.filenamechars:
hexeditor.py
(text/plain, 47.2 KB)
#!/usr/bin/env python ### BITPIM ### ### Copyright (C) 2003-2004 Roger Binns <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### $Id: hexeditor.py,v 1.15 2005/06/15 02:52:21 djpham Exp $ """A hex editor widget""" # system modules import string import struct # wx modules import wx from wx.lib import masked from wx.lib import scrolledpanel as scrolled # bitpim modules import common #------------------------------------------------------------------------------- class DataStruct(object): def __init__(self, _name): self.name=_name self.fields=[] def set(self, dict): self.name=dict.keys()[0] self.fields=[] for f in dict[self.name]: if f['type']==DataItem.numeric_type: item=NumericDataItem(f['name']) elif f['type']==DataItem.string_type: item=StringDataItem(f['name']) else: item=DataItem(f['name'], DataItem.struct_type) item.set(f) self.fields.append(item) def get(self): l=[] for f in self.fields: l.append(f.get()) return { self.name: l } def encode(self, data, buffer_offset=0): # encode data to each & every fields and return the results l=[] start=0 data_len=len(data) for f in self.fields: s=f.encode(data, start) start=f.start+f.len l.append( { '[0x%04X=%d]%s'%(f.start+buffer_offset, f.start+buffer_offset, f.name): `s` }) if start>=data_len: break return l #------------------------------------------------------------------------------- class DataItem(object): """Represent a data item/component with in a record, which is a list of these things. """ offset_from_start='From Start' offset_from_prev='From Last Field' string_type='string' numeric_type='numeric' struct_type='struct' def __init__(self, _name, _type=numeric_type): self.name=_name self.offset_type=self.offset_from_start self.offset=0 self.size=1 self.start=self.len=None # start & length/size of actual data encoded # numeric fields self.unsigned=True self.LE=True # string fields self.fixed=True self.null_terminated=False self.type=_type def _get_type(self): return self._type def _set_type(self, _type): if _type not in (self.numeric_type, self.string_type, self.struct_type): raise TypeError self._type=_type if _type==self.numeric_type: self.__class__=NumericDataItem elif _type==self.string_type: self.__class__=StringDataItem type=property(fget=_get_type, fset=_set_type) def get(self): return { 'name': self.name, 'offset_type': self.offset_type, 'offset': self.offset, 'type': self.type } def set(self, d): self.name=d.get('name', '<None>') self.offset_type=d.get('offset_type', None) self.offset=d.get('offset', None) self.type=d.get('type', None) def encode(self, s, start=None): """Encode the value of this item based on the string s""" raise NotImplementedError #------------------------------------------------------------------------------- class NumericDataItem(DataItem): _fmts={ # struct pack/unpack formats True: { # unsigned True: { # little endian 1: 'B', 2: '<H', 4: '<I' }, # size False: { # big endian 1: 'B', 2: '>H', 4: '>I' } }, # size False: { # signed True: { # little endian 1: 'b', 2: '<h', 4: '<i' }, # size False: { # big endian 1: 'b', 2: '>h', 4: '>i' } } } # size def __init__(self, name): super(NumericDataItem, self).__init__(name, self.numeric_type) def get(self): r=super(NumericDataItem, self).get() r.update( { 'unsigned': self.unsigned, 'little_endian': self.LE, 'size': self.size }) return r def set(self, d): super(NumericDataItem, self).set(d) if d.get('type', None)!=self.numeric_type: raise TypeError self.unsigned=d.get('unsigned', True) self.LE=d.get('little_endian', True) self.size=d.get('size', 1) def encode(self, s, start=None): fmt=self._fmts[self.unsigned][self.LE][self.size] self.len=struct.calcsize(fmt) if self.offset_type==self.offset_from_start: self.start=self.offset else: if start is None: raise ValueError self.start=start+self.offset return struct.unpack(fmt, s[self.start:self.start+self.len])[0] #------------------------------------------------------------------------------- class StringDataItem(DataItem): def __init__(self, name): super(StringDataItem, self).__init__(name, self.string_type) def get(self): r=super(StringDataItem, self).get() r.update({ 'fixed': self.fixed, 'size': self.size, 'null_terminated': self.null_terminated }) return r def set(self, d): super(StringDataItem, self).set(d) if d.get('type', None)!=self.string_type: raise TypeError self.fixed=d.get('fixed', True) self.size=d.get('size', 0) self.null_terminated=d.get('null_terminated', False) def encode(self, s, start=None): if self.offset_type==self.offset_from_start: self.start=self.offset else: if start is None: raise ValueError self.start=start+self.offset if self.fixed: # fixed length string if self.size==-1: # take all available space self.len=len(s)-self.offset s0=s[self.start:] else: # fixed size s0=s[self.start:self.start+self.size] self.len=self.size else: # pascal style variable string self.len=ord(s[self.start]) s0=s[self.start+1:self.start+1+self.len] if self.null_terminated: i=s0.find('\x00') if i==-1: return s0 else: self.len=i return s0[:i] else: return s0 #------------------------------------------------------------------------------- class GeneralInfoSizer(wx.FlexGridSizer): def __init__(self, parent): super(GeneralInfoSizer, self).__init__(-1, 2, 5, 5) self.AddGrowableCol(1) self.Add(wx.StaticText(parent, -1, 'Struct Name:'), 0, wx.EXPAND|wx.ALL, 5) self._struct_name=wx.TextCtrl(parent, -1, '') self.Add(self._struct_name, 0, wx.EXPAND|wx.ALL, 5) self.Add(wx.StaticText(parent, -1, 'Field Name:'), 0, wx.EXPAND|wx.ALL, 5) self._name=wx.TextCtrl(parent, -1, '') self.Add(self._name, 0, wx.EXPAND|wx.ALL, 5) self.Add(wx.StaticText(parent, -1, 'Type:'), 0, wx.EXPAND|wx.ALL, 5) self._type=wx.ComboBox(parent, wx.NewId(), choices=[DataItem.numeric_type, DataItem.string_type], value=DataItem.numeric_type, style=wx.CB_DROPDOWN|wx.CB_READONLY) self.Add(self._type, 0, wx.EXPAND|wx.ALL, 5) self.Add(wx.StaticText(parent, -1, 'Offset Type:'), 0, wx.EXPAND|wx.ALL, 5) self._offset_type=wx.ComboBox(parent, -1, value=DataItem.offset_from_start, choices=[DataItem.offset_from_start, DataItem.offset_from_prev], style=wx.CB_DROPDOWN|wx.CB_READONLY) self.Add(self._offset_type, 0, wx.EXPAND|wx.ALL, 5) self.Add(wx.StaticText(parent, -1, 'Offset Value:'), 0, wx.EXPAND|wx.ALL, 5) self._offset=masked.NumCtrl(parent, wx.NewId(), allowNegative=False, min=0) self.Add(self._offset, 0, wx.ALL, 5) self._fields_group=(self._name, self._type, self._offset_type, self._offset) def set(self, data): if isinstance(data, DataStruct): self._struct_name.SetValue(data.name) elif isinstance(data, DataItem): self._name.SetValue(data.name) self._type.SetValue(data.type) self._offset_type.SetValue(data.offset_type) self._offset.SetValue(data.offset) def get(self, data): data.name=self._name.GetValue() data.type=self._type.GetValue() data.offset_type=self._offset_type.GetValue() data.offset=int(self._offset.GetValue()) return data def show(self, show_struct=False, show_field=False): self._struct_name.Enable(show_struct) for w in self._fields_group: w.Enable(show_field) def _get_struct_name(self): return self._struct_name.GetValue() struct_name=property(fget=_get_struct_name) def _get_type(self): return self._type.GetValue() type=property(fget=_get_type) def _get_type_id(self): return self._type.GetId() type_id=property(fget=_get_type_id) #------------------------------------------------------------------------------- class NumericInfoSizer(wx.FlexGridSizer): _sign_choices=['Unsigned', 'Signed'] _endian_choices=['Little Endian', 'Big Endian'] _size_choices=['1', '2', '4'] def __init__(self, parent): super(NumericInfoSizer, self).__init__(-1, 2, 5, 5) self.AddGrowableCol(1) self.Add(wx.StaticText(parent, -1, 'Signed:'), 0, wx.EXPAND|wx.ALL, 5) self._sign=wx.ComboBox(parent, -1, value=self._sign_choices[0], choices=self._sign_choices, style=wx.CB_DROPDOWN|wx.CB_READONLY) self.Add(self._sign, 0, wx.EXPAND|wx.ALL, 5) self.Add(wx.StaticText(parent, -1, 'Endian:'), 0, wx.EXPAND|wx.ALL, 5) self._endian=wx.ComboBox(parent, -1, value=self._endian_choices[0], choices=self._endian_choices, style=wx.CB_DROPDOWN|wx.CB_READONLY) self.Add(self._endian, 0, wx.EXPAND|wx.ALL, 5) self.Add(wx.StaticText(parent, -1, 'Size:'), 0, wx.EXPAND|wx.ALL, 5) self._size=wx.ComboBox(parent, -1, value=self._size_choices[0], choices=self._size_choices, style=wx.CB_DROPDOWN|wx.CB_READONLY) self.Add(self._size, 0, wx.EXPAND|wx.ALL, 5) def set(self, data): if data.unsigned: self._sign.SetValue(self._sign_choices[0]) else: self._sign.SetValue(self._sign_choices[1]) if data.LE: self._endian.SetValue(self._endian_choices[0]) else: self._endian.SetValue(self._endian_choices[1]) self._size.SetValue(`data.size`) def get(self, data): data.unsigned=self._sign.GetValue()==self._sign_choices[0] data.LE=self._endian.GetValue()==self._endian_choices[0] data.size=int(self._size.GetValue()) return data #------------------------------------------------------------------------------- class StringInfoSizer(wx.FlexGridSizer): _fixed_choices=['Fixed', 'Pascal'] def __init__(self, parent): super(StringInfoSizer, self).__init__(-1, 2, 5, 5) self.AddGrowableCol(1) self.Add(wx.StaticText(parent, -1, 'Fixed/Pascal:'), 0, wx.EXPAND|wx.ALL, 5) self._fixed=wx.ComboBox(parent, -1, value=self._fixed_choices[0], choices=self._fixed_choices, style=wx.CB_DROPDOWN|wx.CB_READONLY) self.Add(self._fixed, 0, wx.EXPAND|wx.ALL, 5) self.Add(wx.StaticText(parent, -1, 'Max Length:'), 0, wx.EXPAND|wx.ALL, 5) self._max_len=masked.NumCtrl(parent, -1, value=1, min=-1) self.Add(self._max_len, 0, wx.EXPAND|wx.ALL, 5) self.Add(wx.StaticText(parent, -1, 'Null Terminated:'), 0, wx.EXPAND|wx.ALL, 5) self._null_terminated=wx.CheckBox(parent, -1) self.Add(self._null_terminated, 0, wx.EXPAND|wx.ALL, 5) def set(self, data): if data.fixed: self._fixed.SetValue(self._fixed_choices[0]) else: self._fixed.SetValue(self._fixed_choices[1]) self._max_len.SetValue(`data.size`) self._null_terminated.SetValue(data.null_terminated) def get(self, data): data.fixed=self._fixed.GetValue()==self._fixed_choices[0] data.size=int(self._max_len.GetValue()) data.null_terminated=self._null_terminated.GetValue() return data #------------------------------------------------------------------------------- class TemplateDialog(wx.Dialog): _type_choices=['Numeric', 'String'] _struct_type='struct' _field_type='field' def __init__(self, parent): super(TemplateDialog, self).__init__(parent, -1, 'Hex Template Editor', style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) self._data=[] self._item_tree=self._numeric_bs=self._string_bs=None self._general_bs=None self._tree_root=None self._field_info=self._field_info_hbs=None self._info_sizer={ NumericDataItem.numeric_type: self._numeric_bs, StringDataItem.string_type: self._string_bs } main_vbs=wx.BoxSizer(wx.VERTICAL) hbs1=wx.BoxSizer(wx.HORIZONTAL) hbs1.Add(self._create_tree_pane(), 1, wx.EXPAND|wx.ALL, 5) hbs1.Add(self._create_info_pane(), 2, wx.EXPAND|wx.ALL, 5) main_vbs.Add(hbs1, 1, wx.EXPAND|wx.ALL, 5) main_vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) main_vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.SetSizer(main_vbs) self.SetAutoLayout(True) main_vbs.Fit(self) def _create_tree_pane(self): vbs=wx.BoxSizer(wx.VERTICAL) sw=scrolled.ScrolledPanel(self, -1) self._item_tree=wx.TreeCtrl(sw, wx.NewId(), style=wx.TR_DEFAULT_STYLE|wx.TR_HAS_BUTTONS) wx.EVT_TREE_SEL_CHANGED(self, self._item_tree.GetId(), self._OnTreeSel) self._tree_root=self._item_tree.AddRoot('Data Templates') sw_bs=wx.BoxSizer(wx.VERTICAL) sw_bs.Add(self._item_tree, 1, wx.EXPAND|wx.ALL, 0) sw.SetSizer(sw_bs) sw.SetAutoLayout(True) sw_bs.Fit(sw) sw.SetupScrolling() vbs.Add(sw, 1, wx.EXPAND|wx.ALL, 5) hbs=wx.BoxSizer(wx.HORIZONTAL) hbs.Add(wx.Button(self, wx.ID_ADD, 'Add'), 0, wx.EXPAND|wx.ALL, 5) hbs.Add(wx.Button(self, wx.ID_DELETE, 'Delete'), 0, wx.EXPAND|wx.ALL, 5) wx.EVT_BUTTON(self, wx.ID_ADD, self._OnAdd) wx.EVT_BUTTON(self, wx.ID_DELETE, self._OnDelete) vbs.Add(hbs, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5) return vbs def _create_info_pane(self): # main boxsize vbs=wx.BoxSizer(wx.VERTICAL) hbs=wx.BoxSizer(wx.HORIZONTAL) # Type & offset static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Field Type'), wx.VERTICAL) self._general_bs=GeneralInfoSizer(self) wx.EVT_COMBOBOX(self, self._general_bs.type_id, self._OnTypeChanged) static_bs.Add(self._general_bs, 0, wx.EXPAND|wx.ALL, 5) hbs.Add(static_bs, 0, wx.ALL, 5) # all info self._field_info=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Field Info'), wx.VERTICAL) # numeric info box self._numeric_bs=NumericInfoSizer(self) self._field_info.Add(self._numeric_bs, 0, wx.EXPAND|wx.ALL, 5) self._string_bs=StringInfoSizer(self) self._field_info.Add(self._string_bs, 0, wx.EXPAND|wx.ALL, 5) hbs.Add(self._field_info, 0, wx.ALL, 5) vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5) hbs1=wx.BoxSizer(wx.HORIZONTAL) hbs1.Add(wx.Button(self, wx.ID_SAVE, 'Set'), 0, wx.EXPAND|wx.ALL, 5) hbs1.Add(wx.Button(self, wx.ID_REVERT, 'Revert'), 0, wx.EXPAND|wx.ALL, 5) wx.EVT_BUTTON(self, wx.ID_SAVE, self._OnSave) wx.EVT_BUTTON(self, wx.ID_REVERT, self._OnRevert) vbs.Add(hbs1, 0, wx.ALIGN_CENTER_HORIZONTAL, 0) self._field_info_hbs=hbs return vbs def _show_field_info(self, _struct=False, field=False, numeric_field=False, string_field=False): # show/hide individual fields self._general_bs.show(_struct, field) self._field_info.Show(self._numeric_bs, numeric_field) self._field_info.Show(self._string_bs, string_field) self._field_info.Layout() self._field_info_hbs.Layout() def _populate(self): # clear the tree and repopulate self._item_tree.DeleteChildren(self._tree_root) for i,e in enumerate(self._data): item=self._item_tree.AppendItem(self._tree_root, e.name) self._item_tree.SetPyData(item, { 'type': self._struct_type, 'index': i }) for i1,e1 in enumerate(e.fields): field_item=self._item_tree.AppendItem(item, e1.name) self._item_tree.SetPyData(field_item, { 'type': self._field_type, 'index': i, 'field_index': i1 }) self.expand() def _populate_struct(self, _item_index): self._general_bs.set(self._data[_item_index]) self._show_field_info(True) def _populate_each(self, _struct_index, _item_index): _struct=self._data[_struct_index] _item=_struct.fields[_item_index] self._general_bs.set(_item) if _item.type==DataItem.numeric_type: self._show_field_info(True, True, True) self._numeric_bs.set(_item) else: self._show_field_info(True, True, False, True) self._string_bs.set(_item) def _OnTypeChanged(self, _): new_type=self._general_bs.type self._show_field_info(True, True, new_type==DataItem.numeric_type, new_type==DataItem.string_type) def _OnAdd(self, _): sel_idx=self._item_tree.GetSelection() if not sel_idx.IsOk(): return if sel_idx==self._tree_root: # add a new structure struct_item=DataStruct('New Struct') self._data.append(struct_item) else: # add a new field to the existing structure data_item=self._item_tree.GetPyData(sel_idx) item=NumericDataItem('New Field') self._data[data_item['index']].fields.append(item) self._populate() def _OnDelete(self, _): sel_idx=self._item_tree.GetSelection() if not sel_idx.IsOk(): return node_data=self._item_tree.GetPyData(sel_idx) if node_data is None: return if node_data['type']==self._field_type: # del this field del self._data[node_data['index']].fields[node_data['field_index']] else: # del this struct and its fields del self._data[node_data['index']] # and re-populate the tree self._populate() def _OnSave(self, _): sel_idx=self._item_tree.GetSelection() if not sel_idx.IsOk(): return node_data=self._item_tree.GetPyData(sel_idx) if node_data is None: return # update the struct name self._data[node_data['index']].name=self._general_bs.struct_name if node_data['type']==self._field_type: data_item=self._data[node_data['index']].\ fields[node_data['field_index']] data_item=self._general_bs.get(data_item) if data_item.type==DataItem.numeric_type: data_item=self._numeric_bs.get(data_item) else: data_item=self._string_bs.get(data_item) self._data[node_data['index']].fields[node_data['field_index']]=data_item self._item_tree.SetItemText(self._item_tree.GetItemParent(sel_idx), self._data[node_data['index']].name) self._item_tree.SetItemText(sel_idx, data_item.name) else: self._item_tree.SetItemText(sel_idx, self._data[node_data['index']].name) def _OnRevert(self, _): sel_idx=self._item_tree.GetSelection() if not sel_idx.IsOk(): return node_data=self._item_tree.GetPyData(sel_idx) if node_data is None: self._show_field_info() else: self._populate_struct(node_data['index']) if node_data['type']==self._field_type: self._populate_each(node_data['index'], node_data['field_index']) def _OnTreeSel(self, evt): sel_idx=evt.GetItem() if not sel_idx.IsOk(): # invalid selection return item_data=self._item_tree.GetPyData(sel_idx) if item_data is None: self._show_field_info() else: self._populate_struct(item_data['index']) if item_data['type']==self._field_type: self._populate_each(item_data['index'], item_data['field_index']) def expand(self): # expand the tree self._item_tree.Expand(self._tree_root) (id, cookie)=self._item_tree.GetFirstChild(self._tree_root) while id.IsOk(): self._item_tree.Expand(id) (id, cookie)=self._item_tree.GetNextChild(self._tree_root, cookie) def set(self, l): self._data=l self._populate() def get(self): return self._data #------------------------------------------------------------------------------- class HexEditor(wx.ScrolledWindow): _addr_range=xrange(8) _hex_range_start=10 _hex_range_start2=33 _hex_range=xrange(_hex_range_start, 58) _ascii_range_start=60 _ascii_range=xrange(60, 76) def __init__(self, parent, id=-1, style=wx.WANTS_CHARS, _set_pos=None, _set_sel=None, _set_val=None): wx.ScrolledWindow.__init__(self, parent, id, style=style) self.parent=parent self.data="" self.title="" self.buffer=None self.hasfocus=False self.dragging=False self.current_ofs=None self._module=None self._templates=[] self._search_string=None # ways of displaying status self.set_pos=_set_pos or self._set_pos self.set_val=_set_val or self._set_val self.set_sel=_set_sel or self._set_sel # some GUI setup self.SetBackgroundColour("WHITE") self.SetCursor(wx.StockCursor(wx.CURSOR_IBEAM)) self.sethighlight(wx.NamedColour("BLACK"), wx.NamedColour("YELLOW")) self.setnormal(wx.NamedColour("BLACK"), wx.NamedColour("WHITE")) self.setfont(wx.TheFontList.FindOrCreateFont(10, wx.MODERN, wx.NORMAL, wx.NORMAL)) self.OnSize(None) self.highlightrange(None, None) # other stuff self._create_context_menu() self._map_events() def _map_events(self): wx.EVT_SCROLLWIN(self, self.OnScrollWin) wx.EVT_PAINT(self, self.OnPaint) wx.EVT_SIZE(self, self.OnSize) wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground) wx.EVT_SET_FOCUS(self, self.OnGainFocus) wx.EVT_KILL_FOCUS(self, self.OnLoseFocus) wx.EVT_LEFT_DOWN(self, self.OnStartSelection) wx.EVT_LEFT_UP(self, self.OnEndSelection) wx.EVT_MOTION(self, self.OnMakeSelection) wx.EVT_RIGHT_UP(self, self.OnRightClick) def _create_context_menu(self): self._reload_menu_id=self._apply_menu_id=None menu_items=( ('File', (('Load', self.OnLoadFile), ('Save As', self.OnSaveAs), ('Save Selection As', self.OnSaveSelection), ('Save Hexdump As', self.OnSaveHexdumpAs))), ('Set Selection', (('Start', self.OnStartSelMenu), ('End', self.OnEndSelMenu))), ('Value', self.OnViewValue), ('Search', (('Search', self.OnSearch), ('Search Again', self.OnSearchAgain))), ('Import Python Module', self.OnImportModule), ('Reload Python Module', self.OnReloadModule, '_reload_menu_id'), ('Apply Python Func', self.OnApplyFunc, '_apply_menu_id'), ('Template', (('Load', self.OnTemplateLoad), ('Save As', self.OnTemplateSaveAs), ('Edit', self.OnTemplateEdit), ('Apply', self.OnTemplateApply))) ) self._bgmenu=wx.Menu() for menu_item in menu_items: if isinstance(menu_item[1], tuple): # submenu sub_menu=wx.Menu() for submenu_item in menu_item[1]: id=wx.NewId() sub_menu.Append(id, submenu_item[0]) wx.EVT_MENU(self, id, submenu_item[1]) self._bgmenu.AppendMenu(wx.NewId(), menu_item[0], sub_menu) else: # regular menu item id=wx.NewId() self._bgmenu.Append(id, menu_item[0]) wx.EVT_MENU(self, id, menu_item[1]) if len(menu_item)>2: # need to save menu ID setattr(self, menu_item[2], id) def SetData(self, data): self.data=data self.needsupdate=True self.updatescrollbars() self.Refresh() def SetTitle(self, title): self.title=title def SetStatusDisplay(self, _set_pos=None, _set_sel=None, _set_val=None): self.set_pos=_set_pos or self._set_pos self.set_sel=_set_sel or self._set_sel self.set_val=_set_val or self._set_val def OnEraseBackground(self, _): pass def _set_pos(self, pos): pass def _set_sel(self, sel_start, sel_end): pass def _set_val(self, v): pass def _to_char_line(self, x, y): """Convert an x,y point to (char, line) """ return x/self.charwidth, y/self.charheight def _to_xy(self, char, line): return char*self.charwidth, line*self.charheight def _to_buffer_offset(self, char, line): if char in self._hex_range: if char>self._hex_range_start2: char-=1 if ((char-self._hex_range_start)%3)<2: return line*16+(char-self._hex_range_start)/3 elif char in self._ascii_range: return line*16+char-self._ascii_range_start def _set_and_move(self, evt): c,l=self._to_char_line(evt.GetX(), evt.GetY()) self.GetCaret().Move(self._to_xy(c, l)) x0, y0=self.GetViewStart() char_x=c+x0 line_y=l+y0 return self._to_buffer_offset(char_x, line_y) _value_formats=( ('unsigned char', 'B', struct.calcsize('B')), ('signed char', 'b', struct.calcsize('b')), ('LE unsigned short', '<H', struct.calcsize('<H')), ('LE signed short', '<h', struct.calcsize('<h')), ('BE unsigned short', '>H', struct.calcsize('>H')), ('BE signed short', '>h', struct.calcsize('>h')), ('LE unsigned int', '<I', struct.calcsize('<I')), ('LE signed int', '<i', struct.calcsize('<i')), ('BE unsigned int', '>I', struct.calcsize('>I')), ('BE signed int', '>i', struct.calcsize('>i')), ) def _gen_values(self, _data, _ofs): """ Generate the values of various number formats starting at the current offset. """ n=_data[_ofs:] len_n=len(n) s='0x%X=%d'%(_ofs, _ofs) res=[{ 'Data Offset': s}, {'':''} ] for i,e in enumerate(self._value_formats): if len_n<e[2]: continue v=struct.unpack(e[1], n[:e[2]])[0] if i%2: s='%d'%v else: fmt='0x%0'+str(e[2]*2)+'X=%d' s=fmt%(v,v) res.append({ e[0]: s }) return res def _apply_template(self, template_name): # if user specifies a block, encode that, if self.highlightstart is None or self.highlightstart==-1 or \ self.highlightend is None or self.highlightend==-1: # no selection _data=self.data[self.current_ofs:] _ofs=self.current_ofs else: _data=self.data[self.highlightstart:self.highlightend] _ofs=self.highlightstart for f in self._templates: if f.name==template_name: l=[{ 'Template': f.name }, { 'Data Offset': '0x%04X=%d'%(_ofs, _ofs) }] return l+f.encode(_data, _ofs) return [] def _display_result(self, result): """ Display the results from applying a Python routine over the data """ s='' for d in result: for k,e in d.items(): s+=k+':\t'+e+'\n' dlg=wx.MessageDialog(self, s, 'Results', style=wx.OK) dlg.ShowModal() dlg.Destroy() def OnLoadFile(self, _): dlg=wx.FileDialog(self, 'Select a file to load', style=wx.OPEN|wx.FILE_MUST_EXIST) if dlg.ShowModal()==wx.ID_OK: self.SetData(file(dlg.GetPath(), 'rb').read()) dlg.Destroy() def OnSaveAs(self, _): dlg=wx.FileDialog(self, 'Select a file to save', style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlg.ShowModal()==wx.ID_OK: file(dlg.GetPath(), 'wb').write(self.data) dlg.Destroy() def hexdumpdata(self): res="" l=len(self.data) if self.title: res += self.title+": "+`l`+" bytes\n" res += "<#! !#>\n" pos=0 while pos<l: text="%08X "%(pos) line=self.data[pos:pos+16] for i in range(len(line)): text+="%02X "%(ord(line[i])) text+=" "*(16-len(line)) text+=" " for i in range(len(line)): c=line[i] if (ord(c)>=32 and string.printable.find(c)>=0): text+=c else: text+='.' res+=text+"\n" pos+=16 return res def OnSaveHexdumpAs(self, _): dlg=wx.FileDialog(self, 'Select a file to save', style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlg.ShowModal()==wx.ID_OK: file(dlg.GetPath(), 'wb').write(self.hexdumpdata()) dlg.Destroy() def OnSaveSelection(self, _): if self.highlightstart is None or self.highlightstart==-1 or \ self.highlightend is None or self.highlightend==-1: # no selection return dlg=wx.FileDialog(self, 'Select a file to save', style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlg.ShowModal()==wx.ID_OK: file(dlg.GetPath(), 'wb').write( self.data[self.highlightstart:self.highlightend]) dlg.Destroy() def OnReloadModule(self, _): try: reload(self._module) except: self._module=None w=wx.MessageDialog(self, 'Failed to reload module', 'Reload Module Error', style=wx.OK|wx.ICON_ERROR) w.ShowModal() w.Destroy() def OnApplyFunc(self, _): choices=[x for x in dir(self._module) \ if callable(getattr(self._module, x))] dlg=wx.SingleChoiceDialog(self, 'Select a function to apply:', 'Apply Python Func', choices) if dlg.ShowModal()==wx.ID_OK: try: res=getattr(self._module, dlg.GetStringSelection())( self, self.data, self.current_ofs) self._display_result(res) except: w=wx.MessageDialog(self, 'Apply Func raised an exception', 'Apply Func Error', style=wx.OK|wx.ICON_ERROR) w.ShowModal() w.Destroy() dlg.Destroy() def OnImportModule(self, _): dlg=wx.TextEntryDialog(self, 'Enter the name of a Python Module:', 'Module Import') if dlg.ShowModal()==wx.ID_OK: try: self._module=__import__(dlg.GetValue()) except ImportError: self._module=None w=wx.MessageDialog(self, 'Failed to import module: '+dlg.GetValue(), 'Module Import Error', style=wx.OK|wx.ICON_ERROR) w.ShowModal() w.Destroy() dlg.Destroy() def OnStartSelMenu(self, evt): ofs=self.current_ofs if ofs is not None: self.highlightstart=ofs self.needsupdate=True self.Refresh() self.set_sel(self.highlightstart, self.highlightend) def OnEndSelMenu(self, _): ofs=self.current_ofs if ofs is not None: self.highlightend=ofs+1 self.needsupdate=True self.Refresh() self.set_sel(self.highlightstart, self.highlightend) def OnViewValue(self, _): ofs=self.current_ofs if ofs is not None: self._display_result(self._gen_values(self.data, ofs)) def OnStartSelection(self, evt): self.highlightstart=self.highlightend=None ofs=self._set_and_move(evt) if ofs is not None: self.highlightstart=ofs self.dragging=True self.set_val(self.data[ofs:]) else: self.set_val(None) self.needsupdate=True self.Refresh() self.set_pos(ofs) self.set_sel(self.highlightstart, self.highlightend) def OnMakeSelection(self, evt): if not self.dragging: return ofs=self._set_and_move(evt) if ofs is not None: self.highlightend=ofs+1 self.needsupdate=True self.Refresh() self.set_pos(ofs) self.set_sel(self.highlightstart, self.highlightend) def OnEndSelection(self, evt): self.dragging=False ofs=self._set_and_move(evt) self.set_pos(ofs) self.set_sel(self.highlightstart, self.highlightend) def OnRightClick(self, evt): self.current_ofs=self._set_and_move(evt) if self.current_ofs is None: self.set_val(None) else: self.set_val(self.data[self.current_ofs:]) self.set_pos(self.current_ofs) self._bgmenu.Enable(self._apply_menu_id, self._module is not None) self._bgmenu.Enable(self._reload_menu_id, self._module is not None) self.PopupMenu(self._bgmenu, evt.GetPosition()) def OnTemplateLoad(self, _): dlg=wx.FileDialog(self, 'Select a file to load', wildcard='*.tmpl', style=wx.OPEN|wx.FILE_MUST_EXIST) if dlg.ShowModal()==wx.ID_OK: result={} try: execfile(dlg.GetPath()) except UnicodeError: common.unicode_execfile(dlg.GetPath()) exist_keys={} for i,e in enumerate(self._templates): exist_keys[e.name]=i for d in result['templates']: data_struct=DataStruct('new struct') data_struct.set(d) if exist_keys.has_key(data_struct.name): self._templates[exist_keys[data_struct.name]]=data_struct else: self._templates.append(data_struct) dlg.Destroy() def OnTemplateSaveAs(self, _): dlg=wx.FileDialog(self, 'Select a file to save', wildcard='*.tmpl', style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlg.ShowModal()==wx.ID_OK: r=[x.get() for x in self._templates] common.writeversionindexfile(dlg.GetPath(), { 'templates': r }, 1) dlg.Destroy() def OnTemplateApply(self, _): if not self._templates: # no templates to apply return choices=[x.name for x in self._templates] dlg=wx.SingleChoiceDialog(self, 'Select a template to apply:', 'Apply Data Template', choices) if dlg.ShowModal()==wx.ID_OK: try: res=self._apply_template(dlg.GetStringSelection()) self._display_result(res) except: raise w=wx.MessageDialog(self, 'Apply Template raised an exception', 'Apply Template Error', style=wx.OK|wx.ICON_ERROR) w.ShowModal() w.Destroy() dlg.Destroy() def OnTemplateEdit(self, _): dlg=TemplateDialog(self) dlg.set(self._templates) if dlg.ShowModal()==wx.ID_OK: self._templates=dlg.get() dlg.Destroy() def OnSearch(self, evt): dlg=wx.TextEntryDialog(self, 'Enter data to search (1 0x23 045 ...):', 'Search Data') if dlg.ShowModal()==wx.ID_OK: l=dlg.GetValue().split(' ') s='' for e in l: if e[0:2]=='0x': s+=chr(int(e, 16)) elif e[0]=='0': s+=chr(int(e, 8)) else: s+=chr(int(e)) i=self.data[self.current_ofs:].find(s) if i!=-1: self._search_string=s self.highlightstart=i+self.current_ofs self.highlightend=self.highlightstart+len(s) self.needsupdate=True self.Refresh() self.set_sel(self.highlightstart, self.highlightend) else: self._search_string=None def OnSearchAgain(self, evt): if self._search_string is not None: i=self.data[self.current_ofs:].find(self._search_string) if i==-1: return self.highlightstart=i+self.current_ofs self.highlightend=self.highlightstart+len(self._search_string) self.needsupdate=True self.Refresh() self.set_sel(self.highlightstart, self.highlightend) def OnSize(self, evt): # uncomment these lines to prevent going wider than is needed # if self.width>self.widthinchars*self.charwidth: # self.SetClientSize( (self.widthinchars*self.charwidth, self.height) ) if evt is None: self.width=(self.widthinchars+3)*self.charwidth self.height=self.charheight*20 self.SetClientSize((self.width, self.height)) self.SetCaret(wx.Caret(self, (self.charwidth, self.charheight))) self.GetCaret().Show(True) else: self.width,self.height=self.GetClientSizeTuple() self.needsupdate=True def OnGainFocus(self,_): self.hasfocus=True self.needsupdate=True self.Refresh() def OnLoseFocus(self,_): self.hasfocus=False self.needsupdate=True self.Refresh() def highlightrange(self, start, end): self.needsupdate=True self.highlightstart=start self.highlightend=end self.Refresh() self.set_pos(None) self.set_sel(self.highlightstart, self.highlightend) self.set_val(None) def _ishighlighted(self, pos): return pos>=self.highlightstart and pos<self.highlightend def sethighlight(self, foreground, background): self.highlight=foreground,background def setnormal(self, foreground, background): self.normal=foreground,background def setfont(self, font): dc=wx.ClientDC(self) dc.SetFont(font) self.charwidth, self.charheight=dc.GetTextExtent("M") self.font=font self.updatescrollbars() def updatescrollbars(self): # how many lines are we? lines=len(self.data)/16 if lines==0 or len(self.data)%16: lines+=1 self.datalines=lines ## lines+=1 # status line # fixed width self.widthinchars=8+2+3*16+1+2+16 self.SetScrollbars(self.charwidth, self.charheight, self.widthinchars, lines, self.GetViewStart()[0], self.GetViewStart()[1]) def _setnormal(self,dc): dc.SetTextForeground(self.normal[0]) dc.SetTextBackground(self.normal[1]) def _sethighlight(self,dc): dc.SetTextForeground(self.highlight[0]) dc.SetTextBackground(self.highlight[1]) def _setstatus(self,dc): dc.SetTextForeground(self.normal[1]) dc.SetTextBackground(self.normal[0]) dc.SetBrush(wx.BLACK_BRUSH) def OnDraw(self, dc): xd,yd=self.GetViewStart() st=0 # 0=normal, 1=highlight dc.BeginDrawing() dc.SetBackgroundMode(wx.SOLID) dc.SetFont(self.font) for line in range(yd, min(self.datalines, yd+self.height/self.charheight+1)): # address self._setnormal(dc) st=0 dc.DrawText("%08X" % (line*16), 0, line*self.charheight) # bytes for i in range(16): pos=line*16+i if pos>=len(self.data): break hl=self._ishighlighted(pos) if hl!=st: if hl: st=1 self._sethighlight(dc) else: st=0 self._setnormal(dc) if hl: space="" if i<15: if self._ishighlighted(pos+1): space=" " if i==7: space=" " else: space="" c=self.data[pos] dc.DrawText("%02X%s" % (ord(c),space), (10+(3*i)+(i>=8))*self.charwidth, line*self.charheight) if not (ord(c)>=32 and string.printable.find(c)>=0): c='.' dc.DrawText(c, (10+(3*16)+2+i)*self.charwidth, line*self.charheight) ## if self.hasfocus: ## self._setstatus(dc) ## w,h=self.GetClientSizeTuple() ## dc.DrawRectangle(0,h-self.charheight+yd*self.charheight,self.widthinchars*self.charwidth,self.charheight) ## dc.DrawText("A test of stuff "+`yd`, 0, h-self.charheight+yd*self.charheight) dc.EndDrawing() def updatebuffer(self): if self.buffer is None or \ self.buffer.GetWidth()!=self.width or \ self.buffer.GetHeight()!=self.height: if self.buffer is not None: del self.buffer self.buffer=wx.EmptyBitmap(self.width, self.height) mdc=wx.MemoryDC() mdc.SelectObject(self.buffer) mdc.SetBackground(wx.TheBrushList.FindOrCreateBrush(self.GetBackgroundColour(), wx.SOLID)) mdc.Clear() self.PrepareDC(mdc) self.OnDraw(mdc) mdc.SelectObject(wx.NullBitmap) del mdc def OnPaint(self, event): if self.needsupdate: self.needsupdate=False self.updatebuffer() dc=wx.PaintDC(self) dc.BeginDrawing() dc.DrawBitmap(self.buffer, 0, 0, False) dc.EndDrawing() def OnScrollWin(self, event): self.needsupdate=True self.Refresh() # clear whole widget event.Skip() # default event handlers now do scrolling etc class HexEditorDialog(wx.Dialog): _pane_widths=[-2, -3, -4] _pos_pane_index=0 _sel_pane_index=1 _val_pane_index=2 def __init__(self, parent, data='', title='BitPim Hex Editor', helpd_id=-1): super(HexEditorDialog, self).__init__(parent, -1, title, size=(500, 500), style=wx.DEFAULT_DIALOG_STYLE|\ wx.RESIZE_BORDER) self._status_bar=wx.StatusBar(self, -1) self._status_bar.SetFieldsCount(len(self._pane_widths)) self._status_bar.SetStatusWidths(self._pane_widths) vbs=wx.BoxSizer(wx.VERTICAL) self._hex_editor=HexEditor(self, _set_pos=self.set_pos, _set_val=self.set_val, _set_sel=self.set_sel) self._hex_editor.SetData(data) self._hex_editor.SetTitle(title) vbs.Add(self._hex_editor, 1, wx.EXPAND|wx.ALL, 5) vbs.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5) ok_btn=wx.Button(self, wx.ID_OK, 'OK') vbs.Add(ok_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) vbs.Add(self._status_bar, 0, wx.EXPAND|wx.ALL, 0) self.SetSizer(vbs) self.SetAutoLayout(True) vbs.Fit(self) def set_pos(self, pos): """Display the current buffer offset in the format of Pos: 0x12=18 """ if pos is None: s='' else: s='Pos: 0x%X=%d'%(pos, pos) self._status_bar.SetStatusText(s, self._pos_pane_index) def set_sel(self, sel_start, sel_end): if sel_start is None or sel_start==-1 or\ sel_end is None or sel_end ==-1: s='' else: sel_len=sel_end-sel_start sel_end-=1 s='Sel: 0x%X=%d to 0x%X=%d (0x%X=%d bytes)'%( sel_start, sel_start, sel_end, sel_end, sel_len, sel_len) self._status_bar.SetStatusText(s, self._sel_pane_index) def set_val(self, v): if v: # char s='Val: 0x%02X=%d'%(ord(v[0]), ord(v[0])) if len(v)>1: # short u_s=struct.unpack('<H', v[:struct.calcsize('<H')])[0] s+=' 0x%04X=%d'%(u_s, u_s) if len(v)>3: # int/long u_i=struct.unpack('<I', v[:struct.calcsize('<I')])[0] s+=' 0x%08X=%d'%(u_i, u_i) else: s='' self._status_bar.SetStatusText(s, self._val_pane_index) def set(self, data): self._hex_editor.SetData(data) if __name__=='__main__': import sys if len(sys.argv)!=2: print 'Usage:',sys.argv[0],'<File Name>' sys.exit(1) app=wx.PySimpleApp() dlg=HexEditorDialog(None, file(sys.argv[1], 'rb').read(), sys.argv[1]) if True: dlg.ShowModal() else: import hotshot f=hotshot.Profile("hexeprof",1) f.runcall(dlg.ShowModal) f.close() import hotshot.stats stats=hotshot.stats.load("hexeprof") stats.strip_dirs() # stats.sort_stats("cumulative") stats.sort_stats("time", "calls") stats.print_stats(30) dlg.Destroy() sys.exit(0)
hexeditor.py.diff
(application/octet-stream, 728 B)
Index: hexeditor.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/hexeditor.py,v
retrieving revision 1.15
diff -u -r1.15 hexeditor.py
--- hexeditor.py 15 Jun 2005 02:52:21 -0000 1.15
+++ hexeditor.py 8 Oct 2005 18:40:22 -0000
@@ -886,7 +886,10 @@
style=wx.OPEN|wx.FILE_MUST_EXIST)
if dlg.ShowModal()==wx.ID_OK:
result={}
- execfile(dlg.GetPath())
+ try:
+ execfile(dlg.GetPath())
+ except UnicodeError:
+ common.unicode_execfile(dlg.GetPath())
exist_keys={}
for i,e in enumerate(self._templates):
exist_keys[e.name]=i
makedist.py
(text/plain, 13.8 KB)
#!/usr/bin/env python ### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> ### Copyright (C) 2003-2004 Steven Palm <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### $Id: makedist.py,v 1.94 2005/07/19 06:39:06 rogerb Exp $ # Runs on Linux, Windows and Mac """Builds a binary distribution of BitPim This code runs on Windows, Linux and Mac, and will create binary distributions suitable for mass use""" import os import shutil import sys import glob import re import version def rmrf(path): """Delete directory tree like rm -rf does""" entries=os.listdir(path) for e in entries: fullname=os.path.join(path,e) if os.path.isdir(fullname): rmrf(fullname) else: os.remove(fullname) os.rmdir(path) def run(*args): """Execute the command. The path is searched""" print `args` sl=os.spawnl if sys.platform!='win32': sl=os.spawnlp ret=apply(sl, (os.P_WAIT,args[0])+args) else: # win98 was fine with above code, winxp just chokes # so we call system() instead str="" for a in args: if a.find(' ')>=0: str+=' "'+a+'"' else: str+=" "+a str=str[1:] # remove first space # If you ever wanted proof how idiotic windows is, here it is # if there is a value enclosed in double quotes, it is # taken as the window title, even if it comes after all # the switches, so i have to supply one, otherwise it mistakes # the command to run as the window title ret=os.system('start /b /wait "%s" %s' % (args[0], str)) print "returned", ret if ret!=0: raise Exception("The command failed") def sanitycheck(): "Check everything is ok" print "=== Sanity check ===" print "python version", if sys.version_info<(2,3): raise Exception("Should be at least Python 2,3 - this is "+sys.version) print " OK" print "wxPython version", import wx if wx.VERSION[:4]!=(2,6,1,0): raise Exception("Should be wxPython 2.6.1.0. This is "+`wx.VERSION`) print " OK" print "wxPython is unicode build", if not wx.USE_UNICODE: raise Exception("You need a unicode build of wxPython") print " OK" print "native.usb", import native.usb print " OK" print "pycrypto version", expect='2.0.1' import Crypto if Crypto.__version__!=expect: raise Exception("Should be %s version of pycrypto - you have %s" % (expect, Crypto.__version__)) print " OK" print "paramiko version", expect='1.4 (oddish)' import paramiko if paramiko.__version__!=expect: raise Exception("Should be %s version of paramiko - you have %s" % (expect, paramiko.__version__)) print " OK" print "bitfling", import bitfling print " OK" print "pyserial", import serial print " OK" print "apsw", import apsw ver="3.2.7-r1" if apsw.apswversion()!=ver: raise Exception("Should be apsw version %s - you have %s" % (ver, apsw.apswversion())) print " OK" print "sqlite", ver="3.2.7" if apsw.sqlitelibversion()!=ver: raise Exception("Should be sqlite version %s - you have %s" % (ver, apsw.sqlitelibversion())) print " OK" print "jaro/winkler string matcher", import native.strings.jarow print " OK" # bsddb (Linux only, for evolution) if sys.platform=="linux2": print "bsddb ", import bsddb print " OK" print "=== All checks out ===" def clean(): """Remove temporary directories created by various packaging tools""" if os.path.isdir("dist"): rmrf("dist") if os.path.isdir("build"): rmrf("build") for file in ['setup.cfg']: if os.path.isfile(file): os.remove(file) def resources(): """Get a list of the resources (images, executables, sounds etc) we ship @rtype: dict @return: The key for each entry in the dict is a directory name, and the value is a list of files within that directory""" tbl={} # list of files exts=[ '*.xy', '*.png', '*.ttf', '*.wav', '*.jpg', '*.css', '*.pdc', '*.ids'] if sys.platform=='win32': # on windows we also want the chm help file and the manifest needed to get Xp style widgets exts=exts+['*.chm', '*.manifest'] exts=exts+['helpers/*.exe','helpers/*.dll'] if sys.platform=='linux2': exts=exts+['helpers/*.lbin', '*.htb'] if sys.platform=='darwin': exts=exts+['helpers/*.mbin', '*.htb'] # list of directories to look in dirs=[ os.path.join('.', 'resources'), '.' ] # don't ship list dontship=["group.png"] # group is not used as is vx4400 specific anyway dontship.append("pvconv.exe") # Qualcomm won't answer if I can ship this for wildcard in exts: for dir in dirs: for file in glob.glob(os.path.join(dir, wildcard)): if os.path.basename(file).lower() in dontship: continue d=os.path.dirname(file) if not tbl.has_key(d): tbl[d]=[] tbl[d].append(file) files=[] for i in tbl.keys(): files.append( (i, tbl[i]) ) return files def getallencodings(): "Return the list of encodings modules" # bring in every codec in existence import encodings res=[] for f in glob.glob(os.path.join(os.path.dirname(encodings.__file__), "*.py")): v="encodings."+os.path.basename(f)[:-3] if v.endswith("__init__"): continue if v not in res: try: __import__(v) res.append(v) except: # many codecs exist, but fail on import with all sorts of random exceptions pass return res def isofficialbuild(): "Work out if this is an official build" import socket h=socket.gethostname().lower() # not built by rogerb (or stevep/n9yty) are unofficial return h in ('rh9bitpim.rogerbinns.com', "roger-sqyvr14d3", "smpbook.n9yty.com", "smpbook.local.", "rogerbmac.rogerbinns.com", "rogerbmac.local") def ensureofficial(): """If this is not an official build then ensure that version.vendor doesn't say it is""" if not isofficialbuild(): if version.vendor=="official": # it isn't official, so modify file f=open("version.py", "rt").read() newf=f.replace('vendor="official"', 'vendor="unofficial"') assert newf!=f open("version.py", "wt").write(newf) def copyresources(dest): """Copies the resources to the specified destination directory. The directory structure is preserved in the copy""" for dir,files in resources(): if not os.path.exists(os.path.join(dest,dir)): os.makedirs(os.path.join(dest,dir)) for file in files: print file shutil.copy(file, os.path.join(dest, file)) def getsubs(): """Gets the list of substitutions to be performed on the template files A partial current list is: - VERSION: The full version number of the product - OUTFILE: The filename of resulting installer (Windows specific) - NAME: The product name (in lower case) - RELEASE: The release is an increment if multiple releases are made of the same version. This corresponds to the last part of the filename for RPM packages @rtype: dict """ # Get version info import version verstr=version.version if version.testver: verstr+="-test"+`version.testver` if not isofficialbuild(): verstr+="-unofficial" filename="bitpim-"+verstr+"-setup" if sys.platform=='linux2': # linux needs all the dash bits as underscores verstr=re.sub("-", "_", verstr) res={} res['VERSION']=verstr res['OUTFILE']=filename res['NAME']=version.name.lower() res['RELEASE']=`version.release` res['COPYRIGHT']=version.copyright res['DQVERSION']=".".join([`i` for i in version.dqver]) res['DESCRIPTION']=version.description res['COPYRIGHT']=version.copyright res['URL']=version.url return res def dosubs(infile, outfile, subs): """Performs substitutions on a template file @param infile: filename to read @param outfile: filename to write resutl to @type subs: dict @param subs: the substitutions to make """ stuff=open(infile, "rt").read() for k in subs: stuff=re.sub("%%"+k+"%%", subs[k], stuff) open(outfile, "w").write(stuff) def windowsbuild(): """Do all the steps necessary to make a Windows installer""" # check libusb import win32api try: win32api.FreeLibrary(win32api.LoadLibrary("libusb0.dll")) except: raise Exception("You need libusb0.dll to be available to do a build. You only need the dll, not the rest of libusb-win32. (It doesn't have to be available on the end user system, but does need to be present to do a correct build") # clean up clean() # need setup.cfg with version info in it v=getsubs() # Build python and stuff coms=[f[:-3] for f in glob.glob("com_*.py")] run( "c:\\python23\python", "p2econfig.py", "py2exe", "-O2", "-i", ",".join(coms+getallencodings())) # Rename to correct thing os.rename("dist\\bp.exe", "dist\\bitpim.exe") # Remove files I don't want - py2exe did this correctly once for f in ("libusb0.dll", "w9xpopen.exe"): ff=os.path.join("dist", f) if os.path.isfile(ff): os.remove(ff) v=getsubs() dosubs("bitpim.iss", "bitpim-out.iss", v) filename=v['OUTFILE'] # Run innosetup run("c:\\program files\\inno setup 5\\compil32.exe", "/cc", "bitpim-out.iss") # copy to S: drive if os.path.isdir("s:\\"): shutil.copyfile("dist\\"+filename+".exe", "s:\\bprel\\"+filename+".exe") def linuxbuild(): """Do all the steps necessary to make a Linux RPM""" try: rmrf("i386") except: pass clean() os.mkdir("dist") coms=[f[:-3] for f in glob.glob("com_*.py")] cxfreezedir='/opt/cx_Freeze-3.0.1' j=os.path.join run("env", "PYTHONOPTIMIZE=2", "PATH=%s:%s" % (cxfreezedir, os.environ["PATH"]), "FreezePython", "--install-dir="+os.path.abspath("dist"), "--base-name", j(cxfreezedir,"bases","Console"), "--init-script", j(cxfreezedir,"initscripts","ConsoleSetLibPath.py"), "--include-modules", ",".join(coms+getallencodings()), "bp.py") copyresources("dist") v=getsubs() instdir="/usr/lib/%s-%s" % (v['NAME'], v['VERSION']) run("sh", "-e", "-c", "cd dist ; ../unixpkg/getallwxlibs") run("sh", "-e", "-c", "cd dist ; ../unixpkg/rpathfixup") run("sh", "-e", "-c", "cd dist ; strip *.so") run("sh", "-e", "-c", "cd dist ; tar cvf ../dist.tar *") clean() os.mkdir("dist") for f in glob.glob("unixpkg/*"): if os.path.isfile(f): shutil.copy(f, "dist") dosubs("unixpkg/bitpim.spec", "dist/bitpim.spec", v) shutil.copy("dist.tar", "dist") os.remove("dist.tar") n="%s-%s" % (v['NAME'], v['VERSION']) try: rmrf(n) except: pass os.rename("dist", n) run("tar", "cvfz", n+".tar.gz", n) rmrf(n) run("rpmbuild", "-ta", '--define=_rpmdir %s' % (os.getcwd(),), "--target", "i386-linux", n+".tar.gz") os.remove(n+".tar.gz") def macbuild(): """Do all the steps necessary to make a Mac dimg Now using the py2app stuff from Bob Ippolito <[email protected]>, the replacement for the crufty `bundlebuilder` stuff previously used for MacOS X executables. This will be the new standard for PythonMac bundling. It can be found for now at: http://undefined.org/python/#py2app """ try: rmrf("dist") except: pass clean() os.mkdir("dist") from distutils.core import setup import py2app import string name = "BitPim" iconfile = "bitpim.icns" v=getsubs() data_files=[] includePackages=[] verstr = "%s-%s" % (v['NAME'], version.versionstring) includes=[f[:-3] for f in glob.glob("com_*.py")] plist = dict( CFBundleIconFile = iconfile, CFBundleName = name, CFBundleShortVersionString = verstr, CFBundleGetInfoString = verstr, CFBundleExecutable = name, CFBundleIdentifier = 'org.bitpim.bitpim', ) opts = dict(py2app=dict( compressed=1, iconfile=iconfile, plist=plist, includes=includes, optimize=2 )) app = [ dict( script="bp.py", ),] setup( data_files = resources(), options = opts, app = app, script_args = ("py2app",) ) os.system("find dist -name '*.so' -print0 | xargs -0 strip -x") # if (os.uname()[2] >= "7.0.0"): ret=os.system('hdiutil create -srcfolder dist -volname BitPim -nouuid -noanyowners dist/PANTHER-%s.dmg' % verstr) print "image creation returned", ret else: # print "Create disk image with dist folder as the source in DiskCopy with name:\n -> JAGUAR-%s.dmg <-." % verstr ret=os.system('/usr/local/bin/buildDMG.pl -buildDir=dist -compressionLevel=9 -volName=BitPim -dmgName=JAGUAR-%s.dmg dist/BitPim.app' % verstr) print "image creation returned", ret if __name__=='__main__': # do a sanity check first sanitycheck() # ensure only official builds are marked as such ensureofficial() if sys.platform=='win32': windowsbuild() elif sys.platform=='linux2': linuxbuild() elif sys.platform=='darwin': macbuild() else: print "Unknown platform", sys.platform
makedist.py.diff
(application/octet-stream, 739 B)
Index: makedist.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/makedist.py,v
retrieving revision 1.94
diff -u -r1.94 makedist.py
--- makedist.py 19 Jul 2005 06:39:06 -0000 1.94
+++ makedist.py 8 Oct 2005 18:36:20 -0000
@@ -111,13 +111,13 @@
print "apsw",
import apsw
- ver="3.2.2-r1"
+ ver="3.2.7-r1"
if apsw.apswversion()!=ver:
raise Exception("Should be apsw version %s - you have %s" % (ver, apsw.apswversion()))
print " OK"
print "sqlite",
- ver="3.2.2"
+ ver="3.2.7"
if apsw.sqlitelibversion()!=ver:
raise Exception("Should be sqlite version %s - you have %s" % (ver, apsw.sqlitelibversion()))
print " OK"
common.py.diff
(application/octet-stream, 1.2 KB)
Index: common.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/common.py,v
retrieving revision 1.50
diff -u -r1.50 common.py
--- common.py 17 May 2005 08:39:48 -0000 1.50
+++ common.py 8 Oct 2005 18:17:44 -0000
@@ -202,9 +202,28 @@
print formatexception()
raise
+def unicode_execfile(filename, dict1=0, dict2=0):
+ # this version allows the path portion of the filename to
+ # contain non-acsii characters, the filename itself cannot contain
+ # ascii characters, execfile does not work if the filename contains
+ # non-ascii characters
+ curdir=os.getcwdu()
+ filepath, file=os.path.split(filename)
+ os.chdir(filepath)
+ if dict1==0:
+ execfile(file)
+ elif dict2==0:
+ execfile(file, dict1)
+ else:
+ execfile(file, dict1, dict2)
+ os.chdir(curdir)
+
def readversionedindexfile(filename, dict, versionhandlerfunc, currentversion):
assert currentversion>0
- execfile(filename, dict, dict)
+ try:
+ execfile(filename, dict, dict)
+ except UnicodeError:
+ unicode_execfile(filename, dict, dict)
if not dict.has_key('FILEVERSION'):
version=0
else:
common.py
(text/plain, 21.1 KB)
### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### $Id: common.py,v 1.50 2005/05/17 08:39:48 rogerb Exp $ # Documentation """Various classes and functions that are used by GUI and command line versions of BitPim""" # standard modules import string import cStringIO import StringIO import sys import traceback import tempfile import random import os class FeatureNotAvailable(Exception): """The device doesn't support the feature""" def __init__(self, device, message="The device doesn't support the feature"): Exception.__init__(self, "%s: %s" % (device, message)) self.device=device self.message=message class IntegrityCheckFailed(Exception): def __init__(self, device, message): Exception.__init__(self, "%s: %s" % (device, message)) self.device=device self.message=message class PhoneBookBusyException(Exception): "The phonebook is busy on the phone" pass class HelperBinaryNotFound(Exception): def __init__(self, basename, fullname, paths): Exception.__init__(self, "Helper binary %s not found. It should be in one of %s" % (fullname, ", ".join(paths))) self.basename=basename self.fullname=fullname self.paths=paths class CommandExecutionFailed(Exception): def __init__(self, retcode, args): Exception.__init__(self, "Command execution failed with code %d: %s" % (retcode, " ".join(args))) self.retcode=retcode self.args=args class ConversionNotSupported(Exception): def __init__(self, msg): Exception.__init__(self, msg) self.msg=msg class InSafeModeException(Exception): def __init__(self): Exception.__init__(self, "BitPim is in safe mode - this operation has been blocked") # generic comms exception and then various specialisations class CommsException(Exception): """Generic commmunications exception""" def __init__(self, message, device="<>"): Exception.__init__(self, "%s: %s" % (device, message)) self.device=device self.message=message class CommsNeedConfiguring(CommsException): """The communication settings need to be configured""" pass class CommsDeviceNeedsAttention(CommsException): """The communication port or device attached to it needs some manual intervention""" pass class CommsDataCorruption(CommsException): """There was some form of data corruption""" pass class CommsTimeout(CommsException): """Timeout while reading or writing the commport""" pass class CommsOpenFailure(CommsException): """Failed to open the communications port/device""" pass class CommsWrongPort(CommsException): """The wrong port has been selected, typically the modem port on an LG composite device""" pass class AutoPortsFailure(CommsException): """Failed to auto detect a useful port""" def __init__(self, portstried): self.device="auto" self.message="Failed to auto-detect the port to use. " if portstried is not None and len(portstried): self.message+="I tried "+", ".join(portstried) else: self.message+="I couldn't detect any candidate ports" CommsException.__init__(self, self.message, self.device) def datatohexstring(data): """Returns a pretty printed hexdump of the data @rtype: string""" res=cStringIO.StringIO() lchar="" lhex="00000000 " for count in range(0, len(data)): b=ord(data[count]) lhex=lhex+"%02x " % (b,) if b>=32 and string.printable.find(chr(b))>=0: lchar=lchar+chr(b) else: lchar=lchar+'.' if (count+1)%16==0: res.write(lhex+" "+lchar+"\n") lhex="%08x " % (count+1,) lchar="" if len(data): while (count+1)%16!=0: count=count+1 lhex=lhex+" " res.write(lhex+" "+lchar+"\n") return res.getvalue() def hexify(data): "Turns binary data into a hex string (like the output of MD5/SHA hexdigest)" return "".join(["%02x" % (ord(x),) for x in data]) def prettyprintdict(dictionary, indent=0): """Returns a pretty printed version of the dictionary The elements are sorted into alphabetical order, and printed one per line. Dictionaries within the values are also pretty printed suitably indented. @rtype: string""" res=cStringIO.StringIO() # the indent string istr=" " # opening brace res.write("%s{\n" % (istr*indent,)) indent+=1 # sort the keys keys=dictionary.keys() keys.sort() # print each key for k in keys: v=dictionary[k] # is it a dict if isinstance(v, dict): res.write("%s%s:\n%s\n" % (istr*indent, `k`, prettyprintdict(v, indent+1))) else: # is it a list of dicts? if isinstance(v, list): dicts=0 for item in v: if isinstance(item, dict): dicts+=1 if dicts and dicts==len(v): res.write("%s%s:\n%s[\n" % (istr*indent,`k`,istr*(indent+1))) for item in v: res.write(prettyprintdict(item, indent+2)) res.write("%s],\n" % (istr*(indent+1))) continue res.write("%s%s: %s,\n" % (istr*indent, `k`, `v`)) # closing brace indent-=1 if indent>0: comma="," else: comma="" res.write("%s}%s\n" % (istr*indent,comma)) return res.getvalue() class exceptionwrap: """A debugging assist class that helps in tracking down functions returning exceptions""" def __init__(self, callable): self.callable=callable def __call__(self, *args, **kwargs): try: return self.callable(*args, **kwargs) except: print "in exception wrapped call", `self.callable` print formatexception() raise def unicode_execfile(filename, dict1=0, dict2=0): # this version allows the path portion of the filename to # contain non-acsii characters, the filename itself cannot contain # ascii characters, execfile does not work if the filename contains # non-ascii characters curdir=os.getcwdu() filepath, file=os.path.split(filename) os.chdir(filepath) if dict1==0: execfile(file) elif dict2==0: execfile(file, dict1) else: execfile(file, dict1, dict2) os.chdir(curdir) def readversionedindexfile(filename, dict, versionhandlerfunc, currentversion): assert currentversion>0 try: execfile(filename, dict, dict) except UnicodeError: unicode_execfile(filename, dict, dict) if not dict.has_key('FILEVERSION'): version=0 else: version=dict['FILEVERSION'] del dict['FILEVERSION'] if version<currentversion: versionhandlerfunc(dict, version) def writeversionindexfile(filename, dict, currentversion): assert currentversion>0 f=open(filename, "w") for key in dict: v=dict[key] if isinstance(v, type({})): f.write("result['%s']=%s\n" % (key, prettyprintdict(dict[key]))) else: f.write("result['%s']=%s\n" % (key, `v`)) f.write("FILEVERSION=%d\n" % (currentversion,)) f.close() def formatexceptioneh(*excinfo): print formatexception(excinfo) def formatexception(excinfo=None, lastframes=8): """Pretty print exception, including local variable information. See Python Cookbook, recipe 14.4. @param excinfo: tuple of information returned from sys.exc_info when the exception occurred. If you don't supply this then information about the current exception being handled is used @param lastframes: local variables are shown for these number of frames @return: A pretty printed string """ if excinfo is None: excinfo=sys.exc_info() s=StringIO.StringIO() traceback.print_exception(*excinfo, **{'file': s}) tb=excinfo[2] while True: if not tb.tb_next: break tb=tb.tb_next stack=[] f=tb.tb_frame while f: stack.append(f) f=f.f_back stack.reverse() if len(stack)>lastframes: stack=stack[-lastframes:] print >>s, "\nVariables by last %d frames, innermost last" % (lastframes,) for frame in stack: print >>s, "" print >>s, "Frame %s in %s at line %s" % (frame.f_code.co_name, frame.f_code.co_filename, frame.f_lineno) for key,value in frame.f_locals.items(): # filter out modules if type(value)==type(sys): continue print >>s,"%15s = " % (key,), try: if type(value)==type({}): kk=value.keys() kk.sort() print >>s, "Keys",kk print >>s, "%15s " % ("",) , print >>s,`value`[:80] except: print >>s,"(Exception occurred printing value)" return s.getvalue() def gettempfilename(extension): "Returns a filename to be used for a temporary file" # safest Python 2.3 method x=tempfile.NamedTemporaryFile(suffix="."+extension) n=x.name x.close() del x return n def getfullname(name): """Returns the object corresponding to name. Imports will be done as necessary to resolve every part of the name""" mods=name.split('.') dict={} for i in range(len(mods)): # import everything try: exec "import %s" % (".".join(mods[:i])) in dict, dict except: pass # ok, we should have the name now return eval(name, dict, dict) def list_union(*lists): res=[] for l in lists: for item in l: if item not in res: res.append(item) return res def getkv(dict, key, updates=None): "Gets a key and value from a dict, returning as a dict potentially applying updates" d={key: dict[key].copy()} if updates: d[key].update(updates) return d # some obfuscation # obfuscate pwd _magic=[ord(x) for x in "IamAhaPp12&s]"] # the oldies are the best def obfus_encode(str): res=[] for i in range(len(str)): res.append(ord(str[i])^_magic[i%len(_magic)]) return "".join(["%02x" % (x,) for x in res]) def obfus_decode(str): res=[] for i in range(0, len(str), 2): res.append(int(str[i:i+2], 16)) x="" for i in range(len(res)): x+=chr(res[i]^_magic[i%len(_magic)]) return x # unicode byte order markers to codecs # this really should be part of the standard library # we try to import the encoding first. that has the side # effect of ensuring that the freeze tools pick up the # right bits of code as well import codecs _boms=[] # 64 bit try: import encodings.utf_64 _boms.append( (codecs.BOM64_BE, "utf_64") ) _boms.append( (codecs.BOM64_LE, "utf_64") ) except: pass # 32 bit try: import encodings.utf_32 _boms.append( (codecs.BOM_UTF32, "utf_32") ) _boms.append( (codecs.BOM_UTF32_BE, "utf_32") ) _boms.append( (codecs.BOM_UTF32_LE, "utf_32") ) except: pass # 16 bit try: import encodings.utf_16 _boms.append( (codecs.BOM_UTF16, "utf_16") ) _boms.append( (codecs.BOM_UTF16_BE, "utf_16") ) _boms.append( (codecs.BOM_UTF16_LE, "utf_16") ) except: pass # 8 bit try: import encodings.utf_8 _boms.append( (codecs.BOM_UTF8, "utf_8") ) except: pass # NB: the 32 bit and 64 bit versions have the BOM constants defined in Py 2.3 # but no corresponding encodings module. They are here for completeness. # The order of above also matters since the first ones have longer # boms than the latter ones, and we need to be unambiguous _maxbomlen=max([len(bom) for bom,codec in _boms]) def opentextfile(name): """This function detects unicode byte order markers and if present uses the codecs module instead to open the file instead with appropriate unicode decoding, else returns the file using standard open function""" f=open(name, "rb") start=f.read(_maxbomlen) for bom,codec in _boms: if start.startswith(bom): f.close() # some codecs don't do readline, so we have to vector via stringio # many postings also claim that the BOM is returned as the first # character but that hasn't been the case in my testing return StringIO.StringIO(codecs.open(name, "r", codec).read()) f.close() return open(name, "rtU") # don't you just love i18n # the following function is actually defined in guihelper and # inserted into this module. the intention is to ensure this # module doesn't have to import wx. The guihelper version # checks if wx is in unicode mode #def strorunicode(s): # if isinstance(s, unicode): return s # return str(s) def forceascii(s): if s is None: return s try: return str(s) except UnicodeEncodeError: return s.encode("ascii", 'replace') # The CRC and escaping mechanisms are the same as used in PPP, HDLC and # various other standards. pppterminator="\x7e" def pppescape(data): return data.replace("\x7d", "\x7d\x5d") \ .replace("\x7e", "\x7d\x5e") def pppunescape(d): if d.find("\x7d")<0: return d res=list(d) try: start=0 while True: p=res.index("\x7d", start) res[p:p+2]=chr(ord(res[p+1])^0x20) start=p+1 except ValueError: return "".join(res) # See http://www.repairfaq.org/filipg/LINK/F_crc_v35.html for more info # on CRC _crctable=( 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, # 0 - 7 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, # 8 - 15 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, # 16 - 23 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, # 24 - 31 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, # 32 - 39 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, # 40 - 47 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, # 48 - 55 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, # 56 - 63 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, # 64 - 71 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, # 72 - 79 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, # 80 - 87 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, # 88 - 95 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, # 96 - 103 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, # 104 - 111 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, # 112 - 119 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, # 120 - 127 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, # 128 - 135 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, # 136 - 143 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, # 144 - 151 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, # 152 - 159 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, # 160 - 167 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, # 168 - 175 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, # 176 - 183 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, # 184 - 191 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, # 192 - 199 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, # 200 - 207 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, # 208 - 215 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, # 216 - 223 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, # 224 - 231 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, # 232 - 239 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, # 240 - 247 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, # 248 - 255 ) def crc(data, initial=0xffff): "CRC calculation - returns 16 bit integer" res=initial for byte in data: curres=res res=res>>8 # zero extended val=(ord(byte)^curres) & 0xff val=_crctable[val] res=res^val res=(~res)&0xffff return res def crcs(data, initial=0xffff): "CRC calculation - returns 2 byte string LSB" r=crc(data, initial) return "%c%c" % ( r& 0xff, (r>>8)&0xff) ### ### Pathname processing (independent of host OS) ### def basename(name): if name.rfind('\\')>=0 or name.rfind('/')>=0: pos=max(name.rfind('\\'), name.rfind('/')) name=name[pos+1:] return name def stripext(name): if name.rfind('.')>=0: name=name[:name.rfind('.')] return name def getext(name): if name.rfind('.')>=0: return name[name.rfind('.')+1:] return '' #------------------------------------------------------------------------------- # number <-> string conversion routines def LSBUint16(v): if len(v)<2: return None return ord(v[0])+(ord(v[1])<<8) def LSBUint32(v): if len(v)<4: return None return ord(v[0])+(ord(v[1])<<8)+(ord(v[2])<<16)+(ord(v[3])<<24) def MSBUint16(v): if len(v)<2: return None return ord(v[1])+(ord(v[0])<<8) def MSBUint32(v): if len(v)<4: return None return ord(v[3])+(ord(v[2])<<8)+(ord(v[1])<<16)+(ord(v[0])<<24) def LSBstr16(v): return chr(v&0xff)+chr((v>>8)&0xff) def LSBstr32(v): return chr(v&0xff)+chr((v>>8)&0xff)+chr((v>>16)&0xff)+chr((v>>24)&0xff) def MSBstr16(v): return chr((v>>8)&0xff)+chr(v&0xff) def MSBstr32(v): return chr((v>>24)&0xff)+chr((v>>16)&0xff)+chr((v>>8)&0xff)+chr(v&0xff) ### ### Binary handling ### nibbles=("0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111") def tobinary(v): return nibbles[v>>4]+nibbles[v&0x0f] def frombinary(v): res=0 for i in v: res*=2 res+=bool(i=="1") return res # This could be done more efficiently through clever combinations of # masks and shifts but it is harder to write and debug (I tried :-) # and isn't any more effective on the short strings we work with def decodecharacterbits(bytes, bitsperchar, charconv=chr, terminator=None): """Decodes the characters out of a string of bytes where each character takes a fixed number of bits (eg 7) @param bytes: atring containing the raw bytes @param bitsperchar: number of bits making up each character @param charconv: the function used to convert the integer value into a character @param terminator: if this character is seen then it and the remaining bits are ignored""" bits="".join([tobinary(ord(c)) for c in bytes]) value=[] while len(bits): c=charconv(frombinary(bits[:bitsperchar])) if c==terminator: break value.append(c) bits=bits[bitsperchar:] return "".join(value) ### ### Cache information against a file ### def statinfo(filename): """Returns a simplified version of os.stat results that can be used to tell if a file has changed. The normal structure returned also has things like last access time which should not be used to tell if a file has changed.""" try: s=os.stat(filename) return (s.st_mode, s.st_ino, s.st_dev, s.st_uid, s.st_gid, s.st_size, s.st_mtime, s.st_ctime) except: return None class FileCache: def __init__(self, lowwater=100, hiwater=140): self.items={} self.hiwater=hiwater self.lowwater=lowwater def get(self, filename): v=self.items.get(filename, None) if v is None: return None si,value=v # check freshness if si==statinfo(filename): return value return None def set(self, filename, value): # we deliberately return value make this easy to use in return statement if len(self.items)>=self.hiwater: while len(self.items)>self.lowwater: del self.items[random.choice(self.items.keys())] # yes there is a race condition with statinfo changing after program # has calculated value but before calling this function self.items[filename]=statinfo(filename),value return value