0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: gui.py 4625 2008-07-01 00:21:27Z djpham $ 0009 0010 """The main gui code for BitPim""" 0011 0012 # System modules 0013 from __future__ import with_statement 0014 import contextlib 0015 import thread, threading 0016 import Queue 0017 import time 0018 import os 0019 import cStringIO 0020 import zipfile 0021 import re 0022 import sys 0023 import shutil 0024 import types 0025 import datetime 0026 import sha 0027 import codecs 0028 0029 # wx modules 0030 import wx 0031 import wx.lib.colourdb 0032 import wx.html 0033 0034 # my modules 0035 import guiwidgets 0036 import common 0037 import version 0038 import helpids 0039 import comdiagnose 0040 import phonebook 0041 import importexport 0042 import guihelper 0043 import bphtml 0044 import bitflingscan 0045 import update 0046 import phoneinfo 0047 import phone_detect 0048 import phone_media_codec 0049 import pubsub 0050 import phones.com_brew as com_brew 0051 import auto_sync 0052 import phone_root 0053 import playlist 0054 import fileview 0055 import data_recording 0056 import analyser 0057 import t9editor 0058 import newdb_wiz 0059 import bp_config 0060 0061 if guihelper.IsMSWindows(): 0062 import win32api 0063 import win32con 0064 import win32gui 0065 import msvcrt 0066 else: 0067 import fcntl 0068 0069 ### 0070 ### Used to check our threading 0071 ### 0072 mainthreadid=thread.get_ident() 0073 helperthreadid=-1 # set later 0074 0075 ### 0076 ### Used to handle Task Bar Icon feature (Windows only) 0077 ### 0078 if guihelper.IsMSWindows(): 0079 class TaskBarIcon(wx.TaskBarIcon): 0080 def __init__(self, mw): 0081 super(TaskBarIcon, self).__init__() 0082 self.mw=mw 0083 self._set_icon() 0084 wx.EVT_TASKBAR_LEFT_DCLICK(self, self.OnDclkRestore) 0085 0086 def _create_menu(self): 0087 _menu=wx.Menu() 0088 _id=wx.NewId() 0089 if self.mw.IsIconized(): 0090 _menu.Append(_id, 'Restore') 0091 wx.EVT_MENU(self, _id, self.OnRestore) 0092 else: 0093 _menu.Append(_id, 'Minimize') 0094 wx.EVT_MENU(self, _id, self.OnMinimize) 0095 _menu.AppendSeparator() 0096 _id=wx.NewId() 0097 _menu.Append(_id, 'Close') 0098 wx.EVT_MENU(self, _id, self.OnClose) 0099 return _menu 0100 0101 def _set_icon(self): 0102 _icon=wx.Icon(guihelper.getresourcefile('bitpim.ico'), 0103 wx.BITMAP_TYPE_ICO) 0104 if _icon.Ok(): 0105 self.SetIcon(_icon, 'BitPim') 0106 0107 def CreatePopupMenu(self): 0108 return self._create_menu() 0109 def OnDclkRestore(self, _): 0110 self.mw.Iconize(False) 0111 wx.PostEvent(self.mw, wx.IconizeEvent(self.mw.GetId(), False)) 0112 def OnRestore(self, _): 0113 self.mw.Iconize(False) 0114 def OnMinimize(self, _): 0115 self.mw.Iconize(True) 0116 def OnClose(self, _): 0117 self.RemoveIcon() 0118 self.mw.Close() 0119 0120 ### 0121 ### Implements a nice flexible callback object 0122 ### 0123 0124 class Callback: 0125 "Callback class. Extra arguments can be supplied at call time" 0126 def __init__(self, method, *args, **kwargs): 0127 if __debug__: 0128 global mainthreadid 0129 assert mainthreadid==thread.get_ident() 0130 self.method=method 0131 self.args=args 0132 self.kwargs=kwargs 0133 0134 def __call__(self, *args, **kwargs): 0135 if __debug__: 0136 global mainthreadid 0137 assert mainthreadid==thread.get_ident() 0138 d=self.kwargs.copy() 0139 d.update(kwargs) 0140 apply(self.method, self.args+args, d) 0141 0142 class Request: 0143 def __init__(self, method, *args, **kwargs): 0144 # created in main thread 0145 if __debug__: 0146 global mainthreadid 0147 assert mainthreadid==thread.get_ident() 0148 self.method=method 0149 self.args=args 0150 self.kwargs=kwargs 0151 0152 def __call__(self, *args, **kwargs): 0153 # called in helper thread 0154 if __debug__: 0155 global helperthreadid 0156 assert helperthreadid==thread.get_ident() 0157 d=self.kwargs.copy() 0158 d.update(kwargs) 0159 return apply(self.method, self.args+args, d) 0160 0161 0162 ### 0163 ### Event used for passing results back from helper thread 0164 ### 0165 0166 class HelperReturnEvent(wx.PyEvent): 0167 def __init__(self, callback, *args, **kwargs): 0168 if __debug__: 0169 # verify being called in comm worker thread 0170 global helperthreadid 0171 ## assert helperthreadid==thread.get_ident() 0172 global EVT_CALLBACK 0173 wx.PyEvent.__init__(self) 0174 self.SetEventType(EVT_CALLBACK) 0175 self.cb=callback 0176 self.args=args 0177 self.kwargs=kwargs 0178 0179 def __call__(self): 0180 if __debug__: 0181 global mainthreadid 0182 ## assert mainthreadid==thread.get_ident() 0183 return apply(self.cb, self.args, self.kwargs) 0184 0185 ### 0186 ### Our helper thread where all the work gets done 0187 ### 0188 0189 thesplashscreen=None # set to non-none if there is one 0190 0191 class MySplashScreen(wx.SplashScreen): 0192 def __init__(self, app, config): 0193 self.app=app 0194 # how long are we going to be up for? 0195 time=config.ReadInt("splashscreentime", 2500) 0196 if time>0: 0197 bmp=guihelper.getbitmap("splashscreen") 0198 self.drawnameandnumber(bmp) 0199 wx.SplashScreen.__init__(self, bmp, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT, 0200 time, 0201 None, -1) 0202 wx.EVT_CLOSE(self, self.OnClose) 0203 self.Show() 0204 app.Yield(True) 0205 global thesplashscreen 0206 thesplashscreen=self 0207 return 0208 # timeout is <=0 so don't show splash screen 0209 self.goforit() 0210 0211 def drawnameandnumber(self, bmp): 0212 dc=wx.MemoryDC() 0213 dc.SelectObject(bmp) 0214 # where we start writing 0215 x=23 0216 y=40 0217 # Product name 0218 if False: 0219 str=version.name 0220 dc.SetTextForeground( wx.NamedColour("MEDIUMORCHID4") ) 0221 dc.SetFont( self._gimmethedamnsizeirequested(25, wx.ROMAN, wx.NORMAL, wx.NORMAL) ) 0222 w,h=dc.GetTextExtent(str) 0223 dc.DrawText(str, x, y) 0224 y+=h+0 0225 # Version number 0226 x=58 0227 y=127 0228 str=version.versionstring+"-"+version.vendor 0229 dc.SetTextForeground( wx.NamedColour("MEDIUMBLUE") ) 0230 dc.SetFont( self._gimmethedamnsizeirequested(15, wx.ROMAN, wx.NORMAL, wx.NORMAL) ) 0231 w,h=dc.GetTextExtent(str) 0232 dc.DrawText(str, x+10, y) 0233 y+=h+0 0234 # all done 0235 dc.SelectObject(wx.NullBitmap) 0236 0237 def _gimmethedamnsizeirequested(self, ps, family, style, weight): 0238 # on Linux we have to ask for bigger than we want 0239 if guihelper.IsGtk(): 0240 ps=ps*1.6 0241 font=wx.TheFontList.FindOrCreateFont(int(ps), family, style, weight) 0242 return font 0243 0244 def goforit(self): 0245 self.app.makemainwindow() 0246 0247 def OnClose(self, evt): 0248 self.goforit() 0249 evt.Skip() 0250 0251 class BitPimExit(Exception): 0252 pass 0253 0254 class WorkerThreadFramework(threading.Thread): 0255 def __init__(self): 0256 threading.Thread.__init__(self, name="BitPim helper") 0257 self.q=Queue.Queue() 0258 0259 def setdispatch(self, dispatchto): 0260 self.dispatchto=dispatchto 0261 0262 def checkthread(self): 0263 # Function to verify we are running in the correct 0264 # thread. All functions in derived class should call this 0265 global helperthreadid 0266 assert helperthreadid==thread.get_ident() 0267 0268 def run(self): 0269 global helperthreadid 0270 helperthreadid=thread.get_ident() 0271 first=1 0272 while True: 0273 if not first: 0274 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.endbusycb)) 0275 else: 0276 first=0 0277 item=self.q.get() 0278 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.startbusycb)) 0279 call=item[0] 0280 resultcb=item[1] 0281 ex=None 0282 res=None 0283 try: 0284 res=call() 0285 except Exception,e: 0286 ex=e 0287 if not hasattr(e,"gui_exc_info"): 0288 ex.gui_exc_info=sys.exc_info() 0289 0290 wx.PostEvent(self.dispatchto, HelperReturnEvent(resultcb, ex, res)) 0291 if isinstance(ex, BitPimExit): 0292 # gracefully end this thread! 0293 break 0294 0295 def progressminor(self, pos, max, desc=""): 0296 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.progressminorcb, pos, max, desc)) 0297 0298 def progressmajor(self, pos, max, desc=""): 0299 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.progressmajorcb, pos, max, desc)) 0300 0301 def progress(self, pos, max, desc=""): 0302 self.progressminor(pos, max, desc) 0303 0304 def log(self, str): 0305 if self.dispatchto.wantlog: 0306 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.logcb, str)) 0307 0308 def logdata(self, str, data, klass=None, data_type=None): 0309 if self.dispatchto.wantlog: 0310 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.logdatacb, str, data, klass, 0311 data_type)) 0312 0313 #### 0314 #### Main application class. Runs the event loop etc 0315 #### 0316 0317 # safe mode items 0318 def _notsafefunc(*args, **kwargs): 0319 raise common.InSafeModeException() 0320 0321 class _NotSafeObject: 0322 def __getattr__(self, *args): _notsafefunc() 0323 def __setattr__(self, *args): _notsafefunc() 0324 0325 _NotSafeObject=_NotSafeObject() 0326 0327 class Event(object): 0328 """Simple Event class that supports Context Manager""" 0329 def __init__(self): 0330 self._event=threading.Event() 0331 def __enter__(self): 0332 self._event.set() 0333 def __exit__(self, exc_type, exc_value, tb): 0334 self._event.clear() 0335 def set(self): 0336 return self._event.set() 0337 def clear(self): 0338 return self._event.clear() 0339 def isSet(self): 0340 return self._event.isSet() 0341 def wait(self, timeout=None): 0342 return self._event.wait(timeout) 0343 0344 EVT_CALLBACK=None 0345 class MainApp(wx.App): 0346 def __init__(self, argv, config_filename=None): 0347 self.frame=None 0348 self.SAFEMODE=False 0349 codecs.register(phone_media_codec.search_func) 0350 self._config_filename=config_filename 0351 # simple Event object to flag when entering/leaving critical section 0352 self.critical=Event() 0353 wx.App.__init__(self, redirect=False, 0354 useBestVisual=not guihelper.IsGtk()) 0355 0356 def lock_file(self, filename): 0357 # if the file can be locked, lock it and return True. 0358 # return False otherwise. 0359 try: 0360 self.lockedfile=file(filename, 'w') 0361 except IOError: 0362 # failed to create the file, just bail 0363 self.lockedfile=None 0364 return True 0365 try: 0366 if guihelper.IsMSWindows(): 0367 msvcrt.locking(self.lockedfile.fileno(), 0368 msvcrt.LK_NBLCK, 1) 0369 else: 0370 # Linux & Mac 0371 fcntl.flock(self.lockedfile.fileno(), 0372 fcntl.LOCK_EX|fcntl.LOCK_NB) 0373 return True 0374 except IOError: 0375 return False 0376 0377 def usingsamedb(self): 0378 # using a simple file locking method 0379 return not self.lock_file(os.path.join(self.config._path, '.lock')) 0380 0381 def OnInit(self): 0382 self.made=False 0383 # Routine maintenance 0384 wx.lib.colourdb.updateColourDB() 0385 0386 # Thread stuff 0387 global mainthreadid 0388 mainthreadid=thread.get_ident() 0389 0390 # for help to save prefs 0391 cfgstr='bitpim' 0392 self.SetAppName(cfgstr) 0393 self.SetVendorName(cfgstr) 0394 0395 # Establish config stuff 0396 self.config=bp_config.Config(self._config_filename) 0397 # Check to see if we're the 2nd instance running on the same DB 0398 if self.usingsamedb(): 0399 guihelper.MessageDialog(None, 'Another copy of BitPim is using the same data dir:\n%s'%self.config._path, 0400 'BitPim Error', 0401 style=wx.OK|wx.ICON_ERROR) 0402 return False 0403 # this is for wx native use, like the freaking help controller ! 0404 self.wxconfig=wx.Config(cfgstr, style=wx.CONFIG_USE_LOCAL_FILE) 0405 0406 # safe mode is read at startup and can't be changed 0407 self.SAFEMODE=self.config.ReadInt("SafeMode", False) 0408 0409 # we used to initialise help here, but in wxPython the stupid help window 0410 # appeared on Windows just setting it up. We now defer setting it up 0411 # until it is needed 0412 self.helpcontroller=None 0413 0414 # html easy printing 0415 self.htmlprinter=bphtml.HtmlEasyPrinting(None, self.config, "printing") 0416 0417 global EVT_CALLBACK 0418 EVT_CALLBACK=wx.NewEventType() 0419 0420 # initialize the Brew file cache 0421 com_brew.file_cache=com_brew.FileCache(self.config.Read('path', '')) 0422 0423 # get the splash screen up 0424 MySplashScreen(self, self.config) 0425 0426 return True 0427 0428 def ApplySafeMode(self): 0429 # make very sure we are in safe mode 0430 if not self.SAFEMODE: 0431 return 0432 if self.frame is None: 0433 return 0434 # ensure various objects/functions are changed to not-safe 0435 objects={self.frame: 0436 ( "dlgsendphone", "OnDataSendPhone", "OnDataSendPhoneGotFundamentals", "OnDataSendPhoneResults"), 0437 self.frame.tree.filesystemwidget: 0438 ( "OnFileDelete", "OnFileOverwrite", "OnNewSubdir", "OnNewFile", "OnDirDelete", "OnRestore"), 0439 self.frame.wt: 0440 ( "senddata", "writewallpaper", "writeringtone", "writephonebook", "writecalendar", "rmfile", 0441 "writefile", "mkdir", "rmdir", "rmdirs", "restorefiles" ), 0442 self.frame.phoneprofile: 0443 ( "convertphonebooktophone", ), 0444 self.frame.phonemodule.Phone: 0445 ( "mkdir", "mkdirs", "rmdir", "rmfile", "rmdirs", "writefile", "savegroups", "savephonebook", 0446 "savecalendar", "savewallpapers", "saveringtones") 0447 } 0448 0449 for obj, names in objects.iteritems(): 0450 if obj is None: 0451 continue 0452 for name in names: 0453 field=getattr(obj, name, None) 0454 if field is None or field is _notsafefunc or field is _NotSafeObject: 0455 continue 0456 if isinstance(field, (types.MethodType, types.FunctionType)): 0457 newval=_notsafefunc 0458 else: newval=_NotSafeObject 0459 setattr(obj, name, newval) 0460 0461 # remove various menu items if we can find them 0462 removeids=(guihelper.ID_DATASENDPHONE, guihelper.ID_FV_OVERWRITE, guihelper.ID_FV_NEWSUBDIR, 0463 guihelper.ID_FV_NEWFILE, guihelper.ID_FV_DELETE, guihelper.ID_FV_RENAME, 0464 guihelper.ID_FV_RESTORE, guihelper.ID_FV_ADD) 0465 mb=self.frame.GetMenuBar() 0466 menus=[mb.GetMenu(i) for i in range(mb.GetMenuCount())] 0467 fsw=self.frame.tree.filesystemwidget 0468 if fsw is not None: 0469 menus.extend( [fsw.list.filemenu, fsw.tree.dirmenu, fsw.list.genericmenu] ) 0470 for menu in menus: 0471 for id in removeids: 0472 item=menu.FindItemById(id) 0473 if item is not None: 0474 menu.RemoveItem(item) 0475 0476 0477 0478 0479 ## def setuphelpiwant(self): 0480 ## """This is how the setuphelp code is supposed to be, but stuff is missing from wx""" 0481 ## self.helpcontroller=wx.BestHelpController() 0482 ## self.helpcontroller.Initialize(gethelpfilename) 0483 0484 def _setuphelp(self): 0485 """Does all the nonsense to get help working""" 0486 if guihelper.IsMSWindows(): 0487 self.helpcontroller=True 0488 return 0489 elif guihelper.IsMac(): 0490 # we use apple's help mechanism 0491 from Carbon import AH 0492 path=os.path.abspath(os.path.join(guihelper.resourcedirectory, "..", "..", "..")) 0493 # path won't exist if we aren't a bundle 0494 if os.path.exists(path) and path.endswith(".app"): 0495 res=AH.AHRegisterHelpBook(path) 0496 self.helpcontroller=True 0497 return 0498 0499 # Standard WX style help 0500 # htmlhelp isn't correctly wrapper in wx package 0501 # Add the Zip filesystem 0502 wx.FileSystem_AddHandler(wx.ZipFSHandler()) 0503 # Get the help working 0504 self.helpcontroller=wx.html.HtmlHelpController() 0505 self.helpcontroller.AddBook(guihelper.gethelpfilename()+".htb") 0506 self.helpcontroller.UseConfig(self.wxconfig, "help") 0507 0508 # now context help 0509 # (currently borken) 0510 # self.helpprovider=wx.HelpControllerHelpProvider(self.helpcontroller) 0511 # wx.HelpProvider_Set(provider) 0512 0513 def displayhelpid(self, id): 0514 """Display a specific Help Topic""" 0515 if self.helpcontroller is None: 0516 self._setuphelp() 0517 0518 if guihelper.IsMSWindows(): 0519 import win32help 0520 fname=guihelper.gethelpfilename()+".chm>Help" 0521 if id is None: 0522 id=helpids.ID_WELCOME 0523 # display the topic 0524 _hwnd=win32gui.GetDesktopWindow() 0525 win32help.HtmlHelp(_hwnd, fname, win32help.HH_DISPLAY_TOPIC, id) 0526 # and sync the TOC 0527 win32help.HtmlHelp(_hwnd, fname, win32help.HH_SYNC, id) 0528 0529 elif guihelper.IsMac() and self.helpcontroller is True: 0530 from Carbon import AH 0531 res=AH.AHGotoPage('BitPim Help', id, None) 0532 0533 else: 0534 if id is None: 0535 self.helpcontroller.DisplayContents() 0536 else: 0537 self.helpcontroller.Display(id) 0538 0539 def makemainwindow(self): 0540 if self.made: 0541 return # already been called 0542 self.made=True 0543 # make the main frame 0544 title='BitPim' 0545 name=self.config.Read('name', None) 0546 if name: 0547 title+=' - '+name 0548 self.frame=MainWindow(None, -1, title, self.config) 0549 self.frame.Connect(-1, -1, EVT_CALLBACK, self.frame.OnCallback) 0550 if guihelper.IsMac(): 0551 self.frame.MacSetMetalAppearance(True) 0552 0553 # make the worker thread 0554 wt=WorkerThread() 0555 wt.setdispatch(self.frame) 0556 wt.setDaemon(1) 0557 wt.start() 0558 self.frame.wt=wt 0559 self.SetTopWindow(self.frame) 0560 self.SetExitOnFrameDelete(True) 0561 self.ApplySafeMode() 0562 wx.CallAfter(self.CheckDetectPhone) 0563 wx.CallAfter(self.CheckUpdate) 0564 # double-check the locked file 0565 if self.lockedfile is None: 0566 self.usingsamedb() 0567 0568 update_delta={ 'Daily': 1, 'Weekly': 7, 'Monthly': 30 } 0569 def CheckUpdate(self): 0570 if version.isdevelopmentversion(): 0571 return 0572 if self.frame is None: 0573 return 0574 # tell the frame to do a check-for-update 0575 update_rate=self.config.Read('updaterate', '') 0576 if not len(update_rate) or update_rate =='Never': 0577 return 0578 last_update=self.config.Read('last_update', '') 0579 try: 0580 if len(last_update): 0581 last_date=datetime.date(int(last_update[:4]), int(last_update[4:6]), 0582 int(last_update[6:])) 0583 next_date=last_date+datetime.timedelta(\ 0584 self.update_delta.get(update_rate, 7)) 0585 else: 0586 next_date=last_date=datetime.date.today() 0587 except ValueError: 0588 # month day swap problem 0589 next_date=last_date=datetime.date.today() 0590 if datetime.date.today()<next_date: 0591 return 0592 self.frame.AddPendingEvent(\ 0593 wx.PyCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, 0594 guihelper.ID_HELP_UPDATE)) 0595 0596 def CheckDetectPhone(self): 0597 if self.config.ReadInt('autodetectstart', 0) or self.frame.needconfig: 0598 self.frame.AddPendingEvent( 0599 wx.PyCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, 0600 guihelper.ID_EDITDETECT)) 0601 0602 def OnExit(self): 0603 self.config.Flush() 0604 # we get stupid messages about daemon threads, and Python's library 0605 # doesn't provide any way to interrupt them, nor to suppress these 0606 # messages. ::TODO:: maybe remove the onexit handler installed by 0607 # treading._MainThread 0608 sys.excepthook=donothingexceptionhandler 0609 0610 def ExitMainLoop(self): 0611 if guihelper.IsGtk(): 0612 # This hangs for GTK, so what the heck! 0613 self.OnExit() 0614 sys.exit(0) 0615 super(MainApp, self).ExitMainLoop() 0616 0617 # do nothing exception handler 0618 def donothingexceptionhandler(*args): 0619 pass 0620 0621 # Entry point 0622 def run(argv, kwargs): 0623 return MainApp(argv, **kwargs).MainLoop() 0624 0625 ### 0626 ### Main Window (frame) class 0627 ### 0628 0629 class MenuCallback: 0630 "A wrapper to help with callbacks that ignores arguments when invoked" 0631 def __init__(self, func, *args, **kwargs): 0632 self.func=func 0633 self.args=args 0634 self.kwargs=kwargs 0635 0636 def __call__(self, *args): 0637 return self.func(*self.args, **self.kwargs) 0638 0639 class MainWindow(wx.Frame): 0640 def __init__(self, parent, id, title, config): 0641 wx.Frame.__init__(self, parent, id, title, 0642 style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE) 0643 wx.GetApp().frame=self 0644 0645 wx.GetApp().htmlprinter.SetParentFrame(self) 0646 0647 sys.excepthook=Callback(self.excepthook) 0648 ### plumbing, callbacks 0649 self.wt=None # worker thread 0650 self.progressminorcb=Callback(self.OnProgressMinor) 0651 self.progressmajorcb=Callback(self.OnProgressMajor) 0652 self.logcb=Callback(self.OnLog) 0653 self.logdatacb=Callback(self.OnLogData) 0654 self.startbusycb=Callback(self.OnBusyStart) 0655 self.endbusycb=Callback(self.OnBusyEnd) 0656 self.queue=Queue.Queue() 0657 0658 ### random variables 0659 self.exceptiondialog=None 0660 self.wantlog=1 # do we want to receive log information 0661 self.config=config 0662 self.progmajortext="" 0663 self.__owner_name='' 0664 0665 self._taskbar=None 0666 self._taskbar_on_closed=False 0667 self._close_button=False 0668 self.__phone_detect_at_startup=False 0669 self._autodetect_delay=0 0670 self._dr_rec=None # Data Recording 0671 self._dr_play=None # Data Play back 0672 0673 ### Status bar 0674 0675 sb=guiwidgets.MyStatusBar(self) 0676 self.SetStatusBar(sb) 0677 self.SetStatusBarPane(sb.GetHelpPane()) 0678 0679 ### Art 0680 # establish the custom art provider for custom icons 0681 # this is a global setting, so no need to call it for each toolbar 0682 wx.ArtProvider.PushProvider(guihelper.ArtProvider()) 0683 0684 # frame icon 0685 ib=wx.IconBundle() 0686 ib.AddIconFromFile(guihelper.getresourcefile("bitpim.ico"), wx.BITMAP_TYPE_ANY) 0687 self.SetIcons(ib) 0688 0689 ### Menubar 0690 0691 menuBar = wx.MenuBar() 0692 menu = wx.Menu() 0693 # menu.Append(guihelper.ID_FILENEW, "&New", "Start from new") 0694 # menu.Append(guihelper.ID_FILEOPEN, "&Open", "Open a file") 0695 # menu.Append(guihelper.ID_FILESAVE, "&Save", "Save your work") 0696 menu.Append(guihelper.ID_FILEPRINT, "&Print...", "Print phonebook") 0697 # menu.AppendSeparator() 0698 0699 # imports 0700 impmenu=wx.Menu() 0701 for x, desc, help, func in importexport.GetPhonebookImports(): 0702 if isinstance(func, tuple): 0703 # submenu 0704 _submenu=wx.Menu() 0705 for _id, _desc, _help, _func in func: 0706 _submenu.Append(_id, _desc, _help) 0707 if _func: 0708 wx.EVT_MENU(self, _id, MenuCallback(_func, self)) 0709 impmenu.AppendMenu(x, desc, _submenu, help) 0710 else: 0711 impmenu.Append(x, desc, help) 0712 wx.EVT_MENU(self, x, MenuCallback(func, self) ) 0713 0714 menu.AppendMenu(guihelper.ID_FILEIMPORT, "&Import", impmenu) 0715 0716 # exports 0717 expmenu=wx.Menu() 0718 for x, desc, help, func in importexport.GetPhonebookExports(): 0719 expmenu.Append(x, desc, help) 0720 wx.EVT_MENU(self, x, MenuCallback(func, self) ) 0721 0722 menu.AppendMenu(guihelper.ID_FILEEXPORT, "&Export", expmenu) 0723 0724 if not guihelper.IsMac(): 0725 menu.AppendSeparator() 0726 menu.Append(guihelper.ID_FILEEXIT, "E&xit", "Close down this program") 0727 menuBar.Append(menu, "&File"); 0728 self.__menu_edit=menu=wx.Menu() 0729 menu.Append(guihelper.ID_EDITSELECTALL, "&Select All\tCtrl+A", "Select All") 0730 menu.AppendSeparator() 0731 menu.Append(guihelper.ID_EDITADDENTRY, "&New...\tCtrl+N", "Add an item") 0732 menu.Append(guihelper.ID_EDITCOPY, "&Copy\tCtrl+C", "Copy to the clipboard") 0733 menu.Append(guihelper.ID_EDITPASTE,"&Paste\tCtrl+V", "Paste from the clipboard") 0734 menu.Append(guihelper.ID_EDITDELETEENTRY, "&Delete", "Delete currently selected entry") 0735 menu.Append(guihelper.ID_EDITRENAME, "&Rename\tF2", "Rename currently selected entry") 0736 menu.AppendSeparator() 0737 menu.Append(guihelper.ID_EDITDETECT, 0738 "D&etect Phone", "Auto Detect Phone") 0739 if guihelper.IsMac(): 0740 wx.App_SetMacPreferencesMenuItemId(guihelper.ID_EDITSETTINGS) 0741 menu.Append(guihelper.ID_EDITSETTINGS, "Pre&ferences...", "Edit Settings") 0742 else: 0743 menu.AppendSeparator() 0744 menu.Append(guihelper.ID_EDITSETTINGS, "&Settings", "Edit settings") 0745 menuBar.Append(menu, "&Edit"); 0746 0747 menu=wx.Menu() 0748 menu.Append(guihelper.ID_DATAGETPHONE, "Get Phone &Data ...", "Loads data from the phone") 0749 menu.Append(guihelper.ID_DATASENDPHONE, "&Send Phone Data ...", "Sends data to the phone") 0750 menu.Append(guihelper.ID_DATAHISTORICAL, "&Historical Data ...", "View Current & Historical Data") 0751 menu.AppendSeparator() 0752 menu.Append(guihelper.ID_DATANEWDB, 'Create New Storage ...', 0753 'Create a New BitPim Storage Area') 0754 menuBar.Append(menu, "&Data") 0755 0756 menu=wx.Menu() 0757 menu.Append(guihelper.ID_VIEWCOLUMNS, "&Columns ...", "Which columns to show") 0758 menu.AppendCheckItem(guihelper.ID_VIEWPREVIEW, "&Phonebook Preview", "Toggle Phonebook Preview Pane") 0759 menu.AppendSeparator() 0760 menu.AppendCheckItem(guihelper.ID_VIEWLOGDATA, "&View protocol logging", "View protocol logging information") 0761 menu.Append(guihelper.ID_VIEWCLEARLOGS, "Clear &Logs", "Clears the contents of the log panes") 0762 menu.AppendSeparator() 0763 menu.AppendCheckItem(guihelper.ID_VIEWFILESYSTEM, "View &Filesystem", "View filesystem on the phone") 0764 menu.AppendSeparator() 0765 menu.Append(guihelper.ID_EDITPHONEINFO, 0766 "Phone &Info", "Display Phone Information") 0767 menuBar.Append(menu, "&View") 0768 # Debug menu 0769 menu=wx.Menu() 0770 menu.Append(guihelper.ID_DR_SETTINGS, '&Data Recording', 0771 'Data Recording Settings') 0772 ## menu.Append(guihelper.ID_DEBUG_SCRIPT, '&Script', 0773 ## 'Run Debug Script') 0774 menuBar.Append(menu, "De&bug") 0775 # Help menu 0776 menu=wx.Menu() 0777 if guihelper.IsMac(): 0778 menu.Append(guihelper.ID_HELPHELP, "&Help on this panel", "Help for the panel you are looking at") 0779 else: 0780 menu.Append(guihelper.ID_HELPHELP, "&Help", "Help for the panel you are looking at") 0781 menu.Append(guihelper.ID_HELPTOUR, "&Tour", "Tour of BitPim") 0782 menu.Append(guihelper.ID_HELPCONTENTS, "&Contents", "Table of contents for the online help") 0783 menu.Append(guihelper.ID_HELPHOWTOS, "H&owTos", "Help on how to do certain function") 0784 menu.Append(guihelper.ID_HELPFAQ, "&FAQ", "Frequently Asked Questions") 0785 menu.Append(guihelper.ID_HELPSUPPORT, "&Support", "Getting support for BitPim") 0786 menu.Append(guihelper.ID_HELPPHONE, "Your &Phone", "Help on specific phonemodel") 0787 if version.vendor=='official': 0788 menu.AppendSeparator() 0789 menu.Append(guihelper.ID_HELP_UPDATE, "&Check for Update", "Check for any BitPim Update") 0790 if guihelper.IsMac(): 0791 wx.App_SetMacAboutMenuItemId(guihelper.ID_HELPABOUT) 0792 menu.Append(guihelper.ID_HELPABOUT, "&About BitPim", "Display program information") 0793 wx.App_SetMacHelpMenuTitleName("&Help") 0794 wx.App_SetMacExitMenuItemId(guihelper.ID_FILEEXIT) 0795 else: 0796 menu.AppendSeparator() 0797 menu.Append(guihelper.ID_HELPABOUT, "&About", "Display program information") 0798 menuBar.Append(menu, "&Help"); 0799 self.SetMenuBar(menuBar) 0800 0801 ### toolbar 0802 self.tb=self.CreateToolBar(wx.TB_HORIZONTAL) 0803 self.tb.SetToolBitmapSize(wx.Size(32,32)) 0804 sz=self.tb.GetToolBitmapSize() 0805 0806 # add and delete tools 0807 self.tb.AddSimpleTool(guihelper.ID_DATAGETPHONE, wx.ArtProvider.GetBitmap(guihelper.ART_DATAGETPHONE, wx.ART_TOOLBAR, sz), 0808 "Get Phone Data", "Synchronize BitPim with Phone") 0809 self.tb.AddLabelTool(guihelper.ID_DATASENDPHONE, "Send Phone Data", wx.ArtProvider.GetBitmap(guihelper.ART_DATASENDPHONE, wx.ART_TOOLBAR, sz), 0810 shortHelp="Send Phone Data", longHelp="Synchronize Phone with BitPim") 0811 self.tb.AddLabelTool(guihelper.ID_DATAHISTORICAL, "BitPim Help", wx.ArtProvider.GetBitmap(guihelper.ART_DATAHISTORICAL, wx.ART_TOOLBAR, sz), 0812 shortHelp="Historical Data", longHelp="Show Historical Data") 0813 self.tb.AddSeparator() 0814 self.tb.AddLabelTool(guihelper.ID_EDITADDENTRY, "Add", wx.ArtProvider.GetBitmap(wx.ART_ADD_BOOKMARK, wx.ART_TOOLBAR, sz), 0815 shortHelp="Add", longHelp="Add an item") 0816 self.tb.AddLabelTool(guihelper.ID_EDITDELETEENTRY, "Delete", wx.ArtProvider.GetBitmap(wx.ART_DEL_BOOKMARK, wx.ART_TOOLBAR, sz), 0817 shortHelp="Delete", longHelp="Delete item") 0818 self.tb.AddLabelTool(guihelper.ID_EDITPHONEINFO, "Phone Info", wx.ArtProvider.GetBitmap(guihelper.ART_EDITPHONEINFO, wx.ART_TOOLBAR, sz), 0819 shortHelp="Phone Info", longHelp="Show Phone Info") 0820 self.tb.AddLabelTool(guihelper.ID_EDITDETECT, "Find Phone", wx.ArtProvider.GetBitmap(guihelper.ART_EDITDETECT, wx.ART_TOOLBAR, sz), 0821 shortHelp="Find Phone", longHelp="Find Phone") 0822 self.tb.AddLabelTool(guihelper.ID_EDITSETTINGS, "Edit Settings", wx.ArtProvider.GetBitmap(guihelper.ART_EDITSETTINGS, wx.ART_TOOLBAR, sz), 0823 shortHelp="Edit Settings", longHelp="Edit BitPim Settings") 0824 self.tb.AddSeparator() 0825 self.tb.AddSimpleTool(guihelper.ID_AUTOSYNCEXECUTE, wx.ArtProvider.GetBitmap(guihelper.ART_AUTOSYNCEXECUTE, wx.ART_TOOLBAR, sz), 0826 "Autosync Calendar", "Synchronize Phone Calendar with PC") 0827 self.tb.AddSeparator() 0828 self.tb.AddLabelTool(guihelper.ID_HELPHELP, "BitPim Help", wx.ArtProvider.GetBitmap(guihelper.ART_HELPHELP, wx.ART_TOOLBAR, sz), 0829 shortHelp="BitPim Help", longHelp="BitPim Help") 0830 0831 0832 # You have to make this call for the toolbar to draw itself properly 0833 self.tb.Realize() 0834 0835 ### persistent dialogs 0836 self.dlggetphone=guiwidgets.GetPhoneDialog(self, "Get Data from Phone") 0837 self.dlgsendphone=guiwidgets.SendPhoneDialog(self, "Send Data to Phone") 0838 0839 # the splitter 0840 self.sw=wx.SplitterWindow(self, wx.NewId(), style=wx.SP_3D|wx.SP_NO_XP_THEME|wx.SP_LIVE_UPDATE) 0841 0842 ### create main tree view 0843 self.tree = phone_root.PhoneTree(self.sw, self, wx.NewId()) 0844 0845 ### Events we handle 0846 wx.EVT_MENU(self, guihelper.ID_FILEPRINT, self.tree.OnFilePrint) 0847 wx.EVT_MENU(self, guihelper.ID_FILEEXIT, self.OnExit) 0848 wx.EVT_MENU(self, guihelper.ID_EDITSETTINGS, self.OnEditSettings) 0849 wx.EVT_MENU(self, guihelper.ID_DATAGETPHONE, self.OnDataGetPhone) 0850 wx.EVT_MENU(self, guihelper.ID_DATASENDPHONE, self.OnDataSendPhone) 0851 wx.EVT_MENU(self, guihelper.ID_DATAHISTORICAL, self.tree.OnDataHistorical) 0852 wx.EVT_MENU(self, guihelper.ID_DATANEWDB, self.OnNewDB) 0853 wx.EVT_MENU(self, guihelper.ID_VIEWCOLUMNS, self.tree.OnViewColumns) 0854 wx.EVT_MENU(self, guihelper.ID_VIEWPREVIEW, self.tree.OnViewPreview) 0855 wx.EVT_MENU(self, guihelper.ID_VIEWCLEARLOGS, self.tree.OnViewClearLogs) 0856 wx.EVT_MENU(self, guihelper.ID_VIEWLOGDATA, self.tree.OnViewLogData) 0857 wx.EVT_MENU(self, guihelper.ID_VIEWFILESYSTEM, self.tree.OnViewFilesystem) 0858 wx.EVT_MENU(self, guihelper.ID_EDITADDENTRY, self.tree.OnEditAddEntry) 0859 wx.EVT_MENU(self, guihelper.ID_EDITDELETEENTRY, self.tree.OnEditDeleteEntry) 0860 wx.EVT_MENU(self, guihelper.ID_EDITSELECTALL, self.tree.OnEditSelectAll) 0861 wx.EVT_MENU(self, guihelper.ID_EDITCOPY, self.tree.OnCopyEntry) 0862 wx.EVT_MENU(self, guihelper.ID_EDITPASTE, self.tree.OnPasteEntry) 0863 wx.EVT_MENU(self, guihelper.ID_EDITRENAME, self.tree.OnRenameEntry) 0864 wx.EVT_MENU(self, guihelper.ID_HELPABOUT, self.OnHelpAbout) 0865 wx.EVT_MENU(self, guihelper.ID_HELPHELP, self.OnHelpHelp) 0866 wx.EVT_MENU(self, guihelper.ID_HELPCONTENTS, self.OnHelpContents) 0867 wx.EVT_MENU(self, guihelper.ID_HELPHOWTOS, self.OnHelpHowtos) 0868 wx.EVT_MENU(self, guihelper.ID_HELPFAQ, self.OnHelpFAQ) 0869 wx.EVT_MENU(self, guihelper.ID_HELPSUPPORT, self.OnHelpSupport) 0870 wx.EVT_MENU(self, guihelper.ID_HELPTOUR, self.OnHelpTour) 0871 wx.EVT_MENU(self, guihelper.ID_HELP_UPDATE, self.OnCheckUpdate) 0872 wx.EVT_MENU(self, guihelper.ID_HELPPHONE, self.OnHelpPhone) 0873 wx.EVT_MENU(self, guihelper.ID_EDITPHONEINFO, self.OnPhoneInfo) 0874 wx.EVT_MENU(self, guihelper.ID_EDITDETECT, self.OnDetectPhone) 0875 wx.EVT_MENU(self, guihelper.ID_AUTOSYNCSETTINGS, self.OnAutoSyncSettings) 0876 wx.EVT_MENU(self, guihelper.ID_AUTOSYNCEXECUTE, self.OnAutoSyncExecute) 0877 wx.EVT_MENU(self, guihelper.ID_DR_SETTINGS, self.OnDataRecording) 0878 wx.EVT_CLOSE(self, self.OnClose) 0879 0880 ### Double check our size is meaningful, and make bigger 0881 ### if necessary (especially needed on Mac and Linux) 0882 if min(self.GetSize())<250: 0883 self.SetSize( (640, 480) ) 0884 0885 ### Is config set? 0886 self.configdlg=guiwidgets.ConfigDialog(self, self) 0887 self.needconfig=self.configdlg.needconfig() 0888 self.configdlg.updatevariables() 0889 0890 pos=self.config.ReadInt("mainwindowsplitterpos", 200) 0891 self.tree.active_panel.OnPreActivate() 0892 self.sw.SplitVertically(self.tree, self.tree.active_panel, pos) 0893 self.tree.active_panel.OnPostActivate() 0894 self.sw.SetMinimumPaneSize(50) 0895 wx.EVT_SPLITTER_SASH_POS_CHANGED(self, id, self.OnSplitterPosChanged) 0896 self.tree.Expand(self.tree.root) 0897 0898 # multiple phones can be added here, although we have to figure out which phone 0899 # to use in send/get phone data. 0900 self.tree.CreatePhone("Phone", self.config, self.configpath, "bitpim.db") 0901 #self.tree.CreatePhone("Different database", self.config, "C:/Documents and Settings/Simon/My Documents/bitpim_old") 0902 0903 ### Set autosync settings dialog 0904 self.calenders=importexport.GetCalenderAutoSyncImports() 0905 self.autosyncsetting=auto_sync.AutoSyncSettingsDialog(self, self) 0906 self.autosyncsetting.updatevariables() 0907 self.CloseSplashScreen() 0908 0909 # add update handlers for controls that are not always available 0910 wx.EVT_UPDATE_UI(self, guihelper.ID_AUTOSYNCEXECUTE, self.AutosyncUpdateUIEvent) 0911 wx.EVT_UPDATE_UI(self, guihelper.ID_DATASENDPHONE, self.tree.DataSendPhoneUpdateUIEvent) 0912 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITDELETEENTRY, self.tree.DataDeleteItemUpdateUIEvent) 0913 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITADDENTRY, self.tree.DataAddItemUpdateUIEvent) 0914 wx.EVT_UPDATE_UI(self, guihelper.ID_DATAHISTORICAL, self.tree.HistoricalDataUpdateUIEvent) 0915 wx.EVT_UPDATE_UI(self, guihelper.ID_VIEWCOLUMNS, self.tree.ViewColumnsUpdateUIEvent) 0916 wx.EVT_UPDATE_UI(self, guihelper.ID_VIEWPREVIEW, self.tree.ViewPreviewDataUpdateUIEvent) 0917 wx.EVT_UPDATE_UI(self, guihelper.ID_FILEPRINT, self.tree.FilePrintDataUpdateUIEvent) 0918 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITSELECTALL, self.tree.SelectAllDataUpdateUIEvent) 0919 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITCOPY, self.tree.EditCopyUpdateUIEvent) 0920 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITPASTE, self.tree.EditPasteUpdateUIEvent) 0921 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITRENAME, self.tree.EditRenameUpdateUIEvent) 0922 wx.EVT_UPDATE_UI(self, guihelper.ID_VIEWLOGDATA, self.tree.ViewLogDataUIEvent) 0923 wx.EVT_UPDATE_UI(self, guihelper.ID_VIEWFILESYSTEM, self.tree.ViewFileSystemUIEvent) 0924 wx.EVT_UPDATE_UI(self, guihelper.ID_HELPPHONE, self.OnHelpPhoneUpdateUI) 0925 0926 # Retrieve saved settings... Use 90% of screen if not specified 0927 guiwidgets.set_size("MainWin", self, screenpct=90) 0928 0929 ### Lets go visible 0930 self.Show() 0931 0932 # Show tour on first use 0933 if self.config.ReadInt("firstrun", True): 0934 self.config.WriteInt("firstrun", False) 0935 self.config.Flush() 0936 wx.CallAfter(self.OnHelpTour) 0937 0938 # check for device changes 0939 if guihelper.IsMSWindows(): 0940 if self.config.ReadInt('taskbaricon', 0): 0941 self._taskbar=TaskBarIcon(self) 0942 self._taskbar_on_closed=self.config.ReadInt('taskbaricon1', 0) 0943 # save the old window proc 0944 self.oldwndproc = win32gui.SetWindowLong(self.GetHandle(), 0945 win32con.GWL_WNDPROC, 0946 self.MyWndProc) 0947 if self._taskbar and self._taskbar.IsOk(): 0948 wx.EVT_ICONIZE(self, self.OnIconize) 0949 0950 # response to pubsub request 0951 pubsub.subscribe(self.OnReqChangeTab, pubsub.REQUEST_TAB_CHANGED) 0952 # setup the midnight timer 0953 self._setup_midnight_timer() 0954 0955 if self.IsIconized() and self._taskbar: 0956 # Ugly hack to force the icon onto the system tray when 0957 # the app is started minimized !! 0958 wx.CallAfter(self.Show, False) 0959 self.GetStatusBar().set_app_status_ready() 0960 # Linux USB port notification 0961 if guihelper.IsGtk(): 0962 import comm_notify 0963 comm_notify.start_server(self) 0964 0965 def OnSplitterPosChanged(self,_): 0966 pos=self.sw.GetSashPosition() 0967 self.config.WriteInt("mainwindowsplitterpos", pos) 0968 0969 def SetActivePanel(self, panel): 0970 w2=self.sw.GetWindow2() 0971 if w2 is None or w2 is panel: 0972 return 0973 panel.OnPreActivate() 0974 w2.Show(False) 0975 self.sw.ReplaceWindow(w2, panel) 0976 panel.Show(True) 0977 panel.SetFocus() 0978 panel.OnPostActivate() 0979 0980 def GetActiveMemoWidget(self): 0981 return self.tree.GetActivePhone().memowidget 0982 0983 def GetActiveMediaWidget(self): 0984 return self.tree.GetActivePhone().mediawidget 0985 0986 def GetActiveRingerWidget(self): 0987 return self.tree.GetActivePhone().mediawidget.GetRinger() 0988 0989 def GetActiveWallpaperWidget(self): 0990 return self.tree.GetActivePhone().mediawidget.GetWallpaper() 0991 0992 def GetActiveTodoWidget(self): 0993 return self.tree.GetActivePhone().todowidget 0994 0995 def GetActiveCalendarWidget(self): 0996 return self.tree.GetActivePhone().calendarwidget 0997 0998 def GetActivePlaylistWidget(self): 0999 return self.tree.GetActivePhone().playlistwidget 1000 1001 def GetActivePhonebookWidget(self): 1002 return self.tree.GetActivePhone().phonewidget 1003 1004 def GetActiveCallHistoryWidget(self): 1005 return self.tree.GetActivePhone().callhistorywidget 1006 1007 def GetActiveSMSWidget(self): 1008 return self.tree.GetActivePhone().smswidget 1009 1010 def GetActiveT9EditorWidget(self): 1011 return self.tree.GetActivePhone().t9editorwidget 1012 1013 def GetCurrentActiveWidget(self): 1014 return self.tree.GetActiveWidget() 1015 1016 def GetActiveDatabase(self): 1017 return self.tree.GetActivePhone().GetDatabase() 1018 1019 def UpdateToolbarOnPanelChange(self, add_image, add_help, delete_image, delete_help): 1020 sz=self.tb.GetToolBitmapSize() 1021 pos=self.GetToolBar().GetToolPos(guihelper.ID_EDITADDENTRY) 1022 self.GetToolBar().DeleteTool(guihelper.ID_EDITADDENTRY) 1023 self.tooladd=self.tb.InsertLabelTool(pos, guihelper.ID_EDITADDENTRY, add_help, 1024 wx.ArtProvider.GetBitmap(add_image, wx.ART_TOOLBAR, sz), 1025 shortHelp=add_help, longHelp="Add an item") 1026 pos=self.GetToolBar().GetToolPos(guihelper.ID_EDITDELETEENTRY) 1027 self.GetToolBar().DeleteTool(guihelper.ID_EDITDELETEENTRY) 1028 self.tooldelete=self.tb.InsertLabelTool(pos, guihelper.ID_EDITDELETEENTRY, delete_help, 1029 wx.ArtProvider.GetBitmap(delete_image, wx.ART_TOOLBAR, sz), 1030 shortHelp=delete_help, longHelp="Delete item") 1031 self.tb.Realize() 1032 1033 1034 def CloseSplashScreen(self): 1035 ### remove splash screen if there is one 1036 global thesplashscreen 1037 if thesplashscreen is not None: 1038 try: 1039 # on Linux this is often already deleted and generates an exception 1040 thesplashscreen.Show(False) 1041 except: 1042 pass 1043 thesplashscreen=None 1044 wx.SafeYield(onlyIfNeeded=True) 1045 1046 def AutosyncUpdateUIEvent(self, event): 1047 event.Enable(self.autosyncsetting.IsConfigured()) 1048 1049 def OnExit(self,_=None): 1050 self.Close(True) 1051 1052 # It has been requested that we shutdown 1053 def OnClose(self, event): 1054 if self._taskbar_on_closed and self._close_button and \ 1055 event.CanVeto(): 1056 self._close_button=False 1057 event.Veto() 1058 self.Iconize(True) 1059 return 1060 if not self.IsIconized(): 1061 self.saveSize() 1062 if not self.wt: 1063 # worker thread doesn't exist yet 1064 self.Destroy() 1065 return 1066 # Shutdown helper thread 1067 self.MakeCall( Request(self.wt.exit), Callback(self.OnCloseResults) ) 1068 1069 def OnCloseResults(self, exception, _): 1070 assert isinstance(exception, BitPimExit) 1071 # assume it worked 1072 if self._taskbar: 1073 self._taskbar.Destroy() 1074 self.Destroy() 1075 1076 def OnIconize(self, evt): 1077 if evt.Iconized(): 1078 self.Show(False) 1079 else: 1080 self.Show(True) 1081 self.Raise() 1082 1083 # deal with configuring the phone (commport) 1084 def OnEditSettings(self, _=None): 1085 if wx.IsBusy(): 1086 wx.MessageBox("BitPim is busy. You can't change settings until it has finished talking to your phone.", 1087 "BitPim is busy.", wx.OK|wx.ICON_EXCLAMATION) 1088 else: 1089 # clear the ower's name for manual setting 1090 self.__owner_name='' 1091 self.configdlg.ShowModal() 1092 # about and help 1093 1094 def OnHelpAbout(self,_): 1095 guiwidgets.show_about_dlg(self) 1096 1097 def OnHelpHelp(self, _): 1098 wx.GetApp().displayhelpid(self.GetCurrentActiveWidget().GetHelpID()) 1099 1100 def OnHelpHowtos(self, _): 1101 wx.GetApp().displayhelpid(helpids.ID_HOWTOS) 1102 1103 def OnHelpFAQ(self, _): 1104 wx.GetApp().displayhelpid(helpids.ID_FAQ) 1105 1106 def OnHelpContents(self, _): 1107 wx.GetApp().displayhelpid(None) 1108 1109 def OnHelpSupport(self, _): 1110 wx.GetApp().displayhelpid(helpids.ID_HELPSUPPORT) 1111 1112 def OnHelpTour(self, _=None): 1113 wx.GetApp().displayhelpid(helpids.ID_TOUR) 1114 1115 def OnHelpPhoneUpdateUI(self, event): 1116 if self.phonemodule and hasattr(self.phonemodule.Phone, 'desc'): 1117 event.SetText(self.phonemodule.Phone.desc) 1118 else: 1119 event.SetText('Phone') 1120 event.Enable(bool(hasattr(self.phonemodule.Phone, "helpid") and\ 1121 self.phonemodule.Phone.helpid)) 1122 def OnHelpPhone(self, _): 1123 wx.GetApp().displayhelpid(self.phonemodule.Phone.helpid) 1124 def DoCheckUpdate(self): 1125 s=update.check_update() 1126 if not len(s): 1127 # Failed to update 1128 return 1129 # update our config with the latest version and date 1130 self.config.Write('latest_version', s) 1131 self.config.Write('last_update', 1132 time.strftime('%Y%m%d', time.localtime())) 1133 # update the status bar 1134 self.SetVersionsStatus() 1135 1136 def OnCheckUpdate(self, _): 1137 self.DoCheckUpdate() 1138 1139 def SetPhoneModelStatus(self, stat=guiwidgets.SB_Phone_Set): 1140 phone=self.config.Read('phonetype', 'None') 1141 port=self.config.Read('lgvx4400port', 'None') 1142 if self.__owner_name=='': 1143 self.GetStatusBar().set_phone_model('%s on %s'%(phone, port), 1144 stat) 1145 else: 1146 self.GetStatusBar().set_phone_model('%s %s on %s'%(self.__owner_name, phone, port), 1147 stat) 1148 1149 def OnPhoneInfo(self, _): 1150 self.MakeCall(Request(self.wt.getphoneinfo), 1151 Callback(self.OnDisplayPhoneInfo)) 1152 def OnDisplayPhoneInfo(self, exception, phone_info): 1153 if self.HandleException(exception): return 1154 if phone_info is None: 1155 # data not available 1156 dlg=wx.MessageDialog(self, "Phone Info not available", 1157 "Phone Info Error", style=wx.OK) 1158 else: 1159 dlg=phoneinfo.PhoneInfoDialog(self, phone_info) 1160 with guihelper.WXDialogWrapper(dlg, True): 1161 pass 1162 1163 def OnDetectPhone(self, _=None): 1164 if wx.IsBusy(): 1165 # main thread is busy, put it on the queue for the next turn 1166 self.queue.put((self.OnDetectPhone, (), {}), False) 1167 return 1168 self.__detect_phone() 1169 def __detect_phone(self, using_port=None, check_auto_sync=0, delay=0, silent_fail=False): 1170 self.OnBusyStart() 1171 self.GetStatusBar().progressminor(0, 100, 'Phone detection in progress ...') 1172 self.MakeCall(Request(self.wt.detectphone, using_port, None, delay), 1173 Callback(self.OnDetectPhoneReturn, check_auto_sync, silent_fail)) 1174 def _detect_this_phone(self, check_auto_sync=0, delay=0, silent_fail=False): 1175 # (re)detect the current phone model 1176 self.OnBusyStart() 1177 self.GetStatusBar().progressminor(0, 100, 'Phone detection in progress ...') 1178 self.MakeCall(Request(self.wt.detectphone, 1179 self.config.Read('lgvx4400port', ''), 1180 self.config.Read('phonetype', ''), delay), 1181 Callback(self.OnDetectThisPhoneReturn, check_auto_sync, 1182 silent_fail)) 1183 def OnDetectThisPhoneReturn(self, check_auto_sync, silent_fail, 1184 exception, r): 1185 if self.HandleException(exception): 1186 self.OnBusyEnd() 1187 return 1188 if r: 1189 # detected! 1190 return self.OnDetectPhoneReturn(check_auto_sync, silent_fail, 1191 exception, r) 1192 # Failed to detect current model, retry for all models 1193 self.queue.put((self.__detect_phone, (), 1194 { 'check_auto_sync': check_auto_sync, 1195 'silent_fail': silent_fail }), False) 1196 self.OnBusyEnd() 1197 1198 def __get_owner_name(self, esn, style=wx.DEFAULT_DIALOG_STYLE): 1199 """ retrieve or ask user for the owner's name of this phone 1200 """ 1201 if esn is None or not len(esn): 1202 return None 1203 # esn is found, check if we detected this phone before 1204 phone_id='phones/'+sha.new(esn).hexdigest() 1205 phone_name=self.config.Read(phone_id, '<None/>') 1206 s=None 1207 if phone_name=='<None/>': 1208 # not seen before 1209 with guihelper.WXDialogWrapper(guiwidgets.AskPhoneNameDialog(self, 'A new phone has been detected,\n' 1210 "Would you like to enter the owner's name:", style=style), 1211 True) as (dlg, r): 1212 if r==wx.ID_OK: 1213 # user gave a name 1214 s=dlg.GetValue() 1215 elif r==wx.ID_CANCEL: 1216 s='' 1217 if s is not None: 1218 self.config.Write(phone_id, s) 1219 return s 1220 return phone_name 1221 1222 def OnDetectPhoneReturn(self, check_auto_sync, silent_fail, exception, r): 1223 self._autodetect_delay=0 1224 self.OnBusyEnd() 1225 if self.HandleException(exception): return 1226 if r is None: 1227 if not silent_fail: 1228 self.__owner_name='' 1229 with guihelper.WXDialogWrapper(wx.MessageDialog(self, 'No phone detected/recognized.\nRun Settings?', 1230 'Phone Detection Failed', wx.YES_NO), 1231 True) as (_dlg, retcode): 1232 if retcode==wx.ID_YES: 1233 wx.CallAfter(self.OnEditSettings) 1234 self.SetPhoneModelStatus(guiwidgets.SB_Phone_Set) 1235 else: 1236 if silent_fail: 1237 self.__owner_name=None 1238 else: 1239 self.__owner_name=self.__get_owner_name(r.get('phone_esn', None)) 1240 if self.__owner_name is None or self.__owner_name=='': 1241 self.__owner_name='' 1242 else: 1243 self.__owner_name+="'s" 1244 self.config.Write("phonetype", r['phone_name']) 1245 self.commportsetting=str(r['port']) 1246 self.wt.clearcomm() 1247 self.config.Write("lgvx4400port", r['port']) 1248 self.phonemodule=common.importas(r['phone_module']) 1249 self.phoneprofile=self.phonemodule.Profile() 1250 pubsub.publish(pubsub.PHONE_MODEL_CHANGED, self.phonemodule) 1251 self.SetPhoneModelStatus(guiwidgets.SB_Phone_Detected) 1252 if not silent_fail: 1253 if self.__owner_name =='': 1254 wx.MessageBox('Found %s on %s'%(r['phone_name'], 1255 r['port']), 1256 'Phone Detection', wx.OK) 1257 else: 1258 wx.MessageBox('Found %s %s on %s'%(self.__owner_name, 1259 r['phone_name'], 1260 r['port']), 1261 'Phone Detection', wx.OK) 1262 if check_auto_sync: 1263 # see if we should re-sync the calender on connect, do it silently 1264 self.__autosync_phone(silent=1) 1265 1266 def AddComm(self, name): 1267 # A new comm port became available 1268 print 'New device on port:',name 1269 # check the new device 1270 check_auto_sync=auto_sync.UpdateOnConnect(self) 1271 if name and name.lower()==self.config.Read('lgvx4400port', '').lower(): 1272 _func=self._detect_this_phone 1273 _args=(check_auto_sync, self._autodetect_delay, True) 1274 else: 1275 _func=self.__detect_phone 1276 _args=(name, check_auto_sync, self._autodetect_delay, True) 1277 if wx.IsBusy(): 1278 # current phone operation ongoing, queue this 1279 self.queue.put((_func, _args, {}), False) 1280 else: 1281 _func(*_args) 1282 1283 def RemoveComm(self, name): 1284 # This comm just went away 1285 print "Device remove", name 1286 # device is removed, if it's ours, clear the port 1287 if name and name.lower()==self.config.Read('lgvx4400port', '').lower(): 1288 if self.wt: 1289 self.wt.clearcomm() 1290 self.SetPhoneModelStatus(guiwidgets.SB_Phone_Unavailable) 1291 1292 def NotifyComm(self, evt): 1293 if evt.type==evt.add: 1294 self.AddComm(evt.comm) 1295 else: 1296 self.RemoveComm(evt.comm) 1297 1298 def OnCommNotification(self, evt): 1299 print 'OnCommNotification' 1300 if wx.Thread_IsMain(): 1301 self.NotifyComm(evt) 1302 else: 1303 wx.CallAfter(self.NotifyComm, evt) 1304 1305 def WindowsOnDeviceChanged(self, type, name="", drives=[], flag=None): 1306 if not name.lower().startswith("com"): 1307 return 1308 if type=='DBT_DEVICEREMOVECOMPLETE': 1309 self.RemoveComm(name) 1310 return 1311 if type!='DBT_DEVICEARRIVAL': 1312 # not interested 1313 return 1314 self.AddComm(name) 1315 1316 def MyWndProc(self, hwnd, msg, wparam, lparam): 1317 1318 if msg==win32con.WM_DEVICECHANGE: 1319 try: 1320 type,params=DeviceChanged(wparam, lparam).GetEventInfo() 1321 self.OnDeviceChanged(type, **params) 1322 return True 1323 except: 1324 # something bad happened! Bail and let Windows handle it 1325 return win32gui.CallWindowProc(self.oldwndproc, hwnd, msg, 1326 wparam, lparam) 1327 1328 # Restore the old WndProc. Notice the use of win32api 1329 # instead of win32gui here. This is to avoid an error due to 1330 # not passing a callable object. 1331 if msg == win32con.WM_DESTROY: 1332 win32api.SetWindowLong(self.GetHandle(), 1333 win32con.GWL_WNDPROC, 1334 self.oldwndproc) 1335 if self._taskbar_on_closed and \ 1336 msg==win32con.WM_NCLBUTTONDOWN and \ 1337 wparam==win32con.HTCLOSE: 1338 # The system Close Box was clicked! 1339 self._close_button=True 1340 1341 # Pass all messages (in this case, yours may be different) on 1342 # to the original WndProc 1343 return win32gui.CallWindowProc(self.oldwndproc, 1344 hwnd, msg, wparam, lparam) 1345 1346 if guihelper.IsMSWindows(): 1347 OnDeviceChanged=WindowsOnDeviceChanged 1348 1349 def SetVersionsStatus(self): 1350 current_v=version.version 1351 latest_v=self.config.Read('latest_version') 1352 self.GetStatusBar().set_versions(current_v, latest_v) 1353 1354 def update_cache_path(self): 1355 com_brew.file_cache.set_path(self.configpath) 1356 1357 def OnNewDB(self, _): 1358 newdb_wiz.create_new_db(self, self.config) 1359 1360 ### 1361 ### Main bit for getting stuff from phone 1362 ### 1363 1364 def OnDataGetPhone(self,_): 1365 todo=[] 1366 dlg=self.dlggetphone 1367 dlg.UpdateWithProfile(self.phoneprofile) 1368 if dlg.ShowModal()!=wx.ID_OK: 1369 return 1370 self._autodetect_delay=self.phoneprofile.autodetect_delay 1371 todo.append((self.wt.rebootcheck, "Phone Reboot")) 1372 wx.GetApp().critical.set() 1373 self.MakeCall(Request(self.wt.getdata, dlg, todo), 1374 Callback(self.OnDataGetPhoneResults)) 1375 1376 def OnDataGetPhoneResults(self, exception, results): 1377 with wx.GetApp().critical: 1378 if self.HandleException(exception): return 1379 self.OnLog(`results.keys()`) 1380 self.OnLog(`results['sync']`) 1381 # phonebook 1382 if results['sync'].has_key('phonebook'): 1383 v=results['sync']['phonebook'] 1384 1385 print "phonebookmergesetting is",v 1386 if v=='MERGE': 1387 merge=True 1388 else: 1389 merge=False 1390 self.GetActivePhonebookWidget().importdata(results['phonebook'], results.get('categories', []), merge) 1391 1392 # wallpaper 1393 updwp=False # did we update the wallpaper 1394 if results['sync'].has_key('wallpaper'): 1395 v=results['sync']['wallpaper'] 1396 if v=='MERGE': raise Exception("Not implemented") 1397 updwp=True 1398 self.GetActiveWallpaperWidget().populatefs(results) 1399 self.GetActiveWallpaperWidget().populate(results) 1400 # wallpaper-index 1401 if not updwp and results.has_key('wallpaper-index'): 1402 self.GetActiveWallpaperWidget().updateindex(results) 1403 # ringtone 1404 updrng=False # did we update ringtones 1405 if results['sync'].has_key('ringtone'): 1406 v=results['sync']['ringtone'] 1407 if v=='MERGE': raise Exception("Not implemented") 1408 updrng=True 1409 self.GetActiveRingerWidget().populatefs(results) 1410 self.GetActiveRingerWidget().populate(results) 1411 # ringtone-index 1412 if not updrng and results.has_key('ringtone-index'): 1413 self.GetActiveRingerWidget().updateindex(results) 1414 # calendar 1415 if results['sync'].has_key('calendar'): 1416 v=results['sync']['calendar'] 1417 if v=='MERGE': raise Exception("Not implemented") 1418 results['calendar_version']=self.phoneprofile.BP_Calendar_Version 1419 self.GetActiveCalendarWidget().mergedata(results) 1420 ## self.GetActiveCalendarWidget().populatefs(results) 1421 ## self.GetActiveCalendarWidget().populate(results) 1422 # memo 1423 if results['sync'].has_key('memo'): 1424 v=results['sync']['memo'] 1425 if v=='MERGE': raise Exception("Not implemented") 1426 self.GetActiveMemoWidget().populatefs(results) 1427 self.GetActiveMemoWidget().populate(results) 1428 # todo 1429 if results['sync'].has_key('todo'): 1430 v=results['sync']['todo'] 1431 if v=='MERGE': raise NotImplementedError 1432 self.GetActiveTodoWidget().populatefs(results) 1433 self.GetActiveTodoWidget().populate(results) 1434 # SMS 1435 if results['sync'].has_key('sms'): 1436 v=results['sync']['sms'] 1437 if v=='MERGE': 1438 self.GetActiveSMSWidget().merge(results) 1439 else: 1440 self.GetActiveSMSWidget().populatefs(results) 1441 self.GetActiveSMSWidget().populate(results) 1442 # call history 1443 if results['sync'].has_key('call_history'): 1444 v=results['sync']['call_history'] 1445 if v=='MERGE': 1446 self.GetActiveCallHistoryWidget().merge(results) 1447 else: 1448 self.GetActiveCallHistoryWidget().populatefs(results) 1449 self.GetActiveCallHistoryWidget().populate(results) 1450 # Playlist 1451 if results['sync'].has_key(playlist.playlist_key): 1452 if results['sync'][playlist.playlist_key]=='MERGE': 1453 raise NotImplementedError 1454 self.GetActivePlaylistWidget().populatefs(results) 1455 self.GetActivePlaylistWidget().populate(results) 1456 # T9 User DB 1457 if results['sync'].has_key(t9editor.dict_key): 1458 if results['sync'][t9editor.dict_key]=='MERGE': 1459 raise NotImplementedError 1460 self.GetActiveT9EditorWidget().populatefs(results) 1461 self.GetActiveT9EditorWidget().populate(results) 1462 ### 1463 ### Main bit for sending data to the phone 1464 ### 1465 def OnDataSendPhone(self, _): 1466 dlg=self.dlgsendphone 1467 print self.phoneprofile 1468 dlg.UpdateWithProfile(self.phoneprofile) 1469 if dlg.ShowModal()!=wx.ID_OK: 1470 return 1471 data={} 1472 convertors=[] 1473 todo=[] 1474 funcscb=[] 1475 1476 ### Wallpaper 1477 v=dlg.GetWallpaperSetting() 1478 if v!=dlg.NOTREQUESTED: 1479 merge=True 1480 if v==dlg.OVERWRITE: merge=False 1481 if merge: 1482 want=self.GetActiveWallpaperWidget().SELECTED 1483 else: 1484 want=self.GetActiveWallpaperWidget().ALL 1485 self.GetActiveWallpaperWidget().getdata(data, want) 1486 todo.append( (self.wt.writewallpaper, "Wallpaper", merge) ) 1487 # funcscb.append( self.wallpaperwidget.populate ) 1488 1489 ### Ringtone 1490 v=dlg.GetRingtoneSetting() 1491 if v!=dlg.NOTREQUESTED: 1492 merge=True 1493 if v==dlg.OVERWRITE: merge=False 1494 if merge: 1495 want=self.GetActiveRingerWidget().SELECTED 1496 else: 1497 want=self.GetActiveRingerWidget().ALL 1498 self.GetActiveRingerWidget().getdata(data, want) 1499 todo.append( (self.wt.writeringtone, "Ringtone", merge) ) 1500 # funcscb.append( self.ringerwidget.populate ) 1501 1502 ### Calendar 1503 v=dlg.GetCalendarSetting() 1504 if v!=dlg.NOTREQUESTED: 1505 merge=True 1506 if v==dlg.OVERWRITE: merge=False 1507 data['calendar_version']=self.phoneprofile.BP_Calendar_Version 1508 self.GetActiveCalendarWidget().getdata(data) 1509 todo.append( (self.wt.writecalendar, "Calendar", merge) ) 1510 1511 ### Phonebook 1512 v=dlg.GetPhoneBookSetting() 1513 if v!=dlg.NOTREQUESTED: 1514 if v==dlg.OVERWRITE: 1515 self.GetActivePhonebookWidget().getdata(data) 1516 todo.append( (self.wt.writephonebook, "Phonebook") ) 1517 convertors.append(self.GetActivePhonebookWidget().converttophone) 1518 # writing will modify serials so we need to update 1519 funcscb.append(self.GetActivePhonebookWidget().updateserials) 1520 1521 ### Memo 1522 v=dlg.GetMemoSetting() 1523 if v!=dlg.NOTREQUESTED: 1524 merge=v!=dlg.OVERWRITE 1525 self.GetActiveMemoWidget().getdata(data) 1526 todo.append((self.wt.writememo, "Memo", merge)) 1527 1528 ### Todo 1529 v=dlg.GetTodoSetting() 1530 if v!=dlg.NOTREQUESTED: 1531 merge=v!=dlg.OVERWRITE 1532 self.GetActiveTodoWidget().getdata(data) 1533 todo.append((self.wt.writetodo, "Todo", merge)) 1534 1535 ### SMS 1536 v=dlg.GetSMSSetting() 1537 if v!=dlg.NOTREQUESTED: 1538 merge=v!=dlg.OVERWRITE 1539 self.GetActiveSMSWidget().getdata(data) 1540 todo.append((self.wt.writesms, "SMS", merge)) 1541 1542 ### Playlist 1543 v=dlg.GetPlaylistSetting() 1544 if v!=dlg.NOTREQUESTED: 1545 merge=v!=dlg.OVERWRITE 1546 self.GetActivePlaylistWidget().getdata(data) 1547 todo.append((self.wt.writeplaylist, "Playlist", merge)) 1548 1549 ### T9 User DB 1550 v=dlg.GetT9Setting() 1551 if v!=dlg.NOTREQUESTED: 1552 merge=v!=dlg.OVERWRITE 1553 self.GetActiveT9EditorWidget().getdata(data) 1554 todo.append((self.wt.writet9, "T9", merge)) 1555 1556 data['reboot_delay']=self.phoneprofile.reboot_delay 1557 self._autodetect_delay=self.phoneprofile.autodetect_delay 1558 todo.append((self.wt.rebootcheck, "Phone Reboot")) 1559 self.MakeCall(Request(self.wt.getfundamentals), 1560 Callback(self.OnDataSendPhoneGotFundamentals, data, todo, convertors, funcscb)) 1561 1562 def OnDataSendPhoneGotFundamentals(self,data,todo,convertors, funcscb, exception, results): 1563 if self.HandleException(exception): return 1564 data.update(results) 1565 # call each widget to update fundamentals 1566 # for widget in self.calendarwidget, self.wallpaperwidget, self.ringerwidget, self.phonewidget: 1567 # widget.updatefundamentals(data) 1568 1569 # call convertors 1570 for f in convertors: 1571 f(data) 1572 1573 # Now scribble to phone 1574 self.MakeCall(Request(self.wt.senddata, data, todo), 1575 Callback(self.OnDataSendPhoneResults, funcscb)) 1576 1577 def OnDataSendPhoneResults(self, funcscb, exception, results): 1578 if self.HandleException(exception): return 1579 print results.keys() 1580 for f in funcscb: 1581 f(results) 1582 1583 def GetCalendarData(self): 1584 # return calendar data for export 1585 d={} 1586 return self.GetActiveCalendarWidget().getdata(d).get('calendar', {}) 1587 1588 1589 def OnAutoSyncSettings(self, _=None): 1590 if wx.IsBusy(): 1591 with guihelper.WXDialogWrapper(wx.MessageBox("BitPim is busy. You can't change settings until it has finished talking to your phone.", 1592 "BitPim is busy.", wx.OK|wx.ICON_EXCLAMATION), 1593 True): 1594 pass 1595 else: 1596 # clear the ower's name for manual setting 1597 self.__owner_name='' 1598 self.autosyncsetting.ShowModal() 1599 1600 def OnAutoSyncExecute(self, _=None): 1601 if wx.IsBusy(): 1602 wx.MessageBox("BitPim is busy. You can't run autosync until it has finished talking to your phone.", 1603 "BitPim is busy.", wx.OK|wx.ICON_EXCLAMATION) 1604 return 1605 self.__autosync_phone() 1606 1607 def __autosync_phone(self, silent=0): 1608 r=auto_sync.SyncSchedule(self).sync(self, silent) 1609 1610 # deal with configuring the phone (commport) 1611 def OnReqChangeTab(self, msg=None): 1612 if msg is None: 1613 return 1614 data=msg.data 1615 if not isinstance(data, int): 1616 # wrong data type 1617 if __debug__: 1618 raise TypeError 1619 return 1620 1621 # Busy handling 1622 def OnBusyStart(self): 1623 self.GetStatusBar().set_app_status_busy() 1624 wx.BeginBusyCursor(wx.StockCursor(wx.CURSOR_ARROWWAIT)) 1625 1626 def OnBusyEnd(self): 1627 wx.EndBusyCursor() 1628 self.GetStatusBar().set_app_status_ready() 1629 self.OnProgressMajor(0,1) 1630 # fire the next one in the queue 1631 if not self.queue.empty(): 1632 _q=self.queue.get(False) 1633 wx.CallAfter(_q[0], *_q[1], **_q[2]) 1634 1635 # progress and logging 1636 def OnProgressMinor(self, pos, max, desc=""): 1637 self.GetStatusBar().progressminor(pos, max, desc) 1638 1639 def OnProgressMajor(self, pos, max, desc=""): 1640 self.GetStatusBar().progressmajor(pos, max, desc) 1641 1642 def OnLog(self, str): 1643 if self.__phone_detect_at_startup: 1644 return 1645 str=common.strorunicode(str) 1646 if data_recording.DR_On: 1647 data_recording.record(data_recording.DR_Type_Note, str) 1648 self.tree.lw.log(str) 1649 if self.tree.lwdata is not None: 1650 self.tree.lwdata.log(str) 1651 if str.startswith("<!= "): 1652 p=str.index("=!>")+3 1653 guihelper.MessageDialog(self, str[p:], "Alert", style=wx.OK|wx.ICON_EXCLAMATION) 1654 self.OnLog("Alert dialog closed") 1655 log=OnLog 1656 def OnLogData(self, str, data, klass=None, data_type=None): 1657 if data_recording.DR_On: 1658 data_recording.record(data_recording.DR_Type_Note, str) 1659 data_recording.record(data_type or data_recording.DR_Type_Data, 1660 data, klass) 1661 if self.tree.lwdata is not None: 1662 self.tree.lwdata.logdata(str,data, klass) 1663 1664 def excepthook(self, type, value, traceback): 1665 if not hasattr(value, "gui_exc_info"): 1666 value.gui_exc_info=(type,value,traceback) 1667 self.HandleException(value) 1668 1669 def HandleException(self, exception): 1670 """returns true if this function handled the exception 1671 and the caller should not do any further processing""" 1672 if exception is None: return False 1673 assert isinstance(exception, Exception) 1674 self.CloseSplashScreen() 1675 # always close comm connection when we have any form of exception 1676 if self.wt is not None: 1677 self.wt.clearcomm() 1678 text=None 1679 title=None 1680 style=None 1681 # Here is where we turn the exception into something user friendly 1682 if isinstance(exception, common.CommsDeviceNeedsAttention): 1683 text="%s: %s" % (exception.device, exception.message) 1684 title="Device needs attention - "+exception.device 1685 style=wx.OK|wx.ICON_INFORMATION 1686 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_DEVICE_NEEDS_ATTENTION) 1687 elif isinstance(exception, common.CommsOpenFailure): 1688 text="%s: %s" % (exception.device, exception.message) 1689 title="Failed to open communications - "+exception.device 1690 style=wx.OK|wx.ICON_INFORMATION 1691 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_FAILED_TO_OPEN_DEVICE) 1692 elif isinstance(exception, common.AutoPortsFailure): 1693 text=exception.message 1694 title="Failed to automatically detect port" 1695 style=wx.OK|wx.ICON_INFORMATION 1696 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_FAILED_TO_AUTODETECT_PORT) 1697 elif isinstance(exception, common.HelperBinaryNotFound) and exception.basename=="pvconv": 1698 text="The Qualcomm PureVoice converter program (%s) was not found.\nPlease see the help. Directories looked in are:\n\n " +\ 1699 "\n ".join(exception.paths) 1700 text=text % (exception.fullname,) 1701 title="Failed to find PureVoice converter" 1702 style=wx.OK|wx.ICON_INFORMATION 1703 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_NO_PVCONV) 1704 elif isinstance(exception, common.PhoneBookBusyException): 1705 text="The phonebook is busy on your phone.\nExit back to the main screen and then repeat the operation." 1706 title="Phonebook busy on phone" 1707 style=wx.OK|wx.ICON_INFORMATION 1708 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_PHONEBOOKBUSY) 1709 elif isinstance(exception, common.IntegrityCheckFailed): 1710 text="The phonebook on your phone is partially corrupt. Please read the\nhelp for more details on the cause and fix" 1711 title="IntegrityCheckFailed" 1712 style=wx.OK|wx.ICON_EXCLAMATION 1713 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_LG_INTEGRITYCHECKFAILED) 1714 elif isinstance(exception, common.CommsDataCorruption): 1715 text=exception.message+"\nPlease see the help." 1716 title="Communications Error - "+exception.device 1717 style=wx.OK|wx.ICON_EXCLAMATION 1718 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_COMMSDATAERROR) 1719 elif isinstance(exception, com_brew.BrewAccessDeniedException): 1720 text="Access to the file/directory has been blocked on this phone by the phone provider" 1721 title="Access Denied" 1722 style=wx.OK|wx.ICON_EXCLAMATION 1723 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_BREW_ACCESS_DENIED) 1724 elif isinstance(exception, common.PhoneStringEncodeException): 1725 text="Unable to convert the text <%s> into a format your phone can understand, change the text to contain only %s characters" % (exception.string, `exception.codec`) 1726 title="Text Conversion Error" 1727 style=wx.OK|wx.ICON_EXCLAMATION 1728 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_BREW_ACCESS_DENIED) 1729 1730 if text is not None: 1731 self.OnLog("Error: "+title+"\n"+text) 1732 with guihelper.WXDialogWrapper(guiwidgets.AlertDialogWithHelp(self,text, title, help, style=style), 1733 True): 1734 pass 1735 return True 1736 1737 if self.exceptiondialog is None: 1738 self.excepttime=time.time() 1739 self.exceptcount=0 1740 self.exceptiondialog=guiwidgets.ExceptionDialog(self, exception) 1741 try: 1742 self.OnLog("Exception: "+self.exceptiondialog.getexceptiontext()) 1743 except AttributeError: 1744 # this can happen if main gui hasn't been built yet 1745 pass 1746 else: 1747 self.exceptcount+=1 1748 if self.exceptcount<10: 1749 print "Ignoring an exception as the exception dialog is already up" 1750 try: 1751 self.OnLog("Exception during exception swallowed") 1752 except AttributeError: 1753 # this can happen if main gui hasn't been built yet 1754 pass 1755 return True 1756 1757 self.exceptiondialog.ShowModal() 1758 self.exceptiondialog.Destroy() 1759 self.exceptiondialog=None 1760 return True 1761 1762 # midnight timer stuff 1763 def _OnTimer(self, _): 1764 self.MakeCall(Request(self._pub_timer), 1765 Callback(self._OnTimerReturn)) 1766 1767 def _pub_timer(self): 1768 pubsub.publish(pubsub.MIDNIGHT) 1769 1770 def _OnTimerReturn(self, exceptions, result): 1771 self._timer.Start(((3600*24)+1)*1000, True) 1772 1773 def _setup_midnight_timer(self): 1774 _today=datetime.datetime.now() 1775 _timer_val=24*3600-_today.hour*3600-_today.minute*60-_today.second+1 1776 self._timer=wx.Timer(self) 1777 wx.EVT_TIMER(self, self._timer.GetId(), self._OnTimer) 1778 self._timer.Start(_timer_val*1000, True) 1779 print _timer_val,'seconds till midnight' 1780 1781 # Data Recording stuff 1782 def OnDataRecording(self, _): 1783 with guihelper.WXDialogWrapper(guiwidgets.DRRecFileDialog(self), 1784 True): 1785 pass 1786 1787 # plumbing for the multi-threading 1788 1789 def OnCallback(self, event): 1790 assert isinstance(event, HelperReturnEvent) 1791 event() 1792 1793 def MakeCall(self, request, cbresult): 1794 assert isinstance(request, Request) 1795 assert isinstance(cbresult, Callback) 1796 self.wt.q.put( (request, cbresult) ) 1797 1798 # remember our size and position 1799 1800 def saveSize(self): 1801 guiwidgets.save_size("MainWin", self.GetRect()) 1802 1803 ### 1804 ### Container for midi files 1805 ### 1806 1807 #class MidiFileList(wx.ListCtrl): 1808 # pass 1809 1810 1811 1812 1813 ### 1814 ### Class that does all the comms and other stuff in a seperate 1815 ### thread. 1816 ### 1817 1818 class WorkerThread(WorkerThreadFramework): 1819 def __init__(self): 1820 WorkerThreadFramework.__init__(self) 1821 self.commphone=None 1822 data_recording.register(self.OnDataRecording, self.OnDataRecording, 1823 self.OnDataRecording) 1824 1825 def exit(self): 1826 if __debug__: self.checkthread() 1827 for i in range(0,0): 1828 self.progressmajor(i, 2, "Shutting down helper thread") 1829 time.sleep(1) 1830 self.log("helper thread shut down") 1831 raise BitPimExit("helper thread shutdown") 1832 1833 1834 def clearcomm(self): 1835 if self.commphone is None: 1836 return 1837 self.commphone.close() 1838 self.commphone=None 1839 1840 1841 def setupcomm(self): 1842 if __debug__: self.checkthread() 1843 if self.commphone is None: 1844 import commport 1845 if self.dispatchto.commportsetting is None or \ 1846 len(self.dispatchto.commportsetting)==0: 1847 raise common.CommsNeedConfiguring("Comm port not configured", "DEVICE") 1848 1849 if self.dispatchto.commportsetting=="auto": 1850 autofunc=comdiagnose.autoguessports 1851 else: 1852 autofunc=None 1853 comcfg=self.dispatchto.commparams 1854 1855 name=self.dispatchto.commportsetting 1856 if name.startswith("bitfling::"): 1857 klass=bitflingscan.CommConnection 1858 else: 1859 klass=commport.CommConnection 1860 1861 comport=klass(self, self.dispatchto.commportsetting, autolistfunc=autofunc, 1862 autolistargs=(self.dispatchto.phonemodule,), 1863 baud=comcfg['baud'], timeout=comcfg['timeout'], 1864 hardwareflow=comcfg['hardwareflow'], 1865 softwareflow=comcfg['softwareflow'], 1866 configparameters=comcfg) 1867 1868 try: 1869 self.commphone=self.dispatchto.phonemodule.Phone(self, comport) 1870 except: 1871 comport.close() 1872 raise 1873 1874 def getfundamentals(self): 1875 if __debug__: self.checkthread() 1876 self.setupcomm() 1877 results={} 1878 self.commphone.getfundamentals(results) 1879 return results 1880 1881 def getdata(self, req, todo): 1882 if __debug__: self.checkthread() 1883 self.setupcomm() 1884 results=self.getfundamentals() 1885 com_brew.file_cache.esn=results.get('uniqueserial', None) 1886 willcall=[] 1887 sync={} 1888 for i in ( 1889 (req.GetPhoneBookSetting, self.commphone.getphonebook, "Phone Book", "phonebook"), 1890 (req.GetCalendarSetting, self.commphone.getcalendar, "Calendar", "calendar",), 1891 (req.GetWallpaperSetting, self.commphone.getwallpapers, "Wallpaper", "wallpaper"), 1892 (req.GetRingtoneSetting, self.commphone.getringtones, "Ringtones", "ringtone"), 1893 (req.GetMemoSetting, self.commphone.getmemo, "Memo", "memo"), 1894 (req.GetTodoSetting, self.commphone.gettodo, "Todo", "todo"), 1895 (req.GetSMSSetting, self.commphone.getsms, "SMS", "sms"), 1896 (req.GetCallHistorySetting, self.commphone.getcallhistory, 'Call History', 'call_history'), 1897 (req.GetPlaylistSetting, self.commphone.getplaylist, 'Play List', 'playlist'), 1898 (req.GetT9Setting, self.commphone.gett9db, 'T9 DB', t9editor.dict_key), 1899 ): 1900 st=i[0]() 1901 if st==req.MERGE: 1902 sync[i[3]]="MERGE" 1903 willcall.append(i) 1904 elif st==req.OVERWRITE: 1905 sync[i[3]]="OVERWRITE" 1906 willcall.append(i) 1907 1908 results['sync']=sync 1909 count=0 1910 for i in willcall: 1911 self.progressmajor(count, len(willcall), i[2]) 1912 count+=1 1913 i[1](results) 1914 1915 for xx in todo: 1916 func=xx[0] 1917 desc=xx[1] 1918 args=[results] 1919 if len(xx)>2: 1920 args.extend(xx[2:]) 1921 apply(func, args) 1922 1923 return results 1924 1925 def senddata(self, dict, todo): 1926 count=0 1927 for xx in todo: 1928 func=xx[0] 1929 desc=xx[1] 1930 args=[dict] 1931 if len(xx)>2: 1932 args.extend(xx[2:]) 1933 self.progressmajor(count,len(todo),desc) 1934 apply(func, args) 1935 count+=1 1936 return dict 1937 1938 def writewallpaper(self, data, merge): 1939 if __debug__: self.checkthread() 1940 self.setupcomm() 1941 return self.commphone.savewallpapers(data, merge) 1942 1943 def writeringtone(self, data, merge): 1944 if __debug__: self.checkthread() 1945 self.setupcomm() 1946 return self.commphone.saveringtones(data, merge) 1947 1948 def writephonebook(self, data): 1949 if __debug__: self.checkthread() 1950 self.setupcomm() 1951 return self.commphone.savephonebook(data) 1952 1953 def rebootcheck(self, results): 1954 if __debug__: self.checkthread() 1955 if results.has_key('rebootphone'): 1956 self.log("BitPim is rebooting your phone for changes to take effect") 1957 delay=0 1958 if results.has_key('reboot_delay'): 1959 delay=results['reboot_delay'] 1960 self.phonerebootrequest(delay) 1961 self.clearcomm() 1962 elif results.get('clearcomm', False): 1963 # some model (eg Moto) needs to clear comm after certain mode 1964 self.clearcomm() 1965 1966 def writecalendar(self, data, merge): 1967 if __debug__: self.checkthread() 1968 self.setupcomm() 1969 return self.commphone.savecalendar(data, merge) 1970 1971 def writememo(self, data, merge): 1972 if __debug__: self.checkthread() 1973 self.setupcomm() 1974 return self.commphone.savememo(data, merge) 1975 1976 def writetodo(self, data, merge): 1977 if __debug__: self.checkthread() 1978 self.setupcomm() 1979 return self.commphone.savetodo(data, merge) 1980 1981 def writesms(self, data, merge): 1982 if __debug__: self.checkthread() 1983 self.setupcomm() 1984 return self.commphone.savesms(data, merge) 1985 1986 def writeplaylist(self, data, merge): 1987 if __debug__: self.checkthread() 1988 self.setupcomm() 1989 return self.commphone.saveplaylist(data, merge) 1990 1991 def writet9(self, data, merge): 1992 if __debug__: 1993 self.checkthread() 1994 self.setupcomm() 1995 return self.commphone.savet9db(data, merge) 1996 1997 def getphoneinfo(self): 1998 if __debug__: self.checkthread() 1999 self.setupcomm() 2000 if hasattr(self.commphone, 'getphoneinfo'): 2001 phone_info=phoneinfo.PhoneInfo() 2002 getattr(self.commphone, 'getphoneinfo')(phone_info) 2003 return phone_info 2004 2005 def detectphone(self, using_port=None, using_model=None, delay=0): 2006 self.clearcomm() 2007 time.sleep(delay) 2008 return phone_detect.DetectPhone(self).detect(using_port, using_model) 2009 2010 # various file operations for the benefit of the filesystem viewer 2011 def dirlisting(self, path, recurse=0): 2012 if __debug__: self.checkthread() 2013 self.setupcomm() 2014 try: 2015 return self.commphone.getfilesystem(path, recurse) 2016 except: 2017 self.log('Failed to read dir: '+path) 2018 return {} 2019 2020 def getfileonlylist(self, path): 2021 if __debug__: self.checkthread() 2022 self.setupcomm() 2023 try: 2024 return self.commphone.listfiles(path) 2025 except: 2026 self.log('Failed to read filesystem') 2027 return {} 2028 2029 def getdironlylist(self, path, recurse): 2030 results=self.commphone.listsubdirs(path) 2031 subdir_list=[x['name'] for k,x in results.items()] 2032 if recurse: 2033 for _subdir in subdir_list: 2034 try: 2035 results.update(self.getdironlylist(_subdir, recurse)) 2036 except: 2037 self.log('Failed to list directories in ' +_subdir) 2038 return results 2039 2040 def fulldirlisting(self): 2041 if __debug__: self.checkthread() 2042 self.setupcomm() 2043 try: 2044 return self.getdironlylist("", True) 2045 except: 2046 self.log('Failed to read filesystem') 2047 return {} 2048 2049 def singledirlisting(self, path): 2050 if __debug__: self.checkthread() 2051 self.setupcomm() 2052 try: 2053 return self.getdironlylist(path, False) 2054 except: 2055 self.log('Failed to read filesystem') 2056 return {} 2057 2058 def getfile(self, path): 2059 if __debug__: self.checkthread() 2060 self.setupcomm() 2061 return self.commphone.getfilecontents(path) 2062 2063 def rmfile(self,path): 2064 if __debug__: self.checkthread() 2065 self.setupcomm() 2066 return self.commphone.rmfile(path) 2067 2068 def writefile(self,path,contents): 2069 if __debug__: self.checkthread() 2070 self.setupcomm() 2071 return self.commphone.writefile(path, contents) 2072 2073 def mkdir(self,path): 2074 if __debug__: self.checkthread() 2075 self.setupcomm() 2076 return self.commphone.mkdir(path) 2077 2078 def rmdir(self,path): 2079 if __debug__: self.checkthread() 2080 self.setupcomm() 2081 return self.commphone.rmdir(path) 2082 2083 def rmdirs(self,path): 2084 if __debug__: self.checkthread() 2085 self.setupcomm() 2086 return self.commphone.rmdirs(path) 2087 2088 # offline/reboot/modemmode 2089 def phonerebootrequest(self, reboot_delay=0): 2090 if __debug__: self.checkthread() 2091 self.setupcomm() 2092 return self.commphone.offlinerequest(reset=True, delay=reboot_delay) 2093 2094 def phoneofflinerequest(self): 2095 if __debug__: self.checkthread() 2096 self.setupcomm() 2097 return self.commphone.offlinerequest() 2098 2099 def modemmoderequest(self): 2100 if __debug__: self.checkthread() 2101 self.setupcomm() 2102 return self.commphone.modemmoderequest() 2103 2104 # backups etc 2105 def getbackup(self,path,recurse=0): 2106 if __debug__: self.checkthread() 2107 self.setupcomm() 2108 self.progressmajor(0,0,"Listing files") 2109 files=self.dirlisting(path, recurse) 2110 if path=="/" or path=="": 2111 strip=0 # root dir 2112 else: 2113 strip=len(path)+1 # child 2114 2115 keys=files.keys() 2116 keys.sort() 2117 2118 op=cStringIO.StringIO() 2119 with contextlib.closing(zipfile.ZipFile(op, "w", zipfile.ZIP_DEFLATED)) as zip: 2120 count=0 2121 for k in keys: 2122 try: 2123 count+=1 2124 if files[k]['type']!='file': 2125 continue 2126 self.progressmajor(count, len(keys)+1, "Getting files") 2127 # get the contents 2128 contents=self.getfile(k) 2129 # an artificial sleep. if you get files too quickly, the 4400 eventually 2130 # runs out of buffers and returns truncated packets 2131 time.sleep(0.3) 2132 # add to zip file 2133 zi=zipfile.ZipInfo() 2134 # zipfile does not like unicode. cp437 works on windows well, may be 2135 # a better choice than ascii, but no phones currently support anything 2136 # other than ascii for filenames 2137 if k[strip]=='/': 2138 zi.filename=common.get_ascii_string(k[strip+1:], 'ignore') 2139 else: 2140 zi.filename=common.get_ascii_string(k[strip:], 'ignore') 2141 if files[k]['date'][0]==0: 2142 zi.date_time=(0,0,0,0,0,0) 2143 else: 2144 zi.date_time=time.gmtime(files[k]['date'][0])[:6] 2145 zi.compress_type=zipfile.ZIP_DEFLATED 2146 zip.writestr(zi, contents) 2147 except: 2148 self.log('Failed to read file: '+k) 2149 return op.getvalue() 2150 2151 def restorefiles(self, files): 2152 if __debug__: self.checkthread() 2153 self.setupcomm() 2154 2155 results=[] 2156 2157 seendirs=[] 2158 2159 count=0 2160 for name, contents in files: 2161 self.progressmajor(count, len(files), "Restoring files") 2162 count+=1 2163 d=guihelper.dirname(name) 2164 if d not in seendirs: 2165 seendirs.append(d) 2166 self.commphone.mkdirs(d) 2167 self.writefile(name, contents) 2168 results.append( (True, name) ) 2169 # add a deliberate sleep - some phones (eg vx7000) get overwhelmed when writing 2170 # lots of files in a tight loop 2171 time.sleep(0.3) 2172 2173 return results 2174 2175 def OnDataRecording(self, _=None): 2176 self.clearcomm() 2177 2178 #------------------------------------------------------------------------------- 2179 # For windows platform only 2180 if guihelper.IsMSWindows(): 2181 import struct 2182 class DeviceChanged: 2183 2184 DBT_DEVICEARRIVAL = 0x8000 2185 DBT_DEVICEQUERYREMOVE = 0x8001 2186 DBT_DEVICEQUERYREMOVEFAILED = 0x8002 2187 DBT_DEVICEREMOVEPENDING = 0x8003 2188 DBT_DEVICEREMOVECOMPLETE = 0x8004 2189 DBT_DEVICETYPESPECIFIC = 0x8005 2190 DBT_DEVNODES_CHANGED = 7 2191 DBT_CONFIGCHANGED = 0x18 2192 2193 DBT_DEVTYP_OEM = 0 2194 DBT_DEVTYP_DEVNODE = 1 2195 DBT_DEVTYP_VOLUME = 2 2196 DBT_DEVTYP_PORT = 3 2197 DBT_DEVTYP_NET = 4 2198 2199 DBTF_MEDIA = 0x0001 2200 DBTF_NET = 0x0002 2201 2202 def __init__(self, wparam, lparam): 2203 self._info=None 2204 for name in dir(self): 2205 if name.startswith("DBT") and \ 2206 not name.startswith("DBT_DEVTYP") and \ 2207 getattr(self,name)==wparam: 2208 self._info=(name, dict(self._decode_struct(lparam))) 2209 2210 def GetEventInfo(self): 2211 return self._info 2212 2213 def _decode_struct(self, lparam): 2214 if lparam==0: return () 2215 format = "iii" 2216 buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam) 2217 dbch_size, dbch_devicetype, dbch_reserved = struct.unpack(format, buf) 2218 2219 buf = win32gui.PyMakeBuffer(dbch_size, lparam) # we know true size now 2220 2221 if dbch_devicetype==self.DBT_DEVTYP_PORT: 2222 name="" 2223 for b in buf[struct.calcsize(format):]: 2224 if b!="\x00": 2225 name+=b 2226 continue 2227 break 2228 return ("name", name), 2229 2230 if dbch_devicetype==self.DBT_DEVTYP_VOLUME: 2231 # yes, the last item is a WORD, not a DWORD like the hungarian would lead you to think 2232 format="iiiih0i" 2233 dbcv_size, dbcv_devicetype, dbcv_reserved, dbcv_unitmask, dbcv_flags = struct.unpack(format, buf) 2234 units=[chr(ord('A')+x) for x in range(26) if dbcv_unitmask&(2**x)] 2235 flag="" 2236 for name in dir(self): 2237 if name.startswith("DBTF_") and getattr(self, name)==dbcv_flags: 2238 flag=name 2239 break 2240 2241 return ("drives", units), ("flag", flag) 2242 2243 print "unhandled devicetype struct", dbch_devicetype 2244 return () 2245
Generated by PyXR 0.9.4