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

Source Code for Module gui

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 
   4  ### 
   5  ### This program is free software; you can redistribute it and/or modify 
   6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   7  ### 
   8  ### $Id: gui.py 4768 2009-11-06 02:17:29Z hjelmn $ 
   9   
  10  """The main gui code for BitPim"""  
  11   
  12  # System modules 
  13  from __future__ import with_statement 
  14  import contextlib 
  15  import thread, threading 
  16  import Queue 
  17  import time 
  18  import os 
  19  import cStringIO 
  20  import zipfile 
  21  import re 
  22  import sys 
  23  import shutil 
  24  import types 
  25  import datetime 
  26  import sha 
  27  import codecs 
  28   
  29  # wx modules 
  30  import wx 
  31  import wx.lib.colourdb 
  32  import wx.html 
  33   
  34  # my modules 
  35  import guiwidgets 
  36  import common 
  37  import version 
  38  import helpids 
  39  import comdiagnose 
  40  import phonebook 
  41  import importexport 
  42  import guihelper 
  43  import bphtml 
  44  import bitflingscan 
  45  import update 
  46  import phoneinfo 
  47  import phone_detect 
  48  import phone_media_codec 
  49  import pubsub 
  50  import phones.com_brew as com_brew 
  51  import auto_sync 
  52  import phone_root 
  53  import playlist 
  54  import fileview 
  55  import data_recording 
  56  import analyser 
  57  import t9editor 
  58  import newdb_wiz 
  59  import bp_config 
  60   
  61  if guihelper.IsMSWindows(): 
  62      import win32api 
  63      import win32con 
  64      import win32gui 
  65      import msvcrt 
  66  else: 
  67      import fcntl 
  68   
  69  ### 
  70  ### Used to check our threading 
  71  ### 
  72  mainthreadid=thread.get_ident() 
  73  helperthreadid=-1 # set later 
  74   
  75  ### 
  76  ### Used to handle Task Bar Icon feature (Windows only) 
  77  ### 
  78  if guihelper.IsMSWindows(): 
79 - class TaskBarIcon(wx.TaskBarIcon):
80 - def __init__(self, mw):
81 super(TaskBarIcon, self).__init__() 82 self.mw=mw 83 self._set_icon() 84 wx.EVT_TASKBAR_LEFT_DCLICK(self, self.OnDclkRestore)
85
86 - def _create_menu(self):
87 _menu=wx.Menu() 88 _id=wx.NewId() 89 if self.mw.IsIconized(): 90 _menu.Append(_id, 'Restore') 91 wx.EVT_MENU(self, _id, self.OnRestore) 92 else: 93 _menu.Append(_id, 'Minimize') 94 wx.EVT_MENU(self, _id, self.OnMinimize) 95 _menu.AppendSeparator() 96 _id=wx.NewId() 97 _menu.Append(_id, 'Close') 98 wx.EVT_MENU(self, _id, self.OnClose) 99 return _menu
100
101 - def _set_icon(self):
102 _icon=wx.Icon(guihelper.getresourcefile('bitpim.ico'), 103 wx.BITMAP_TYPE_ICO) 104 if _icon.Ok(): 105 self.SetIcon(_icon, 'BitPim')
106
107 - def CreatePopupMenu(self):
108 return self._create_menu()
109 - def OnDclkRestore(self, _):
110 self.mw.Iconize(False) 111 wx.PostEvent(self.mw, wx.IconizeEvent(self.mw.GetId(), False))
112 - def OnRestore(self, _):
113 self.mw.Iconize(False)
114 - def OnMinimize(self, _):
115 self.mw.Iconize(True)
116 - def OnClose(self, _):
117 self.RemoveIcon() 118 self.mw.Close()
119 120 ### 121 ### Implements a nice flexible callback object 122 ### 123
124 -class Callback:
125 "Callback class. Extra arguments can be supplied at call time"
126 - def __init__(self, method, *args, **kwargs):
127 if __debug__: 128 global mainthreadid 129 assert mainthreadid==thread.get_ident() 130 self.method=method 131 self.args=args 132 self.kwargs=kwargs
133
134 - def __call__(self, *args, **kwargs):
135 if __debug__: 136 global mainthreadid 137 assert mainthreadid==thread.get_ident() 138 d=self.kwargs.copy() 139 d.update(kwargs) 140 apply(self.method, self.args+args, d)
141
142 -class Request:
143 - def __init__(self, method, *args, **kwargs):
144 # created in main thread 145 if __debug__: 146 global mainthreadid 147 assert mainthreadid==thread.get_ident() 148 self.method=method 149 self.args=args 150 self.kwargs=kwargs
151
152 - def __call__(self, *args, **kwargs):
153 # called in helper thread 154 if __debug__: 155 global helperthreadid 156 assert helperthreadid==thread.get_ident() 157 d=self.kwargs.copy() 158 d.update(kwargs) 159 return apply(self.method, self.args+args, d)
160 161 162 ### 163 ### Event used for passing results back from helper thread 164 ### 165
166 -class HelperReturnEvent(wx.PyEvent):
167 - def __init__(self, callback, *args, **kwargs):
168 if __debug__: 169 # verify being called in comm worker thread 170 global helperthreadid 171 ## assert helperthreadid==thread.get_ident() 172 global EVT_CALLBACK 173 wx.PyEvent.__init__(self) 174 self.SetEventType(EVT_CALLBACK) 175 self.cb=callback 176 self.args=args 177 self.kwargs=kwargs
178
179 - def __call__(self):
180 if __debug__: 181 global mainthreadid 182 ## assert mainthreadid==thread.get_ident() 183 return apply(self.cb, self.args, self.kwargs)
184 185 ### 186 ### Our helper thread where all the work gets done 187 ### 188 189 thesplashscreen=None # set to non-none if there is one 190
191 -class MySplashScreen(wx.SplashScreen):
192 - def __init__(self, app, config):
193 self.app=app 194 # how long are we going to be up for? 195 time=config.ReadInt("splashscreentime", 2500) 196 if time>0: 197 bmp=guihelper.getbitmap("splashscreen") 198 self.drawnameandnumber(bmp) 199 wx.SplashScreen.__init__(self, bmp, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT, 200 time, 201 None, -1) 202 wx.EVT_CLOSE(self, self.OnClose) 203 self.Show() 204 app.Yield(True) 205 global thesplashscreen 206 thesplashscreen=self 207 return 208 # timeout is <=0 so don't show splash screen 209 self.goforit()
210
211 - def drawnameandnumber(self, bmp):
212 dc=wx.MemoryDC() 213 dc.SelectObject(bmp) 214 # where we start writing 215 x=23 216 y=40 217 # Product name 218 if False: 219 str=version.name 220 dc.SetTextForeground( wx.NamedColour("MEDIUMORCHID4") ) 221 dc.SetFont( self._gimmethedamnsizeirequested(25, wx.ROMAN, wx.NORMAL, wx.NORMAL) ) 222 w,h=dc.GetTextExtent(str) 223 dc.DrawText(str, x, y) 224 y+=h+0 225 # Version number 226 x=58 227 y=127 228 str=version.versionstring+"-"+version.vendor 229 dc.SetTextForeground( wx.NamedColour("MEDIUMBLUE") ) 230 dc.SetFont( self._gimmethedamnsizeirequested(15, wx.ROMAN, wx.NORMAL, wx.NORMAL) ) 231 w,h=dc.GetTextExtent(str) 232 dc.DrawText(str, x+10, y) 233 y+=h+0 234 # all done 235 dc.SelectObject(wx.NullBitmap)
236
237 - def _gimmethedamnsizeirequested(self, ps, family, style, weight):
238 # on Linux we have to ask for bigger than we want 239 if guihelper.IsGtk(): 240 ps=ps*1.6 241 font=wx.TheFontList.FindOrCreateFont(int(ps), family, style, weight) 242 return font
243
244 - def goforit(self):
245 self.app.makemainwindow()
246
247 - def OnClose(self, evt):
248 self.goforit() 249 evt.Skip()
250
251 -class BitPimExit(Exception):
252 pass
253
254 -class WorkerThreadFramework(threading.Thread):
255 - def __init__(self):
256 threading.Thread.__init__(self, name="BitPim helper") 257 self.q=Queue.Queue()
258
259 - def setdispatch(self, dispatchto):
260 self.dispatchto=dispatchto
261
262 - def checkthread(self):
263 # Function to verify we are running in the correct 264 # thread. All functions in derived class should call this 265 global helperthreadid 266 assert helperthreadid==thread.get_ident()
267
268 - def run(self):
269 global helperthreadid 270 helperthreadid=thread.get_ident() 271 first=1 272 while True: 273 if not first: 274 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.endbusycb)) 275 else: 276 first=0 277 item=self.q.get() 278 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.startbusycb)) 279 call=item[0] 280 resultcb=item[1] 281 ex=None 282 res=None 283 try: 284 res=call() 285 except Exception,e: 286 ex=e 287 if not hasattr(e,"gui_exc_info"): 288 ex.gui_exc_info=sys.exc_info() 289 290 wx.PostEvent(self.dispatchto, HelperReturnEvent(resultcb, ex, res)) 291 if isinstance(ex, BitPimExit): 292 # gracefully end this thread! 293 break
294
295 - def progressminor(self, pos, max, desc=""):
296 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.progressminorcb, pos, max, desc))
297
298 - def progressmajor(self, pos, max, desc=""):
299 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.progressmajorcb, pos, max, desc))
300
301 - def progress(self, pos, max, desc=""):
302 self.progressminor(pos, max, desc)
303
304 - def log(self, str):
305 if self.dispatchto.wantlog: 306 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.logcb, str))
307
308 - def logdata(self, str, data, klass=None, data_type=None):
309 if self.dispatchto.wantlog: 310 wx.PostEvent(self.dispatchto, HelperReturnEvent(self.dispatchto.logdatacb, str, data, klass, 311 data_type))
312 313 #### 314 #### Main application class. Runs the event loop etc 315 #### 316 317 # safe mode items
318 -def _notsafefunc(*args, **kwargs):
319 raise common.InSafeModeException()
320
321 -class _NotSafeObject:
322 - def __getattr__(self, *args): _notsafefunc()
323 - def __setattr__(self, *args): _notsafefunc()
324 325 _NotSafeObject=_NotSafeObject() 326
327 -class Event(object):
328 """Simple Event class that supports Context Manager"""
329 - def __init__(self):
330 self._event=threading.Event()
331 - def __enter__(self):
332 self._event.set()
333 - def __exit__(self, exc_type, exc_value, tb):
334 self._event.clear()
335 - def set(self):
336 return self._event.set()
337 - def clear(self):
338 return self._event.clear()
339 - def isSet(self):
340 return self._event.isSet()
341 - def wait(self, timeout=None):
342 return self._event.wait(timeout)
343 344 EVT_CALLBACK=None
345 -class MainApp(wx.App):
346 - def __init__(self, argv, config_filename=None):
347 self.frame=None 348 self.SAFEMODE=False 349 codecs.register(phone_media_codec.search_func) 350 self._config_filename=config_filename 351 # simple Event object to flag when entering/leaving critical section 352 self.critical=Event() 353 wx.App.__init__(self, redirect=False, 354 useBestVisual=not guihelper.IsGtk())
355
356 - def lock_file(self, filename):
357 # if the file can be locked, lock it and return True. 358 # return False otherwise. 359 try: 360 self.lockedfile=file(filename, 'w') 361 except IOError: 362 # failed to create the file, just bail 363 self.lockedfile=None 364 return True 365 try: 366 if guihelper.IsMSWindows(): 367 msvcrt.locking(self.lockedfile.fileno(), 368 msvcrt.LK_NBLCK, 1) 369 else: 370 # Linux & Mac 371 fcntl.flock(self.lockedfile.fileno(), 372 fcntl.LOCK_EX|fcntl.LOCK_NB) 373 return True 374 except IOError: 375 return False
376
377 - def usingsamedb(self):
378 # using a simple file locking method 379 return not self.lock_file(os.path.join(self.config._path, '.lock'))
380
381 - def OnInit(self):
382 self.made=False 383 # Routine maintenance 384 wx.lib.colourdb.updateColourDB() 385 386 # Thread stuff 387 global mainthreadid 388 mainthreadid=thread.get_ident() 389 390 # for help to save prefs 391 cfgstr='bitpim' 392 self.SetAppName(cfgstr) 393 self.SetVendorName(cfgstr) 394 395 # Establish config stuff 396 self.config=bp_config.Config(self._config_filename) 397 # Check to see if we're the 2nd instance running on the same DB 398 if self.usingsamedb(): 399 guihelper.MessageDialog(None, 'Another copy of BitPim is using the same data dir:\n%s'%self.config._path, 400 'BitPim Error', 401 style=wx.OK|wx.ICON_ERROR) 402 return False 403 # this is for wx native use, like the freaking help controller ! 404 self.wxconfig=wx.Config(cfgstr, style=wx.CONFIG_USE_LOCAL_FILE) 405 406 # safe mode is read at startup and can't be changed 407 self.SAFEMODE=self.config.ReadInt("SafeMode", False) 408 409 # we used to initialise help here, but in wxPython the stupid help window 410 # appeared on Windows just setting it up. We now defer setting it up 411 # until it is needed 412 self.helpcontroller=None 413 414 # html easy printing 415 self.htmlprinter=bphtml.HtmlEasyPrinting(None, self.config, "printing") 416 417 global EVT_CALLBACK 418 EVT_CALLBACK=wx.NewEventType() 419 420 # initialize the Brew file cache 421 com_brew.file_cache=com_brew.FileCache(self.config.Read('path', '')) 422 423 # get the splash screen up 424 MySplashScreen(self, self.config) 425 426 return True
427
428 - def ApplySafeMode(self):
429 # make very sure we are in safe mode 430 if not self.SAFEMODE: 431 return 432 if self.frame is None: 433 return 434 # ensure various objects/functions are changed to not-safe 435 objects={self.frame: 436 ( "dlgsendphone", "OnDataSendPhone", "OnDataSendPhoneGotFundamentals", "OnDataSendPhoneResults"), 437 self.frame.tree.filesystemwidget: 438 ( "OnFileDelete", "OnFileOverwrite", "OnNewSubdir", "OnNewFile", "OnDirDelete", "OnRestore"), 439 self.frame.wt: 440 ( "senddata", "writewallpaper", "writeringtone", "writephonebook", "writecalendar", "rmfile", 441 "writefile", "mkdir", "rmdir", "rmdirs", "restorefiles" ), 442 self.frame.phoneprofile: 443 ( "convertphonebooktophone", ), 444 self.frame.phonemodule.Phone: 445 ( "mkdir", "mkdirs", "rmdir", "rmfile", "rmdirs", "writefile", "savegroups", "savephonebook", 446 "savecalendar", "savewallpapers", "saveringtones") 447 } 448 449 for obj, names in objects.iteritems(): 450 if obj is None: 451 continue 452 for name in names: 453 field=getattr(obj, name, None) 454 if field is None or field is _notsafefunc or field is _NotSafeObject: 455 continue 456 if isinstance(field, (types.MethodType, types.FunctionType)): 457 newval=_notsafefunc 458 else: newval=_NotSafeObject 459 setattr(obj, name, newval) 460 461 # remove various menu items if we can find them 462 removeids=(guihelper.ID_DATASENDPHONE, guihelper.ID_FV_OVERWRITE, guihelper.ID_FV_NEWSUBDIR, 463 guihelper.ID_FV_NEWFILE, guihelper.ID_FV_DELETE, guihelper.ID_FV_RENAME, 464 guihelper.ID_FV_RESTORE, guihelper.ID_FV_ADD) 465 mb=self.frame.GetMenuBar() 466 menus=[mb.GetMenu(i) for i in range(mb.GetMenuCount())] 467 fsw=self.frame.tree.filesystemwidget 468 if fsw is not None: 469 menus.extend( [fsw.list.filemenu, fsw.tree.dirmenu, fsw.list.genericmenu] ) 470 for menu in menus: 471 for id in removeids: 472 item=menu.FindItemById(id) 473 if item is not None: 474 menu.RemoveItem(item)
475 476 477 478 479 ## def setuphelpiwant(self): 480 ## """This is how the setuphelp code is supposed to be, but stuff is missing from wx""" 481 ## self.helpcontroller=wx.BestHelpController() 482 ## self.helpcontroller.Initialize(gethelpfilename) 483
484 - def _setuphelp(self):
485 """Does all the nonsense to get help working""" 486 if guihelper.IsMSWindows(): 487 self.helpcontroller=True 488 return 489 elif guihelper.IsMac(): 490 # we use apple's help mechanism 491 from Carbon import AH 492 path=os.path.abspath(os.path.join(guihelper.resourcedirectory, "..", "..", "..")) 493 # path won't exist if we aren't a bundle 494 if os.path.exists(path) and path.endswith(".app"): 495 res=AH.AHRegisterHelpBook(path) 496 self.helpcontroller=True 497 return 498 499 # Standard WX style help 500 # htmlhelp isn't correctly wrapper in wx package 501 # Add the Zip filesystem 502 wx.FileSystem_AddHandler(wx.ZipFSHandler()) 503 # Get the help working 504 self.helpcontroller=wx.html.HtmlHelpController() 505 self.helpcontroller.AddBook(guihelper.gethelpfilename()+".htb") 506 self.helpcontroller.UseConfig(self.wxconfig, "help")
507 508 # now context help 509 # (currently borken) 510 # self.helpprovider=wx.HelpControllerHelpProvider(self.helpcontroller) 511 # wx.HelpProvider_Set(provider) 512
513 - def displayhelpid(self, id):
514 """Display a specific Help Topic""" 515 if self.helpcontroller is None: 516 self._setuphelp() 517 518 if guihelper.IsMSWindows(): 519 import win32help 520 fname=guihelper.gethelpfilename()+".chm>Help" 521 if id is None: 522 id=helpids.ID_WELCOME 523 # display the topic 524 _hwnd=win32gui.GetDesktopWindow() 525 win32help.HtmlHelp(_hwnd, fname, win32help.HH_DISPLAY_TOPIC, id) 526 # and sync the TOC 527 win32help.HtmlHelp(_hwnd, fname, win32help.HH_SYNC, id) 528 529 elif guihelper.IsMac() and self.helpcontroller is True: 530 from Carbon import AH 531 res=AH.AHGotoPage('BitPim Help', id, None) 532 533 else: 534 if id is None: 535 self.helpcontroller.DisplayContents() 536 else: 537 self.helpcontroller.Display(id)
538
539 - def makemainwindow(self):
540 if self.made: 541 return # already been called 542 self.made=True 543 # make the main frame 544 title='BitPim' 545 name=self.config.Read('name', None) 546 if name: 547 title+=' - '+name 548 self.frame=MainWindow(None, -1, title, self.config) 549 self.frame.Connect(-1, -1, EVT_CALLBACK, self.frame.OnCallback) 550 if guihelper.IsMac(): 551 self.frame.MacSetMetalAppearance(True) 552 553 # make the worker thread 554 wt=WorkerThread() 555 wt.setdispatch(self.frame) 556 wt.setDaemon(1) 557 wt.start() 558 self.frame.wt=wt 559 self.SetTopWindow(self.frame) 560 self.SetExitOnFrameDelete(True) 561 self.ApplySafeMode() 562 wx.CallAfter(self.CheckDetectPhone) 563 wx.CallAfter(self.CheckUpdate) 564 # double-check the locked file 565 if self.lockedfile is None: 566 self.usingsamedb()
567 568 update_delta={ 'Daily': 1, 'Weekly': 7, 'Monthly': 30 }
569 - def CheckUpdate(self):
570 if version.isdevelopmentversion(): 571 return 572 if self.frame is None: 573 return 574 # tell the frame to do a check-for-update 575 update_rate=self.config.Read('updaterate', '') 576 if not len(update_rate) or update_rate =='Never': 577 return 578 last_update=self.config.Read('last_update', '') 579 try: 580 if len(last_update): 581 last_date=datetime.date(int(last_update[:4]), int(last_update[4:6]), 582 int(last_update[6:])) 583 next_date=last_date+datetime.timedelta(\ 584 self.update_delta.get(update_rate, 7)) 585 else: 586 next_date=last_date=datetime.date.today() 587 except ValueError: 588 # month day swap problem 589 next_date=last_date=datetime.date.today() 590 if datetime.date.today()<next_date: 591 return 592 self.frame.AddPendingEvent(\ 593 wx.PyCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, 594 guihelper.ID_HELP_UPDATE))
595
596 - def CheckDetectPhone(self):
597 if self.config.ReadInt('autodetectstart', 0) or self.frame.needconfig: 598 self.frame.AddPendingEvent( 599 wx.PyCommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, 600 guihelper.ID_EDITDETECT))
601
602 - def OnExit(self):
603 self.config.Flush() 604 # we get stupid messages about daemon threads, and Python's library 605 # doesn't provide any way to interrupt them, nor to suppress these 606 # messages. ::TODO:: maybe remove the onexit handler installed by 607 # treading._MainThread 608 sys.excepthook=donothingexceptionhandler
609
610 - def ExitMainLoop(self):
611 if guihelper.IsGtk(): 612 # This hangs for GTK, so what the heck! 613 self.OnExit() 614 sys.exit(0) 615 super(MainApp, self).ExitMainLoop()
616 617 # do nothing exception handler
618 -def donothingexceptionhandler(*args):
619 pass
620 621 # Entry point
622 -def run(argv, kwargs):
623 return MainApp(argv, **kwargs).MainLoop()
624 625 ### 626 ### Main Window (frame) class 627 ### 628 638
639 -class MainWindow(wx.Frame):
640 - def __init__(self, parent, id, title, config):
641 wx.Frame.__init__(self, parent, id, title, 642 style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE) 643 wx.GetApp().frame=self 644 645 wx.GetApp().htmlprinter.SetParentFrame(self) 646 647 sys.excepthook=Callback(self.excepthook) 648 ### plumbing, callbacks 649 self.wt=None # worker thread 650 self.progressminorcb=Callback(self.OnProgressMinor) 651 self.progressmajorcb=Callback(self.OnProgressMajor) 652 self.logcb=Callback(self.OnLog) 653 self.logdatacb=Callback(self.OnLogData) 654 self.startbusycb=Callback(self.OnBusyStart) 655 self.endbusycb=Callback(self.OnBusyEnd) 656 self.queue=Queue.Queue() 657 658 ### random variables 659 self.exceptiondialog=None 660 self.wantlog=1 # do we want to receive log information 661 self.config=config 662 self.progmajortext="" 663 self.__owner_name='' 664 665 self._taskbar=None 666 self._taskbar_on_closed=False 667 self._close_button=False 668 self.__phone_detect_at_startup=False 669 self._autodetect_delay=0 670 self._dr_rec=None # Data Recording 671 self._dr_play=None # Data Play back 672 673 ### Status bar 674 675 sb=guiwidgets.MyStatusBar(self) 676 self.SetStatusBar(sb) 677 self.SetStatusBarPane(sb.GetHelpPane()) 678 679 ### Art 680 # establish the custom art provider for custom icons 681 # this is a global setting, so no need to call it for each toolbar 682 wx.ArtProvider.PushProvider(guihelper.ArtProvider()) 683 684 # frame icon 685 ib=wx.IconBundle() 686 ib.AddIconFromFile(guihelper.getresourcefile("bitpim.ico"), wx.BITMAP_TYPE_ANY) 687 self.SetIcons(ib) 688 689 ### Menubar 690 691 menuBar = wx.MenuBar() 692 menu = wx.Menu() 693 # menu.Append(guihelper.ID_FILENEW, "&New", "Start from new") 694 # menu.Append(guihelper.ID_FILEOPEN, "&Open", "Open a file") 695 # menu.Append(guihelper.ID_FILESAVE, "&Save", "Save your work") 696 menu.Append(guihelper.ID_FILEPRINT, "&Print...", "Print phonebook") 697 # menu.AppendSeparator() 698 699 # imports 700 impmenu=wx.Menu() 701 for x, desc, help, func in importexport.GetPhonebookImports(): 702 if isinstance(func, tuple): 703 # submenu 704 _submenu=wx.Menu() 705 for _id, _desc, _help, _func in func: 706 _submenu.Append(_id, _desc, _help) 707 if _func: 708 wx.EVT_MENU(self, _id, MenuCallback(_func, self)) 709 impmenu.AppendMenu(x, desc, _submenu, help) 710 else: 711 impmenu.Append(x, desc, help) 712 wx.EVT_MENU(self, x, MenuCallback(func, self) ) 713 714 menu.AppendMenu(guihelper.ID_FILEIMPORT, "&Import", impmenu) 715 716 # exports 717 expmenu=wx.Menu() 718 for x, desc, help, func in importexport.GetPhonebookExports(): 719 expmenu.Append(x, desc, help) 720 wx.EVT_MENU(self, x, MenuCallback(func, self) ) 721 722 menu.AppendMenu(guihelper.ID_FILEEXPORT, "&Export", expmenu) 723 724 if not guihelper.IsMac(): 725 menu.AppendSeparator() 726 menu.Append(guihelper.ID_FILEEXIT, "E&xit", "Close down this program") 727 menuBar.Append(menu, "&File"); 728 self.__menu_edit=menu=wx.Menu() 729 menu.Append(guihelper.ID_EDITSELECTALL, "&Select All\tCtrl+A", "Select All") 730 menu.AppendSeparator() 731 menu.Append(guihelper.ID_EDITADDENTRY, "&New...\tCtrl+N", "Add an item") 732 menu.Append(guihelper.ID_EDITCOPY, "&Copy\tCtrl+C", "Copy to the clipboard") 733 menu.Append(guihelper.ID_EDITPASTE,"&Paste\tCtrl+V", "Paste from the clipboard") 734 menu.Append(guihelper.ID_EDITDELETEENTRY, "&Delete", "Delete currently selected entry") 735 menu.Append(guihelper.ID_EDITRENAME, "&Rename\tF2", "Rename currently selected entry") 736 menu.AppendSeparator() 737 menu.Append(guihelper.ID_EDITDETECT, 738 "D&etect Phone", "Auto Detect Phone") 739 if guihelper.IsMac(): 740 wx.App_SetMacPreferencesMenuItemId(guihelper.ID_EDITSETTINGS) 741 menu.Append(guihelper.ID_EDITSETTINGS, "Pre&ferences...", "Edit Settings") 742 else: 743 menu.AppendSeparator() 744 menu.Append(guihelper.ID_EDITSETTINGS, "&Settings", "Edit settings") 745 menuBar.Append(menu, "&Edit"); 746 747 menu=wx.Menu() 748 menu.Append(guihelper.ID_DATAGETPHONE, "Get Phone &Data ...", "Loads data from the phone") 749 menu.Append(guihelper.ID_DATASENDPHONE, "&Send Phone Data ...", "Sends data to the phone") 750 menu.Append(guihelper.ID_DATAHISTORICAL, "&Historical Data ...", "View Current & Historical Data") 751 menu.AppendSeparator() 752 menu.Append(guihelper.ID_DATANEWDB, 'Create New Storage ...', 753 'Create a New BitPim Storage Area') 754 menuBar.Append(menu, "&Data") 755 756 menu=wx.Menu() 757 menu.Append(guihelper.ID_VIEWCOLUMNS, "&Columns ...", "Which columns to show") 758 menu.AppendCheckItem(guihelper.ID_VIEWPREVIEW, "&Phonebook Preview", "Toggle Phonebook Preview Pane") 759 menu.AppendSeparator() 760 menu.AppendCheckItem(guihelper.ID_VIEWLOGDATA, "&View protocol logging", "View protocol logging information") 761 menu.Append(guihelper.ID_VIEWCLEARLOGS, "Clear &Logs", "Clears the contents of the log panes") 762 menu.AppendSeparator() 763 menu.AppendCheckItem(guihelper.ID_VIEWFILESYSTEM, "View &Filesystem", "View filesystem on the phone") 764 menu.AppendSeparator() 765 menu.Append(guihelper.ID_EDITPHONEINFO, 766 "Phone &Info", "Display Phone Information") 767 menuBar.Append(menu, "&View") 768 # Debug menu 769 menu=wx.Menu() 770 menu.Append(guihelper.ID_DR_SETTINGS, '&Data Recording', 771 'Data Recording Settings') 772 ## menu.Append(guihelper.ID_DEBUG_SCRIPT, '&Script', 773 ## 'Run Debug Script') 774 menuBar.Append(menu, "De&bug") 775 # Help menu 776 menu=wx.Menu() 777 if guihelper.IsMac(): 778 menu.Append(guihelper.ID_HELPHELP, "&Help on this panel", "Help for the panel you are looking at") 779 else: 780 menu.Append(guihelper.ID_HELPHELP, "&Help", "Help for the panel you are looking at") 781 menu.Append(guihelper.ID_HELPTOUR, "&Tour", "Tour of BitPim") 782 menu.Append(guihelper.ID_HELPCONTENTS, "&Contents", "Table of contents for the online help") 783 menu.Append(guihelper.ID_HELPHOWTOS, "H&owTos", "Help on how to do certain function") 784 menu.Append(guihelper.ID_HELPFAQ, "&FAQ", "Frequently Asked Questions") 785 menu.Append(guihelper.ID_HELPSUPPORT, "&Support", "Getting support for BitPim") 786 menu.Append(guihelper.ID_HELPPHONE, "Your &Phone", "Help on specific phonemodel") 787 if version.vendor=='official': 788 menu.AppendSeparator() 789 menu.Append(guihelper.ID_HELP_UPDATE, "&Check for Update", "Check for any BitPim Update") 790 if guihelper.IsMac(): 791 wx.App_SetMacAboutMenuItemId(guihelper.ID_HELPABOUT) 792 menu.Append(guihelper.ID_HELPABOUT, "&About BitPim", "Display program information") 793 wx.App_SetMacHelpMenuTitleName("&Help") 794 wx.App_SetMacExitMenuItemId(guihelper.ID_FILEEXIT) 795 else: 796 menu.AppendSeparator() 797 menu.Append(guihelper.ID_HELPABOUT, "&About", "Display program information") 798 menuBar.Append(menu, "&Help"); 799 self.SetMenuBar(menuBar) 800 801 ### toolbar 802 self.tb=self.CreateToolBar(wx.TB_HORIZONTAL) 803 self.tb.SetToolBitmapSize(wx.Size(32,32)) 804 sz=self.tb.GetToolBitmapSize() 805 806 # add and delete tools 807 self.tb.AddSimpleTool(guihelper.ID_DATAGETPHONE, wx.ArtProvider.GetBitmap(guihelper.ART_DATAGETPHONE, wx.ART_TOOLBAR, sz), 808 "Get Phone Data", "Synchronize BitPim with Phone") 809 self.tb.AddLabelTool(guihelper.ID_DATASENDPHONE, "Send Phone Data", wx.ArtProvider.GetBitmap(guihelper.ART_DATASENDPHONE, wx.ART_TOOLBAR, sz), 810 shortHelp="Send Phone Data", longHelp="Synchronize Phone with BitPim") 811 self.tb.AddLabelTool(guihelper.ID_DATAHISTORICAL, "BitPim Help", wx.ArtProvider.GetBitmap(guihelper.ART_DATAHISTORICAL, wx.ART_TOOLBAR, sz), 812 shortHelp="Historical Data", longHelp="Show Historical Data") 813 self.tb.AddSeparator() 814 self.tb.AddLabelTool(guihelper.ID_EDITADDENTRY, "Add", wx.ArtProvider.GetBitmap(wx.ART_ADD_BOOKMARK, wx.ART_TOOLBAR, sz), 815 shortHelp="Add", longHelp="Add an item") 816 self.tb.AddLabelTool(guihelper.ID_EDITDELETEENTRY, "Delete", wx.ArtProvider.GetBitmap(wx.ART_DEL_BOOKMARK, wx.ART_TOOLBAR, sz), 817 shortHelp="Delete", longHelp="Delete item") 818 self.tb.AddLabelTool(guihelper.ID_EDITPHONEINFO, "Phone Info", wx.ArtProvider.GetBitmap(guihelper.ART_EDITPHONEINFO, wx.ART_TOOLBAR, sz), 819 shortHelp="Phone Info", longHelp="Show Phone Info") 820 self.tb.AddLabelTool(guihelper.ID_EDITDETECT, "Find Phone", wx.ArtProvider.GetBitmap(guihelper.ART_EDITDETECT, wx.ART_TOOLBAR, sz), 821 shortHelp="Find Phone", longHelp="Find Phone") 822 self.tb.AddLabelTool(guihelper.ID_EDITSETTINGS, "Edit Settings", wx.ArtProvider.GetBitmap(guihelper.ART_EDITSETTINGS, wx.ART_TOOLBAR, sz), 823 shortHelp="Edit Settings", longHelp="Edit BitPim Settings") 824 self.tb.AddSeparator() 825 self.tb.AddSimpleTool(guihelper.ID_AUTOSYNCEXECUTE, wx.ArtProvider.GetBitmap(guihelper.ART_AUTOSYNCEXECUTE, wx.ART_TOOLBAR, sz), 826 "Autosync Calendar", "Synchronize Phone Calendar with PC") 827 self.tb.AddSeparator() 828 self.tb.AddLabelTool(guihelper.ID_HELPHELP, "BitPim Help", wx.ArtProvider.GetBitmap(guihelper.ART_HELPHELP, wx.ART_TOOLBAR, sz), 829 shortHelp="BitPim Help", longHelp="BitPim Help") 830 831 832 # You have to make this call for the toolbar to draw itself properly 833 self.tb.Realize() 834 835 ### persistent dialogs 836 self.dlggetphone=guiwidgets.GetPhoneDialog(self, "Get Data from Phone") 837 self.dlgsendphone=guiwidgets.SendPhoneDialog(self, "Send Data to Phone") 838 839 # the splitter 840 self.sw=wx.SplitterWindow(self, wx.NewId(), style=wx.SP_3D|wx.SP_NO_XP_THEME|wx.SP_LIVE_UPDATE) 841 842 ### create main tree view 843 self.tree = phone_root.PhoneTree(self.sw, self, wx.NewId()) 844 845 ### Events we handle 846 wx.EVT_MENU(self, guihelper.ID_FILEPRINT, self.tree.OnFilePrint) 847 wx.EVT_MENU(self, guihelper.ID_FILEEXIT, self.OnExit) 848 wx.EVT_MENU(self, guihelper.ID_EDITSETTINGS, self.OnEditSettings) 849 wx.EVT_MENU(self, guihelper.ID_DATAGETPHONE, self.OnDataGetPhone) 850 wx.EVT_MENU(self, guihelper.ID_DATASENDPHONE, self.OnDataSendPhone) 851 wx.EVT_MENU(self, guihelper.ID_DATAHISTORICAL, self.tree.OnDataHistorical) 852 wx.EVT_MENU(self, guihelper.ID_DATANEWDB, self.OnNewDB) 853 wx.EVT_MENU(self, guihelper.ID_VIEWCOLUMNS, self.tree.OnViewColumns) 854 wx.EVT_MENU(self, guihelper.ID_VIEWPREVIEW, self.tree.OnViewPreview) 855 wx.EVT_MENU(self, guihelper.ID_VIEWCLEARLOGS, self.tree.OnViewClearLogs) 856 wx.EVT_MENU(self, guihelper.ID_VIEWLOGDATA, self.tree.OnViewLogData) 857 wx.EVT_MENU(self, guihelper.ID_VIEWFILESYSTEM, self.tree.OnViewFilesystem) 858 wx.EVT_MENU(self, guihelper.ID_EDITADDENTRY, self.tree.OnEditAddEntry) 859 wx.EVT_MENU(self, guihelper.ID_EDITDELETEENTRY, self.tree.OnEditDeleteEntry) 860 wx.EVT_MENU(self, guihelper.ID_EDITSELECTALL, self.tree.OnEditSelectAll) 861 wx.EVT_MENU(self, guihelper.ID_EDITCOPY, self.tree.OnCopyEntry) 862 wx.EVT_MENU(self, guihelper.ID_EDITPASTE, self.tree.OnPasteEntry) 863 wx.EVT_MENU(self, guihelper.ID_EDITRENAME, self.tree.OnRenameEntry) 864 wx.EVT_MENU(self, guihelper.ID_HELPABOUT, self.OnHelpAbout) 865 wx.EVT_MENU(self, guihelper.ID_HELPHELP, self.OnHelpHelp) 866 wx.EVT_MENU(self, guihelper.ID_HELPCONTENTS, self.OnHelpContents) 867 wx.EVT_MENU(self, guihelper.ID_HELPHOWTOS, self.OnHelpHowtos) 868 wx.EVT_MENU(self, guihelper.ID_HELPFAQ, self.OnHelpFAQ) 869 wx.EVT_MENU(self, guihelper.ID_HELPSUPPORT, self.OnHelpSupport) 870 wx.EVT_MENU(self, guihelper.ID_HELPTOUR, self.OnHelpTour) 871 wx.EVT_MENU(self, guihelper.ID_HELP_UPDATE, self.OnCheckUpdate) 872 wx.EVT_MENU(self, guihelper.ID_HELPPHONE, self.OnHelpPhone) 873 wx.EVT_MENU(self, guihelper.ID_EDITPHONEINFO, self.OnPhoneInfo) 874 wx.EVT_MENU(self, guihelper.ID_EDITDETECT, self.OnDetectPhone) 875 wx.EVT_MENU(self, guihelper.ID_AUTOSYNCSETTINGS, self.OnAutoSyncSettings) 876 wx.EVT_MENU(self, guihelper.ID_AUTOSYNCEXECUTE, self.OnAutoSyncExecute) 877 wx.EVT_MENU(self, guihelper.ID_DR_SETTINGS, self.OnDataRecording) 878 wx.EVT_CLOSE(self, self.OnClose) 879 880 ### Double check our size is meaningful, and make bigger 881 ### if necessary (especially needed on Mac and Linux) 882 if min(self.GetSize())<250: 883 self.SetSize( (640, 480) ) 884 885 ### Is config set? 886 self.configdlg=guiwidgets.ConfigDialog(self, self) 887 self.needconfig=self.configdlg.needconfig() 888 self.configdlg.updatevariables() 889 890 pos=self.config.ReadInt("mainwindowsplitterpos", 200) 891 self.tree.active_panel.OnPreActivate() 892 self.sw.SplitVertically(self.tree, self.tree.active_panel, pos) 893 self.tree.active_panel.OnPostActivate() 894 self.sw.SetMinimumPaneSize(50) 895 wx.EVT_SPLITTER_SASH_POS_CHANGED(self, id, self.OnSplitterPosChanged) 896 self.tree.Expand(self.tree.root) 897 898 # multiple phones can be added here, although we have to figure out which phone 899 # to use in send/get phone data. 900 self.tree.CreatePhone("Phone", self.config, self.configpath, "bitpim.db") 901 #self.tree.CreatePhone("Different database", self.config, "C:/Documents and Settings/Simon/My Documents/bitpim_old") 902 903 ### Set autosync settings dialog 904 self.calenders=importexport.GetCalenderAutoSyncImports() 905 self.autosyncsetting=auto_sync.AutoSyncSettingsDialog(self, self) 906 self.autosyncsetting.updatevariables() 907 self.CloseSplashScreen() 908 909 # add update handlers for controls that are not always available 910 wx.EVT_UPDATE_UI(self, guihelper.ID_AUTOSYNCEXECUTE, self.AutosyncUpdateUIEvent) 911 wx.EVT_UPDATE_UI(self, guihelper.ID_DATASENDPHONE, self.tree.DataSendPhoneUpdateUIEvent) 912 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITDELETEENTRY, self.tree.DataDeleteItemUpdateUIEvent) 913 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITADDENTRY, self.tree.DataAddItemUpdateUIEvent) 914 wx.EVT_UPDATE_UI(self, guihelper.ID_DATAHISTORICAL, self.tree.HistoricalDataUpdateUIEvent) 915 wx.EVT_UPDATE_UI(self, guihelper.ID_VIEWCOLUMNS, self.tree.ViewColumnsUpdateUIEvent) 916 wx.EVT_UPDATE_UI(self, guihelper.ID_VIEWPREVIEW, self.tree.ViewPreviewDataUpdateUIEvent) 917 wx.EVT_UPDATE_UI(self, guihelper.ID_FILEPRINT, self.tree.FilePrintDataUpdateUIEvent) 918 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITSELECTALL, self.tree.SelectAllDataUpdateUIEvent) 919 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITCOPY, self.tree.EditCopyUpdateUIEvent) 920 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITPASTE, self.tree.EditPasteUpdateUIEvent) 921 wx.EVT_UPDATE_UI(self, guihelper.ID_EDITRENAME, self.tree.EditRenameUpdateUIEvent) 922 wx.EVT_UPDATE_UI(self, guihelper.ID_VIEWLOGDATA, self.tree.ViewLogDataUIEvent) 923 wx.EVT_UPDATE_UI(self, guihelper.ID_VIEWFILESYSTEM, self.tree.ViewFileSystemUIEvent) 924 wx.EVT_UPDATE_UI(self, guihelper.ID_HELPPHONE, self.OnHelpPhoneUpdateUI) 925 926 # Retrieve saved settings... Use 90% of screen if not specified 927 guiwidgets.set_size("MainWin", self, screenpct=90) 928 929 ### Lets go visible 930 self.Show() 931 932 # Show tour on first use 933 if self.config.ReadInt("firstrun", True): 934 self.config.WriteInt("firstrun", False) 935 self.config.Flush() 936 wx.CallAfter(self.OnHelpTour) 937 938 # check for device changes 939 if guihelper.IsMSWindows(): 940 if self.config.ReadInt('taskbaricon', 0): 941 self._taskbar=TaskBarIcon(self) 942 self._taskbar_on_closed=self.config.ReadInt('taskbaricon1', 0) 943 # save the old window proc 944 self.oldwndproc = win32gui.SetWindowLong(self.GetHandle(), 945 win32con.GWL_WNDPROC, 946 self.MyWndProc) 947 if self._taskbar and self._taskbar.IsOk(): 948 wx.EVT_ICONIZE(self, self.OnIconize) 949 950 # response to pubsub request 951 pubsub.subscribe(self.OnReqChangeTab, pubsub.REQUEST_TAB_CHANGED) 952 # setup the midnight timer 953 self._setup_midnight_timer() 954 955 if self.IsIconized() and self._taskbar: 956 # Ugly hack to force the icon onto the system tray when 957 # the app is started minimized !! 958 wx.CallAfter(self.Show, False) 959 self.GetStatusBar().set_app_status_ready() 960 # Linux USB port notification 961 if guihelper.IsGtk(): 962 import comm_notify 963 comm_notify.start_server(self)
964
965 - def OnSplitterPosChanged(self,_):
966 pos=self.sw.GetSashPosition() 967 self.config.WriteInt("mainwindowsplitterpos", pos)
968
969 - def SetActivePanel(self, panel):
970 w2=self.sw.GetWindow2() 971 if w2 is None or w2 is panel: 972 return 973 panel.OnPreActivate() 974 w2.Show(False) 975 self.sw.ReplaceWindow(w2, panel) 976 panel.Show(True) 977 panel.SetFocus() 978 panel.OnPostActivate()
979
980 - def GetActiveMemoWidget(self):
981 return self.tree.GetActivePhone().memowidget
982
983 - def GetActiveMediaWidget(self):
984 return self.tree.GetActivePhone().mediawidget
985
986 - def GetActiveRingerWidget(self):
987 return self.tree.GetActivePhone().mediawidget.GetRinger()
988
989 - def GetActiveWallpaperWidget(self):
990 return self.tree.GetActivePhone().mediawidget.GetWallpaper()
991
992 - def GetActiveTodoWidget(self):
993 return self.tree.GetActivePhone().todowidget
994
995 - def GetActiveCalendarWidget(self):
996 return self.tree.GetActivePhone().calendarwidget
997
998 - def GetActivePlaylistWidget(self):
999 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,_):
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, _):
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
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, '') 1206 with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, "Owner's name:" , 1207 "Enter Phone Owner's Name", phone_name), 1208 True) as (dlg, r): 1209 if r==wx.ID_OK: 1210 # user gave a name 1211 phone_name=dlg.GetValue() 1212 self.config.Write(phone_id, phone_name) 1213 return phone_name
1214
1215 - def OnDetectPhoneReturn(self, check_auto_sync, silent_fail, exception, r):
1216 self._autodetect_delay=0 1217 self.OnBusyEnd() 1218 if self.HandleException(exception): return 1219 if r is None: 1220 if not silent_fail: 1221 self.__owner_name='' 1222 with guihelper.WXDialogWrapper(wx.MessageDialog(self, 'No phone detected/recognized.\nRun Settings?', 1223 'Phone Detection Failed', wx.YES_NO), 1224 True) as (_dlg, retcode): 1225 if retcode==wx.ID_YES: 1226 wx.CallAfter(self.OnEditSettings) 1227 self.SetPhoneModelStatus(guiwidgets.SB_Phone_Set) 1228 else: 1229 if silent_fail: 1230 self.__owner_name=None 1231 else: 1232 self.__owner_name=self.__get_owner_name(r.get('phone_esn', None)) 1233 if self.__owner_name is None or self.__owner_name=='': 1234 self.__owner_name='' 1235 else: 1236 self.__owner_name+="'s" 1237 self.config.Write("phonetype", r['phone_name']) 1238 self.commportsetting=str(r['port']) 1239 self.wt.clearcomm() 1240 self.config.Write("lgvx4400port", r['port']) 1241 self.phonemodule=common.importas(r['phone_module']) 1242 self.phoneprofile=self.phonemodule.Profile() 1243 pubsub.publish(pubsub.PHONE_MODEL_CHANGED, self.phonemodule) 1244 self.SetPhoneModelStatus(guiwidgets.SB_Phone_Detected) 1245 if not silent_fail: 1246 if self.__owner_name =='': 1247 wx.MessageBox('Found %s on %s'%(r['phone_name'], 1248 r['port']), 1249 'Phone Detection', wx.OK) 1250 else: 1251 wx.MessageBox('Found %s %s on %s'%(self.__owner_name, 1252 r['phone_name'], 1253 r['port']), 1254 'Phone Detection', wx.OK) 1255 if check_auto_sync: 1256 # see if we should re-sync the calender on connect, do it silently 1257 self.__autosync_phone(silent=1)
1258
1259 - def AddComm(self, name):
1260 # A new comm port became available 1261 print 'New device on port:',name 1262 # check the new device 1263 check_auto_sync=auto_sync.UpdateOnConnect(self) 1264 if name and name.lower()==self.config.Read('lgvx4400port', '').lower(): 1265 _func=self._detect_this_phone 1266 _args=(check_auto_sync, self._autodetect_delay, True) 1267 else: 1268 _func=self.__detect_phone 1269 _args=(name, check_auto_sync, self._autodetect_delay, True) 1270 if wx.IsBusy(): 1271 # current phone operation ongoing, queue this 1272 self.queue.put((_func, _args, {}), False) 1273 else: 1274 _func(*_args)
1275
1276 - def RemoveComm(self, name):
1277 # This comm just went away 1278 print "Device remove", name 1279 # device is removed, if it's ours, clear the port 1280 if name and name.lower()==self.config.Read('lgvx4400port', '').lower(): 1281 if self.wt: 1282 self.wt.clearcomm() 1283 self.SetPhoneModelStatus(guiwidgets.SB_Phone_Unavailable)
1284
1285 - def NotifyComm(self, evt):
1286 if evt.type==evt.add: 1287 self.AddComm(evt.comm) 1288 else: 1289 self.RemoveComm(evt.comm)
1290
1291 - def OnCommNotification(self, evt):
1292 print 'OnCommNotification' 1293 if wx.Thread_IsMain(): 1294 self.NotifyComm(evt) 1295 else: 1296 wx.CallAfter(self.NotifyComm, evt)
1297
1298 - def WindowsOnDeviceChanged(self, type, name="", drives=[], flag=None):
1299 if not name.lower().startswith("com"): 1300 return 1301 if type=='DBT_DEVICEREMOVECOMPLETE': 1302 self.RemoveComm(name) 1303 return 1304 if type!='DBT_DEVICEARRIVAL': 1305 # not interested 1306 return 1307 self.AddComm(name)
1308
1309 - def MyWndProc(self, hwnd, msg, wparam, lparam):
1310 1311 if msg==win32con.WM_DEVICECHANGE: 1312 try: 1313 type,params=DeviceChanged(wparam, lparam).GetEventInfo() 1314 self.OnDeviceChanged(type, **params) 1315 return True 1316 except: 1317 # something bad happened! Bail and let Windows handle it 1318 return win32gui.CallWindowProc(self.oldwndproc, hwnd, msg, 1319 wparam, lparam) 1320 1321 # Restore the old WndProc. Notice the use of win32api 1322 # instead of win32gui here. This is to avoid an error due to 1323 # not passing a callable object. 1324 if msg == win32con.WM_DESTROY: 1325 win32api.SetWindowLong(self.GetHandle(), 1326 win32con.GWL_WNDPROC, 1327 self.oldwndproc) 1328 if self._taskbar_on_closed and \ 1329 msg==win32con.WM_NCLBUTTONDOWN and \ 1330 wparam==win32con.HTCLOSE: 1331 # The system Close Box was clicked! 1332 self._close_button=True 1333 1334 # Pass all messages (in this case, yours may be different) on 1335 # to the original WndProc 1336 return win32gui.CallWindowProc(self.oldwndproc, 1337 hwnd, msg, wparam, lparam)
1338 1339 if guihelper.IsMSWindows(): 1340 OnDeviceChanged=WindowsOnDeviceChanged 1341
1342 - def SetVersionsStatus(self):
1343 current_v=version.version 1344 latest_v=self.config.Read('latest_version') 1345 self.GetStatusBar().set_versions(current_v, latest_v)
1346
1347 - def update_cache_path(self):
1348 com_brew.file_cache.set_path(self.configpath)
1349
1350 - def OnNewDB(self, _):
1351 newdb_wiz.create_new_db(self, self.config)
1352 1353 ### 1354 ### Main bit for getting stuff from phone 1355 ### 1356
1357 - def OnDataGetPhone(self,_):
1358 todo=[] 1359 dlg=self.dlggetphone 1360 dlg.UpdateWithProfile(self.phoneprofile) 1361 if dlg.ShowModal()!=wx.ID_OK: 1362 return 1363 self._autodetect_delay=self.phoneprofile.autodetect_delay 1364 todo.append((self.wt.rebootcheck, "Phone Reboot")) 1365 wx.GetApp().critical.set() 1366 self.MakeCall(Request(self.wt.getdata, dlg, todo), 1367 Callback(self.OnDataGetPhoneResults))
1368
1369 - def OnDataGetPhoneResults(self, exception, results):
1370 with wx.GetApp().critical: 1371 if self.HandleException(exception): return 1372 self.OnLog(`results.keys()`) 1373 self.OnLog(`results['sync']`) 1374 # phonebook 1375 if results['sync'].has_key('phonebook'): 1376 v=results['sync']['phonebook'] 1377 1378 print "phonebookmergesetting is",v 1379 if v=='MERGE': 1380 merge=True 1381 else: 1382 merge=False 1383 self.GetActivePhonebookWidget().importdata(results['phonebook'], results.get('categories', []), merge, results.get('group_wallpapers', [])) 1384 1385 # wallpaper 1386 updwp=False # did we update the wallpaper 1387 if results['sync'].has_key('wallpaper'): 1388 v=results['sync']['wallpaper'] 1389 if v=='MERGE': raise Exception("Not implemented") 1390 updwp=True 1391 self.GetActiveWallpaperWidget().populatefs(results) 1392 self.GetActiveWallpaperWidget().populate(results) 1393 # wallpaper-index 1394 if not updwp and results.has_key('wallpaper-index'): 1395 self.GetActiveWallpaperWidget().updateindex(results) 1396 # ringtone 1397 updrng=False # did we update ringtones 1398 if results['sync'].has_key('ringtone'): 1399 v=results['sync']['ringtone'] 1400 if v=='MERGE': raise Exception("Not implemented") 1401 updrng=True 1402 self.GetActiveRingerWidget().populatefs(results) 1403 self.GetActiveRingerWidget().populate(results) 1404 # ringtone-index 1405 if not updrng and results.has_key('ringtone-index'): 1406 self.GetActiveRingerWidget().updateindex(results) 1407 # calendar 1408 if results['sync'].has_key('calendar'): 1409 v=results['sync']['calendar'] 1410 if v=='MERGE': raise Exception("Not implemented") 1411 results['calendar_version']=self.phoneprofile.BP_Calendar_Version 1412 self.GetActiveCalendarWidget().mergedata(results) 1413 ## self.GetActiveCalendarWidget().populatefs(results) 1414 ## self.GetActiveCalendarWidget().populate(results) 1415 # memo 1416 if results['sync'].has_key('memo'): 1417 v=results['sync']['memo'] 1418 if v=='MERGE': raise Exception("Not implemented") 1419 self.GetActiveMemoWidget().populatefs(results) 1420 self.GetActiveMemoWidget().populate(results) 1421 # todo 1422 if results['sync'].has_key('todo'): 1423 v=results['sync']['todo'] 1424 if v=='MERGE': raise NotImplementedError 1425 self.GetActiveTodoWidget().populatefs(results) 1426 self.GetActiveTodoWidget().populate(results) 1427 # SMS 1428 if results['sync'].has_key('sms'): 1429 v=results['sync']['sms'] 1430 if v=='MERGE': 1431 self.GetActiveSMSWidget().merge(results) 1432 else: 1433 self.GetActiveSMSWidget().populatefs(results) 1434 self.GetActiveSMSWidget().populate(results) 1435 # call history 1436 if results['sync'].has_key('call_history'): 1437 v=results['sync']['call_history'] 1438 if v=='MERGE': 1439 self.GetActiveCallHistoryWidget().merge(results) 1440 else: 1441 self.GetActiveCallHistoryWidget().populatefs(results) 1442 self.GetActiveCallHistoryWidget().populate(results) 1443 # Playlist 1444 if results['sync'].has_key(playlist.playlist_key): 1445 if results['sync'][playlist.playlist_key]=='MERGE': 1446 raise NotImplementedError 1447 self.GetActivePlaylistWidget().populatefs(results) 1448 self.GetActivePlaylistWidget().populate(results) 1449 # T9 User DB 1450 if results['sync'].has_key(t9editor.dict_key): 1451 if results['sync'][t9editor.dict_key]=='MERGE': 1452 raise NotImplementedError 1453 self.GetActiveT9EditorWidget().populatefs(results) 1454 self.GetActiveT9EditorWidget().populate(results)
1455 ### 1456 ### Main bit for sending data to the phone 1457 ###
1458 - def OnDataSendPhone(self, _):
1459 dlg=self.dlgsendphone 1460 print self.phoneprofile 1461 dlg.UpdateWithProfile(self.phoneprofile) 1462 if dlg.ShowModal()!=wx.ID_OK: 1463 return 1464 data={} 1465 convertors=[] 1466 todo=[] 1467 funcscb=[] 1468 1469 ### Wallpaper 1470 v=dlg.GetWallpaperSetting() 1471 if v!=dlg.NOTREQUESTED: 1472 merge=True 1473 if v==dlg.OVERWRITE: merge=False 1474 if merge: 1475 want=self.GetActiveWallpaperWidget().SELECTED 1476 else: 1477 want=self.GetActiveWallpaperWidget().ALL 1478 self.GetActiveWallpaperWidget().getdata(data, want) 1479 todo.append( (self.wt.writewallpaper, "Wallpaper", merge) ) 1480 # funcscb.append( self.wallpaperwidget.populate ) 1481 1482 ### Ringtone 1483 v=dlg.GetRingtoneSetting() 1484 if v!=dlg.NOTREQUESTED: 1485 merge=True 1486 if v==dlg.OVERWRITE: merge=False 1487 if merge: 1488 want=self.GetActiveRingerWidget().SELECTED 1489 else: 1490 want=self.GetActiveRingerWidget().ALL 1491 self.GetActiveRingerWidget().getdata(data, want) 1492 todo.append( (self.wt.writeringtone, "Ringtone", merge) ) 1493 # funcscb.append( self.ringerwidget.populate ) 1494 1495 ### Calendar 1496 v=dlg.GetCalendarSetting() 1497 if v!=dlg.NOTREQUESTED: 1498 merge=True 1499 if v==dlg.OVERWRITE: merge=False 1500 data['calendar_version']=self.phoneprofile.BP_Calendar_Version 1501 self.GetActiveCalendarWidget().getdata(data) 1502 todo.append( (self.wt.writecalendar, "Calendar", merge) ) 1503 1504 ### Phonebook 1505 v=dlg.GetPhoneBookSetting() 1506 if v!=dlg.NOTREQUESTED: 1507 if v==dlg.OVERWRITE: 1508 self.GetActivePhonebookWidget().getdata(data) 1509 todo.append( (self.wt.writephonebook, "Phonebook") ) 1510 convertors.append(self.GetActivePhonebookWidget().converttophone) 1511 # writing will modify serials so we need to update 1512 funcscb.append(self.GetActivePhonebookWidget().updateserials) 1513 1514 ### Memo 1515 v=dlg.GetMemoSetting() 1516 if v!=dlg.NOTREQUESTED: 1517 merge=v!=dlg.OVERWRITE 1518 self.GetActiveMemoWidget().getdata(data) 1519 todo.append((self.wt.writememo, "Memo", merge)) 1520 1521 ### Todo 1522 v=dlg.GetTodoSetting() 1523 if v!=dlg.NOTREQUESTED: 1524 merge=v!=dlg.OVERWRITE 1525 self.GetActiveTodoWidget().getdata(data) 1526 todo.append((self.wt.writetodo, "Todo", merge)) 1527 1528 ### SMS 1529 v=dlg.GetSMSSetting() 1530 if v!=dlg.NOTREQUESTED: 1531 merge=v!=dlg.OVERWRITE 1532 self.GetActiveSMSWidget().getdata(data) 1533 todo.append((self.wt.writesms, "SMS", merge)) 1534 1535 ### Playlist 1536 v=dlg.GetPlaylistSetting() 1537 if v!=dlg.NOTREQUESTED: 1538 merge=v!=dlg.OVERWRITE 1539 self.GetActivePlaylistWidget().getdata(data) 1540 todo.append((self.wt.writeplaylist, "Playlist", merge)) 1541 1542 ### T9 User DB 1543 v=dlg.GetT9Setting() 1544 if v!=dlg.NOTREQUESTED: 1545 merge=v!=dlg.OVERWRITE 1546 self.GetActiveT9EditorWidget().getdata(data) 1547 todo.append((self.wt.writet9, "T9", merge)) 1548 1549 data['reboot_delay']=self.phoneprofile.reboot_delay 1550 self._autodetect_delay=self.phoneprofile.autodetect_delay 1551 todo.append((self.wt.rebootcheck, "Phone Reboot")) 1552 self.MakeCall(Request(self.wt.getfundamentals), 1553 Callback(self.OnDataSendPhoneGotFundamentals, data, todo, convertors, funcscb))
1554
1555 - def OnDataSendPhoneGotFundamentals(self,data,todo,convertors, funcscb, exception, results):
1556 if self.HandleException(exception): return 1557 data.update(results) 1558 # call each widget to update fundamentals 1559 # for widget in self.calendarwidget, self.wallpaperwidget, self.ringerwidget, self.phonewidget: 1560 # widget.updatefundamentals(data) 1561 1562 # call convertors 1563 for f in convertors: 1564 f(data) 1565 1566 # Now scribble to phone 1567 self.MakeCall(Request(self.wt.senddata, data, todo), 1568 Callback(self.OnDataSendPhoneResults, funcscb))
1569
1570 - def OnDataSendPhoneResults(self, funcscb, exception, results):
1571 if self.HandleException(exception): return 1572 print results.keys() 1573 for f in funcscb: 1574 f(results)
1575
1576 - def GetCalendarData(self):
1577 # return calendar data for export 1578 d={} 1579 return self.GetActiveCalendarWidget().getdata(d).get('calendar', {})
1580 1581
1582 - def OnAutoSyncSettings(self, _=None):
1583 if wx.IsBusy(): 1584 with guihelper.WXDialogWrapper(wx.MessageBox("BitPim is busy. You can't change settings until it has finished talking to your phone.", 1585 "BitPim is busy.", wx.OK|wx.ICON_EXCLAMATION), 1586 True): 1587 pass 1588 else: 1589 # clear the ower's name for manual setting 1590 self.__owner_name='' 1591 self.autosyncsetting.ShowModal()
1592
1593 - def OnAutoSyncExecute(self, _=None):
1594 if wx.IsBusy(): 1595 wx.MessageBox("BitPim is busy. You can't run autosync until it has finished talking to your phone.", 1596 "BitPim is busy.", wx.OK|wx.ICON_EXCLAMATION) 1597 return 1598 self.__autosync_phone()
1599
1600 - def __autosync_phone(self, silent=0):
1601 r=auto_sync.SyncSchedule(self).sync(self, silent)
1602 1603 # deal with configuring the phone (commport)
1604 - def OnReqChangeTab(self, msg=None):
1605 if msg is None: 1606 return 1607 data=msg.data 1608 if not isinstance(data, int): 1609 # wrong data type 1610 if __debug__: 1611 raise TypeError 1612 return
1613 1614 # Busy handling
1615 - def OnBusyStart(self):
1616 self.GetStatusBar().set_app_status_busy() 1617 wx.BeginBusyCursor(wx.StockCursor(wx.CURSOR_ARROWWAIT))
1618
1619 - def OnBusyEnd(self):
1620 wx.EndBusyCursor() 1621 self.GetStatusBar().set_app_status_ready() 1622 self.OnProgressMajor(0,1) 1623 # fire the next one in the queue 1624 if not self.queue.empty(): 1625 _q=self.queue.get(False) 1626 wx.CallAfter(_q[0], *_q[1], **_q[2])
1627 1628 # progress and logging
1629 - def OnProgressMinor(self, pos, max, desc=""):
1630 self.GetStatusBar().progressminor(pos, max, desc)
1631
1632 - def OnProgressMajor(self, pos, max, desc=""):
1633 self.GetStatusBar().progressmajor(pos, max, desc)
1634
1635 - def OnLog(self, str):
1636 if self.__phone_detect_at_startup: 1637 return 1638 str=common.strorunicode(str) 1639 if data_recording.DR_On: 1640 data_recording.record(data_recording.DR_Type_Note, str) 1641 self.tree.lw.log(str) 1642 if self.tree.lwdata is not None: 1643 self.tree.lwdata.log(str) 1644 if str.startswith("<!= "): 1645 p=str.index("=!>")+3 1646 guihelper.MessageDialog(self, str[p:], "Alert", style=wx.OK|wx.ICON_EXCLAMATION) 1647 self.OnLog("Alert dialog closed")
1648 log=OnLog
1649 - def OnLogData(self, str, data, klass=None, data_type=None):
1650 if data_recording.DR_On: 1651 data_recording.record(data_recording.DR_Type_Note, str) 1652 data_recording.record(data_type or data_recording.DR_Type_Data, 1653 data, klass) 1654 if self.tree.lwdata is not None: 1655 self.tree.lwdata.logdata(str,data, klass)
1656
1657 - def excepthook(self, type, value, traceback):
1658 if not hasattr(value, "gui_exc_info"): 1659 value.gui_exc_info=(type,value,traceback) 1660 self.HandleException(value)
1661
1662 - def HandleException(self, exception):
1663 """returns true if this function handled the exception 1664 and the caller should not do any further processing""" 1665 if exception is None: return False 1666 assert isinstance(exception, Exception) 1667 self.CloseSplashScreen() 1668 # always close comm connection when we have any form of exception 1669 if self.wt is not None: 1670 self.wt.clearcomm() 1671 text=None 1672 title=None 1673 style=None 1674 # Here is where we turn the exception into something user friendly 1675 if isinstance(exception, common.CommsDeviceNeedsAttention): 1676 text="%s: %s" % (exception.device, exception.message) 1677 title="Device needs attention - "+exception.device 1678 style=wx.OK|wx.ICON_INFORMATION 1679 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_DEVICE_NEEDS_ATTENTION) 1680 elif isinstance(exception, common.CommsOpenFailure): 1681 text="%s: %s" % (exception.device, exception.message) 1682 title="Failed to open communications - "+exception.device 1683 style=wx.OK|wx.ICON_INFORMATION 1684 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_FAILED_TO_OPEN_DEVICE) 1685 elif isinstance(exception, common.AutoPortsFailure): 1686 text=exception.message 1687 title="Failed to automatically detect port" 1688 style=wx.OK|wx.ICON_INFORMATION 1689 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_FAILED_TO_AUTODETECT_PORT) 1690 elif isinstance(exception, common.HelperBinaryNotFound) and exception.basename=="pvconv": 1691 text="The Qualcomm PureVoice converter program (%s) was not found.\nPlease see the help. Directories looked in are:\n\n " +\ 1692 "\n ".join(exception.paths) 1693 text=text % (exception.fullname,) 1694 title="Failed to find PureVoice converter" 1695 style=wx.OK|wx.ICON_INFORMATION 1696 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_NO_PVCONV) 1697 elif isinstance(exception, common.PhoneBookBusyException): 1698 text="The phonebook is busy on your phone.\nExit back to the main screen and then repeat the operation." 1699 title="Phonebook busy on phone" 1700 style=wx.OK|wx.ICON_INFORMATION 1701 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_PHONEBOOKBUSY) 1702 elif isinstance(exception, common.IntegrityCheckFailed): 1703 text="The phonebook on your phone is partially corrupt. Please read the\nhelp for more details on the cause and fix" 1704 title="IntegrityCheckFailed" 1705 style=wx.OK|wx.ICON_EXCLAMATION 1706 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_LG_INTEGRITYCHECKFAILED) 1707 elif isinstance(exception, common.CommsDataCorruption): 1708 text=exception.message+"\nPlease see the help." 1709 title="Communications Error - "+exception.device 1710 style=wx.OK|wx.ICON_EXCLAMATION 1711 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_COMMSDATAERROR) 1712 elif isinstance(exception, com_brew.BrewAccessDeniedException): 1713 text="Access to the file/directory has been blocked on this phone by the phone provider" 1714 title="Access Denied" 1715 style=wx.OK|wx.ICON_EXCLAMATION 1716 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_BREW_ACCESS_DENIED) 1717 elif isinstance(exception, common.PhoneStringEncodeException): 1718 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`) 1719 title="Text Conversion Error" 1720 style=wx.OK|wx.ICON_EXCLAMATION 1721 help=lambda _: wx.GetApp().displayhelpid(helpids.ID_BREW_ACCESS_DENIED) 1722 1723 if text is not None: 1724 self.OnLog("Error: "+title+"\n"+text) 1725 with guihelper.WXDialogWrapper(guiwidgets.AlertDialogWithHelp(self,text, title, help, style=style), 1726 True): 1727 pass 1728 return True 1729 1730 if self.exceptiondialog is None: 1731 self.excepttime=time.time() 1732 self.exceptcount=0 1733 self.exceptiondialog=guiwidgets.ExceptionDialog(self, exception) 1734 try: 1735 self.OnLog("Exception: "+self.exceptiondialog.getexceptiontext()) 1736 except AttributeError: 1737 # this can happen if main gui hasn't been built yet 1738 pass 1739 else: 1740 self.exceptcount+=1 1741 if self.exceptcount<10: 1742 print "Ignoring an exception as the exception dialog is already up" 1743 try: 1744 self.OnLog("Exception during exception swallowed") 1745 except AttributeError: 1746 # this can happen if main gui hasn't been built yet 1747 pass 1748 return True 1749 1750 self.exceptiondialog.ShowModal() 1751 self.exceptiondialog.Destroy() 1752 self.exceptiondialog=None 1753 return True
1754 1755 # midnight timer stuff
1756 - def _OnTimer(self, _):
1757 self.MakeCall(Request(self._pub_timer), 1758 Callback(self._OnTimerReturn))
1759
1760 - def _pub_timer(self):
1762
1763 - def _OnTimerReturn(self, exceptions, result):
1764 self._timer.Start(((3600*24)+1)*1000, True)
1765
1766 - def _setup_midnight_timer(self):
1767 _today=datetime.datetime.now() 1768 _timer_val=24*3600-_today.hour*3600-_today.minute*60-_today.second+1 1769 self._timer=wx.Timer(self) 1770 wx.EVT_TIMER(self, self._timer.GetId(), self._OnTimer) 1771 self._timer.Start(_timer_val*1000, True) 1772 print _timer_val,'seconds till midnight'
1773 1774 # Data Recording stuff
1775 - def OnDataRecording(self, _):
1776 with guihelper.WXDialogWrapper(guiwidgets.DRRecFileDialog(self), 1777 True): 1778 pass
1779 1780 # plumbing for the multi-threading 1781
1782 - def OnCallback(self, event):
1783 assert isinstance(event, HelperReturnEvent) 1784 event()
1785
1786 - def MakeCall(self, request, cbresult):
1787 assert isinstance(request, Request) 1788 assert isinstance(cbresult, Callback) 1789 self.wt.q.put( (request, cbresult) )
1790 1791 # remember our size and position 1792
1793 - def saveSize(self):
1794 guiwidgets.save_size("MainWin", self.GetRect())
1795 1796 ### 1797 ### Container for midi files 1798 ### 1799 1800 #class MidiFileList(wx.ListCtrl): 1801 # pass 1802 1803 1804 1805 1806 ### 1807 ### Class that does all the comms and other stuff in a seperate 1808 ### thread. 1809 ### 1810
1811 -class WorkerThread(WorkerThreadFramework):
1812 - def __init__(self):
1813 WorkerThreadFramework.__init__(self) 1814 self.commphone=None 1815 data_recording.register(self.OnDataRecording, self.OnDataRecording, 1816 self.OnDataRecording)
1817
1818 - def exit(self):
1819 if __debug__: self.checkthread() 1820 for i in range(0,0): 1821 self.progressmajor(i, 2, "Shutting down helper thread") 1822 time.sleep(1) 1823 self.log("helper thread shut down") 1824 raise BitPimExit("helper thread shutdown")
1825 1826
1827 - def clearcomm(self):
1828 if self.commphone is None: 1829 return 1830 self.commphone.close() 1831 self.commphone=None
1832 1833
1834 - def setupcomm(self):
1835 if __debug__: self.checkthread() 1836 if self.commphone is None: 1837 import commport 1838 if self.dispatchto.commportsetting is None or \ 1839 len(self.dispatchto.commportsetting)==0: 1840 raise common.CommsNeedConfiguring("Comm port not configured", "DEVICE") 1841 1842 if self.dispatchto.commportsetting=="auto": 1843 autofunc=comdiagnose.autoguessports 1844 else: 1845 autofunc=None 1846 comcfg=self.dispatchto.commparams 1847 1848 name=self.dispatchto.commportsetting 1849 if name.startswith("bitfling::"): 1850 klass=bitflingscan.CommConnection 1851 else: 1852 klass=commport.CommConnection 1853 1854 comport=klass(self, self.dispatchto.commportsetting, autolistfunc=autofunc, 1855 autolistargs=(self.dispatchto.phonemodule,), 1856 baud=comcfg['baud'], timeout=comcfg['timeout'], 1857 hardwareflow=comcfg['hardwareflow'], 1858 softwareflow=comcfg['softwareflow'], 1859 configparameters=comcfg) 1860 1861 try: 1862 self.commphone=self.dispatchto.phonemodule.Phone(self, comport) 1863 except: 1864 comport.close() 1865 raise
1866
1867 - def getfundamentals(self):
1868 if __debug__: self.checkthread() 1869 self.setupcomm() 1870 results={} 1871 self.commphone.getfundamentals(results) 1872 return results
1873
1874 - def getdata(self, req, todo):
1875 if __debug__: self.checkthread() 1876 self.setupcomm() 1877 results=self.getfundamentals() 1878 com_brew.file_cache.esn=results.get('uniqueserial', None) 1879 willcall=[] 1880 sync={} 1881 for i in ( 1882 (req.GetPhoneBookSetting, self.commphone.getphonebook, "Phone Book", "phonebook"), 1883 (req.GetCalendarSetting, self.commphone.getcalendar, "Calendar", "calendar",), 1884 (req.GetWallpaperSetting, self.commphone.getwallpapers, "Wallpaper", "wallpaper"), 1885 (req.GetRingtoneSetting, self.commphone.getringtones, "Ringtones", "ringtone"), 1886 (req.GetMemoSetting, self.commphone.getmemo, "Memo", "memo"), 1887 (req.GetTodoSetting, self.commphone.gettodo, "Todo", "todo"), 1888 (req.GetSMSSetting, self.commphone.getsms, "SMS", "sms"), 1889 (req.GetCallHistorySetting, self.commphone.getcallhistory, 'Call History', 'call_history'), 1890 (req.GetPlaylistSetting, self.commphone.getplaylist, 'Play List', 'playlist'), 1891 (req.GetT9Setting, self.commphone.gett9db, 'T9 DB', t9editor.dict_key), 1892 ): 1893 st=i[0]() 1894 if st==req.MERGE: 1895 sync[i[3]]="MERGE" 1896 willcall.append(i) 1897 elif st==req.OVERWRITE: 1898 sync[i[3]]="OVERWRITE" 1899 willcall.append(i) 1900 1901 results['sync']=sync 1902 count=0 1903 for i in willcall: 1904 self.progressmajor(count, len(willcall), i[2]) 1905 count+=1 1906 i[1](results) 1907 1908 for xx in todo: 1909 func=xx[0] 1910 desc=xx[1] 1911 args=[results] 1912 if len(xx)>2: 1913 args.extend(xx[2:]) 1914 apply(func, args) 1915 1916 return results
1917
1918 - def senddata(self, dict, todo):
1919 count=0 1920 for xx in todo: 1921 func=xx[0] 1922 desc=xx[1] 1923 args=[dict] 1924 if len(xx)>2: 1925 args.extend(xx[2:]) 1926 self.progressmajor(count,len(todo),desc) 1927 apply(func, args) 1928 count+=1 1929 return dict
1930
1931 - def writewallpaper(self, data, merge):
1932 if __debug__: self.checkthread() 1933 self.setupcomm() 1934 return self.commphone.savewallpapers(data, merge)
1935
1936 - def writeringtone(self, data, merge):
1937 if __debug__: self.checkthread() 1938 self.setupcomm() 1939 return self.commphone.saveringtones(data, merge)
1940
1941 - def writephonebook(self, data):
1942 if __debug__: self.checkthread() 1943 self.setupcomm() 1944 return self.commphone.savephonebook(data)
1945
1946 - def rebootcheck(self, results):
1947 if __debug__: self.checkthread() 1948 if results.has_key('rebootphone'): 1949 self.log("BitPim is rebooting your phone for changes to take effect") 1950 delay=0 1951 if results.has_key('reboot_delay'): 1952 delay=results['reboot_delay'] 1953 self.phonerebootrequest(delay) 1954 self.clearcomm() 1955 elif results.get('clearcomm', False): 1956 # some model (eg Moto) needs to clear comm after certain mode 1957 self.clearcomm()
1958
1959 - def writecalendar(self, data, merge):
1960 if __debug__: self.checkthread() 1961 self.setupcomm() 1962 return self.commphone.savecalendar(data, merge)
1963
1964 - def writememo(self, data, merge):
1965 if __debug__: self.checkthread() 1966 self.setupcomm() 1967 return self.commphone.savememo(data, merge)
1968
1969 - def writetodo(self, data, merge):
1970 if __debug__: self.checkthread() 1971 self.setupcomm() 1972 return self.commphone.savetodo(data, merge)
1973
1974 - def writesms(self, data, merge):
1975 if __debug__: self.checkthread() 1976 self.setupcomm() 1977 return self.commphone.savesms(data, merge)
1978
1979 - def writeplaylist(self, data, merge):
1980 if __debug__: self.checkthread() 1981 self.setupcomm() 1982 return self.commphone.saveplaylist(data, merge)
1983
1984 - def writet9(self, data, merge):
1985 if __debug__: 1986 self.checkthread() 1987 self.setupcomm() 1988 return self.commphone.savet9db(data, merge)
1989
1990 - def getphoneinfo(self):
1991 if __debug__: self.checkthread() 1992 self.setupcomm() 1993 if hasattr(self.commphone, 'getphoneinfo'): 1994 phone_info=phoneinfo.PhoneInfo() 1995 getattr(self.commphone, 'getphoneinfo')(phone_info) 1996 return phone_info
1997
1998 - def detectphone(self, using_port=None, using_model=None, delay=0):
1999 self.clearcomm() 2000 time.sleep(delay) 2001 return phone_detect.DetectPhone(self).detect(using_port, using_model)
2002 2003 # various file operations for the benefit of the filesystem viewer
2004 - def dirlisting(self, path, recurse=0):
2005 if __debug__: self.checkthread() 2006 self.setupcomm() 2007 try: 2008 return self.commphone.getfilesystem(path, recurse) 2009 except: 2010 self.log('Failed to read dir: '+path) 2011 return {}
2012
2013 - def getfileonlylist(self, path):
2014 if __debug__: self.checkthread() 2015 self.setupcomm() 2016 try: 2017 return self.commphone.listfiles(path) 2018 except: 2019 self.log('Failed to read filesystem') 2020 return {}
2021
2022 - def getdironlylist(self, path, recurse):
2023 results=self.commphone.listsubdirs(path) 2024 subdir_list=[x['name'] for k,x in results.items()] 2025 if recurse: 2026 for _subdir in subdir_list: 2027 try: 2028 results.update(self.getdironlylist(_subdir, recurse)) 2029 except: 2030 self.log('Failed to list directories in ' +_subdir) 2031 return results
2032
2033 - def fulldirlisting(self):
2034 if __debug__: self.checkthread() 2035 self.setupcomm() 2036 try: 2037 return self.getdironlylist("", True) 2038 except: 2039 self.log('Failed to read filesystem') 2040 return {}
2041
2042 - def singledirlisting(self, path):
2043 if __debug__: self.checkthread() 2044 self.setupcomm() 2045 try: 2046 return self.getdironlylist(path, False) 2047 except: 2048 self.log('Failed to read filesystem') 2049 return {}
2050
2051 - def getfile(self, path):
2052 if __debug__: self.checkthread() 2053 self.setupcomm() 2054 return self.commphone.getfilecontents(path)
2055
2056 - def rmfile(self,path):
2057 if __debug__: self.checkthread() 2058 self.setupcomm() 2059 return self.commphone.rmfile(path)
2060
2061 - def writefile(self,path,contents):
2062 if __debug__: self.checkthread() 2063 self.setupcomm() 2064 return self.commphone.writefile(path, contents)
2065
2066 - def mkdir(self,path):
2067 if __debug__: self.checkthread() 2068 self.setupcomm() 2069 return self.commphone.mkdir(path)
2070
2071 - def rmdir(self,path):
2072 if __debug__: self.checkthread() 2073 self.setupcomm() 2074 return self.commphone.rmdir(path)
2075
2076 - def rmdirs(self,path):
2077 if __debug__: self.checkthread() 2078 self.setupcomm() 2079 return self.commphone.rmdirs(path)
2080 2081 # offline/reboot/modemmode
2082 - def phonerebootrequest(self, reboot_delay=0):
2083 if __debug__: self.checkthread() 2084 self.setupcomm() 2085 return self.commphone.offlinerequest(reset=True, delay=reboot_delay)
2086
2087 - def phoneofflinerequest(self):
2088 if __debug__: self.checkthread() 2089 self.setupcomm() 2090 return self.commphone.offlinerequest()
2091
2092 - def modemmoderequest(self):
2093 if __debug__: self.checkthread() 2094 self.setupcomm() 2095 return self.commphone.modemmoderequest()
2096 2097 # backups etc
2098 - def getbackup(self,path,recurse=0):
2099 if __debug__: self.checkthread() 2100 self.setupcomm() 2101 self.progressmajor(0,0,"Listing files") 2102 files=self.dirlisting(path, recurse) 2103 if path=="/" or path=="": 2104 strip=0 # root dir 2105 else: 2106 strip=len(path)+1 # child 2107 2108 keys=files.keys() 2109 keys.sort() 2110 2111 op=cStringIO.StringIO() 2112 with contextlib.closing(zipfile.ZipFile(op, "w", zipfile.ZIP_DEFLATED)) as zip: 2113 count=0 2114 for k in keys: 2115 try: 2116 count+=1 2117 if files[k]['type']!='file': 2118 continue 2119 self.progressmajor(count, len(keys)+1, "Getting files") 2120 # get the contents 2121 contents=self.getfile(k) 2122 # an artificial sleep. if you get files too quickly, the 4400 eventually 2123 # runs out of buffers and returns truncated packets 2124 time.sleep(0.3) 2125 # add to zip file 2126 zi=zipfile.ZipInfo() 2127 # zipfile does not like unicode. cp437 works on windows well, may be 2128 # a better choice than ascii, but no phones currently support anything 2129 # other than ascii for filenames 2130 if k[strip]=='/': 2131 zi.filename=common.get_ascii_string(k[strip+1:], 'ignore') 2132 else: 2133 zi.filename=common.get_ascii_string(k[strip:], 'ignore') 2134 if files[k]['date'][0]==0: 2135 zi.date_time=(0,0,0,0,0,0) 2136 else: 2137 zi.date_time=time.gmtime(files[k]['date'][0])[:6] 2138 zi.compress_type=zipfile.ZIP_DEFLATED 2139 zip.writestr(zi, contents) 2140 except: 2141 self.log('Failed to read file: '+k) 2142 return op.getvalue()
2143
2144 - def restorefiles(self, files):
2145 if __debug__: self.checkthread() 2146 self.setupcomm() 2147 2148 results=[] 2149 2150 seendirs=[] 2151 2152 count=0 2153 for name, contents in files: 2154 self.progressmajor(count, len(files), "Restoring files") 2155 count+=1 2156 d=guihelper.dirname(name) 2157 if d not in seendirs: 2158 seendirs.append(d) 2159 self.commphone.mkdirs(d) 2160 self.writefile(name, contents) 2161 results.append( (True, name) ) 2162 # add a deliberate sleep - some phones (eg vx7000) get overwhelmed when writing 2163 # lots of files in a tight loop 2164 time.sleep(0.3) 2165 2166 return results
2167
2168 - def OnDataRecording(self, _=None):
2169 self.clearcomm()
2170 2171 #------------------------------------------------------------------------------- 2172 # For windows platform only 2173 if guihelper.IsMSWindows(): 2174 import struct
2175 - class DeviceChanged:
2176 2177 DBT_DEVICEARRIVAL = 0x8000 2178 DBT_DEVICEQUERYREMOVE = 0x8001 2179 DBT_DEVICEQUERYREMOVEFAILED = 0x8002 2180 DBT_DEVICEREMOVEPENDING = 0x8003 2181 DBT_DEVICEREMOVECOMPLETE = 0x8004 2182 DBT_DEVICETYPESPECIFIC = 0x8005 2183 DBT_DEVNODES_CHANGED = 7 2184 DBT_CONFIGCHANGED = 0x18 2185 2186 DBT_DEVTYP_OEM = 0 2187 DBT_DEVTYP_DEVNODE = 1 2188 DBT_DEVTYP_VOLUME = 2 2189 DBT_DEVTYP_PORT = 3 2190 DBT_DEVTYP_NET = 4 2191 2192 DBTF_MEDIA = 0x0001 2193 DBTF_NET = 0x0002 2194
2195 - def __init__(self, wparam, lparam):
2196 self._info=None 2197 for name in dir(self): 2198 if name.startswith("DBT") and \ 2199 not name.startswith("DBT_DEVTYP") and \ 2200 getattr(self,name)==wparam: 2201 self._info=(name, dict(self._decode_struct(lparam)))
2202
2203 - def GetEventInfo(self):
2204 return self._info
2205
2206 - def _decode_struct(self, lparam):
2207 if lparam==0: return () 2208 format = "iii" 2209 buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam) 2210 dbch_size, dbch_devicetype, dbch_reserved = struct.unpack(format, buf) 2211 2212 buf = win32gui.PyMakeBuffer(dbch_size, lparam) # we know true size now 2213 2214 if dbch_devicetype==self.DBT_DEVTYP_PORT: 2215 name="" 2216 for b in buf[struct.calcsize(format):]: 2217 if b!="\x00": 2218 name+=b 2219 continue 2220 break 2221 return ("name", name), 2222 2223 if dbch_devicetype==self.DBT_DEVTYP_VOLUME: 2224 # yes, the last item is a WORD, not a DWORD like the hungarian would lead you to think 2225 format="iiiih0i" 2226 dbcv_size, dbcv_devicetype, dbcv_reserved, dbcv_unitmask, dbcv_flags = struct.unpack(format, buf) 2227 units=[chr(ord('A')+x) for x in range(26) if dbcv_unitmask&(2**x)] 2228 flag="" 2229 for name in dir(self): 2230 if name.startswith("DBTF_") and getattr(self, name)==dbcv_flags: 2231 flag=name 2232 break 2233 2234 return ("drives", units), ("flag", flag) 2235 2236 print "unhandled devicetype struct", dbch_devicetype 2237 return ()
2238