0001 #!/usr/bin/env python 0002 0003 ### BITPIM 0004 ### 0005 ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 0006 ### 0007 ### This program is free software; you can redistribute it and/or modify 0008 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0009 ### 0010 ### $Id: guiwidgets.py 4675 2008-08-12 21:52:12Z djpham $ 0011 0012 """Most of the graphical user interface elements making up BitPim""" 0013 0014 # standard modules 0015 from __future__ import with_statement 0016 import contextlib 0017 import os 0018 import sys 0019 import time 0020 import copy 0021 import StringIO 0022 import getpass 0023 import sha,md5 0024 import gzip 0025 import base64 0026 import thread 0027 import Queue 0028 import shutil 0029 import platform 0030 0031 # wx. modules 0032 import wx 0033 import wx.html 0034 import wx.lib.intctrl 0035 import wx.lib.newevent 0036 import wx.lib.mixins.listctrl as listmix 0037 import wx.lib.stattext as stattext 0038 from wx.lib.masked import NumCtrl 0039 0040 # my modules 0041 import apsw 0042 import common 0043 import version 0044 import helpids 0045 import comscan 0046 import usbscan 0047 import comdiagnose 0048 import analyser 0049 import guihelper 0050 import pubsub 0051 import bphtml 0052 import bitflingscan 0053 import aggregatedisplay 0054 import phone_media_codec 0055 import pubsub 0056 import widgets 0057 import phones 0058 import setphone_wizard 0059 import data_recording 0060 import xyaptu 0061 import serial 0062 0063 ### 0064 ### BitFling cert stuff 0065 ### 0066 0067 BitFlingCertificateVerificationEvent, EVT_BITFLINGCERTIFICATEVERIFICATION = wx.lib.newevent.NewEvent() 0068 0069 0070 #### 0071 #### A simple text widget that does nice pretty logging. 0072 #### 0073 0074 0075 class LogWindow(wx.Panel, widgets.BitPimWidget): 0076 0077 theanalyser=None 0078 0079 def __init__(self, parent): 0080 wx.Panel.__init__(self,parent, -1) 0081 # have to use rich2 otherwise fixed width font isn't used on windows 0082 self.tb=wx.TextCtrl(self, 1, style=wx.TE_MULTILINE| wx.TE_RICH2|wx.TE_DONTWRAP|wx.TE_READONLY) 0083 f=wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL ) 0084 ta=wx.TextAttr(font=f) 0085 self.tb.SetDefaultStyle(ta) 0086 self.sizer=wx.BoxSizer(wx.VERTICAL) 0087 self.sizer.Add(self.tb, 1, wx.EXPAND) 0088 self.SetSizer(self.sizer) 0089 self.SetAutoLayout(True) 0090 self.sizer.Fit(self) 0091 wx.EVT_IDLE(self, self.OnIdle) 0092 wx.EVT_SHOW(self, self.OnShow) 0093 self.outstandingtext=StringIO.StringIO() 0094 0095 wx.EVT_KEY_UP(self.tb, self.OnKeyUp) 0096 0097 def Clear(self): 0098 self.tb.Clear() 0099 0100 def OnSelectAll(self, _): 0101 self.tb.SetSelection(-1, -1) 0102 0103 def OnShow(self, show): 0104 if show.GetShow(): 0105 wx.CallAfter(self.CleanupView) 0106 0107 def CleanupView(self): 0108 self.tb.SetInsertionPoint(0) 0109 self.tb.SetInsertionPointEnd() 0110 self.tb.Refresh() 0111 0112 def OnIdle(self,_): 0113 if self.outstandingtext.tell(): 0114 # this code is written to be re-entrant 0115 newt=self.outstandingtext.getvalue() 0116 self.outstandingtext.seek(0) 0117 self.outstandingtext.truncate() 0118 self.tb.AppendText(newt) 0119 0120 def log(self, str, nl=True): 0121 now=time.time() 0122 t=time.localtime(now) 0123 self.outstandingtext.write("%d:%02d:%02d.%03d " % ( t[3], t[4], t[5], int((now-int(now))*1000))) 0124 self.outstandingtext.write(str) 0125 if nl: 0126 self.outstandingtext.write("\n") 0127 0128 def logdata(self, str, data, klass=None): 0129 o=self.outstandingtext 0130 self.log(str, nl=False) 0131 if data is not None: 0132 o.write(" Data - "+`len(data)`+" bytes\n") 0133 if klass is not None: 0134 try: 0135 o.write("<#! %s.%s !#>\n" % (klass.__module__, klass.__name__)) 0136 except: 0137 klass=klass.__class__ 0138 o.write("<#! %s.%s !#>\n" % (klass.__module__, klass.__name__)) 0139 o.write(common.datatohexstring(data)) 0140 o.write("\n") 0141 0142 def OnKeyUp(self, evt): 0143 keycode=evt.GetKeyCode() 0144 if keycode==ord('P') and evt.ControlDown() and evt.AltDown(): 0145 # analyse what was selected 0146 data=self.tb.GetStringSelection() 0147 # or the whole buffer if it was nothing 0148 if data is None or len(data)==0: 0149 data=self.tb.GetValue() 0150 try: 0151 self.theanalyser.Show() 0152 except: 0153 self.theanalyser=None 0154 0155 if self.theanalyser is None: 0156 self.theanalyser=analyser.Analyser(data=data) 0157 0158 self.theanalyser.Show() 0159 self.theanalyser.newdata(data) 0160 evt.Skip() 0161 0162 def GetValue(self): 0163 """return the log text""" 0164 return self.tb.GetValue() 0165 0166 0167 ### 0168 ### Dialog asking what you want to sync 0169 ### 0170 0171 class GetPhoneDialog(wx.Dialog): 0172 # sync sources ("Pretty Name", "name used to query profile") 0173 sources= ( ('PhoneBook', 'phonebook'), 0174 ('Calendar', 'calendar'), 0175 ('Wallpaper', 'wallpaper'), 0176 ('Ringtone', 'ringtone'), 0177 ('Memo', 'memo'), 0178 ('Todo', 'todo'), 0179 ('SMS', 'sms'), 0180 ('Call History', 'call_history'), 0181 ('Play List', 'playlist'), 0182 ('T9 User DB','t9_udb')) 0183 0184 # actions ("Pretty Name", "name used to query profile") 0185 actions = ( ("Get", "read"), ) 0186 0187 NOTREQUESTED=0 0188 MERGE=1 0189 OVERWRITE=2 0190 0191 # type of action ("pretty name", "name used to query profile") 0192 types= ( ("Add", MERGE), 0193 ("Replace All", OVERWRITE)) 0194 typename={ MERGE: 'MERGE', 0195 OVERWRITE: 'OVERWRITE', 0196 NOTREQUESTED: 'NOTREQUESTED', 0197 } 0198 0199 HELPID=helpids.ID_GET_PHONE_DATA 0200 0201 def __init__(self, frame, title, id=-1): 0202 wx.Dialog.__init__(self, frame, id, title, 0203 style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE) 0204 gs=wx.FlexGridSizer(2+len(self.sources), 1+len(self.types),5 ,10) 0205 gs.AddGrowableCol(1) 0206 gs.AddMany( [ 0207 (wx.StaticText(self, -1, "Source"), 0, wx.EXPAND),]) 0208 0209 for pretty,_ in self.types: 0210 gs.Add(wx.StaticText(self, -1, pretty), 0, wx.ALIGN_CENTRE) 0211 0212 self._widgets={} 0213 for desc, source in self.sources: 0214 _cb=wx.CheckBox(self, wx.NewId(), desc) 0215 _cb.exclusive=False 0216 wx.EVT_CHECKBOX(self, _cb.GetId(), self.DoOkStatus) 0217 gs.Add(_cb, 0, wx.EXPAND) 0218 self._widgets[source]={ 'cb': _cb, 0219 'rb': {}, 0220 } 0221 first=True 0222 for tdesc,tval in self.types: 0223 if first: 0224 style=wx.RB_GROUP 0225 first=0 0226 else: 0227 style=0 0228 _rb=wx.RadioButton(self, -1, "", style=style) 0229 if self._dowesupport(source, self.actions[0][1], tval): 0230 wx.EVT_RADIOBUTTON(self, _rb.GetId(), self.OnOptionSelected) 0231 else: 0232 _rb.SetValue(False) 0233 _rb.Enable(False) 0234 gs.Add(_rb, 0, wx.ALIGN_CENTRE) 0235 self._widgets[source]['rb'][tval]=_rb 0236 0237 bs=wx.BoxSizer(wx.VERTICAL) 0238 bs.Add(gs, 0, wx.EXPAND|wx.ALL, 10) 0239 bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) 0240 0241 but=self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP) 0242 self._btn_ok=self.FindWindowById(wx.ID_OK) 0243 bs.Add(but, 0, wx.EXPAND|wx.ALL, 10) 0244 0245 self.SetSizer(bs) 0246 self.SetAutoLayout(True) 0247 bs.Fit(self) 0248 0249 wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) 0250 0251 def _setting(self, sourcetype): 0252 _w=self._widgets[sourcetype] 0253 if not _w['cb'].GetValue(): 0254 # Not selected 0255 return self.NOTREQUESTED 0256 for _typeval,_rb in _w['rb'].items(): 0257 if _rb.GetValue(): 0258 return _typeval 0259 # should not get here 0260 raise ValueError 0261 0262 def GetPhoneBookSetting(self): 0263 return self._setting("phonebook") 0264 0265 def GetCalendarSetting(self): 0266 return self._setting("calendar") 0267 0268 def GetWallpaperSetting(self): 0269 return self._setting("wallpaper") 0270 0271 def GetRingtoneSetting(self): 0272 return self._setting("ringtone") 0273 0274 def GetMemoSetting(self): 0275 return self._setting("memo") 0276 0277 def GetTodoSetting(self): 0278 return self._setting("todo") 0279 0280 def GetSMSSetting(self): 0281 return self._setting("sms") 0282 def GetCallHistorySetting(self): 0283 return self._setting("call_history") 0284 def GetPlaylistSetting(self): 0285 return self._setting('playlist') 0286 def GetT9Setting(self): 0287 return self._setting('t9_udb') 0288 0289 def OnHelp(self,_): 0290 wx.GetApp().displayhelpid(self.HELPID) 0291 0292 # this is what BitPim itself supports - the phones may support a subset 0293 _notsupported=( 0294 # ('phonebook', 'read', MERGE), # sort of is 0295 ('calendar', 'read', MERGE), 0296 ('wallpaper', 'read', MERGE), 0297 ('ringtone', 'read', MERGE), 0298 ('memo', 'read', MERGE), 0299 ('todo', 'read', MERGE), 0300 ('playlist', 'read', MERGE), 0301 ('t9_udb', 'read', MERGE)) 0302 0303 def _dowesupport(self, source, action, type): 0304 if (source,action,type) in self._notsupported: 0305 return False 0306 return True 0307 0308 def UpdateWithProfile(self, profile): 0309 assert len(self.types)==2 0310 _action=self.actions[0][1] 0311 for source,_w in self._widgets.items(): 0312 _cb=_w['cb'] 0313 _cb.Enable(False) 0314 # are any radio buttons enabled 0315 _rb_on=False 0316 for _type,_rb in _w['rb'].items(): 0317 if self._dowesupport(source, _action, _type) and \ 0318 profile.SyncQuery(source, _action, self.typename[_type]): 0319 _cb.Enable(True) 0320 _cb.exclusive=profile.SyncQuery(source, _action, 'EXCLUSIVE') 0321 _rb.Enable(True) 0322 _rb_on|=bool(_rb.GetValue()) 0323 else: 0324 _rb.SetValue(False) 0325 _rb.Enable(False) 0326 if _cb.IsEnabled(): 0327 # make sure at least one radio button is set 0328 if not _rb_on: 0329 for _rb in _w['rb'].values(): 0330 if _rb.IsEnabled(): 0331 _rb.SetValue(True) 0332 break 0333 else: 0334 # uncheck of not enabled 0335 _cb.SetValue(False) 0336 0337 def ShowModal(self): 0338 # we ensure the OK button is in the correct state 0339 self.DoOkStatus() 0340 return wx.Dialog.ShowModal(self) 0341 0342 def _check_for_exclusive(self, w): 0343 if w.exclusive: 0344 # this one is exclusive, turn off all others 0345 for _w in self._widgets.values(): 0346 if _w['cb'] is not w: 0347 _w['cb'].SetValue(False) 0348 else: 0349 # this one is not exclusive, turn off all exclusive ones 0350 for _w in self._widgets.values(): 0351 if _w['cb'] is not w and \ 0352 _w['cb'].exclusive: 0353 _w['cb'].SetValue(False) 0354 0355 def OnOptionSelected(self, evt): 0356 # User clicked on an option 0357 # Turn on the row to which this option belongs 0358 _rb=evt.GetEventObject() 0359 for _w1 in self._widgets.values(): 0360 if _rb in _w1['rb'].values(): 0361 _w1['cb'].SetValue(True) 0362 # and turn on the OK button 0363 self.DoOkStatus() 0364 return 0365 0366 def DoOkStatus(self, evt=None): 0367 # ensure the OK button is in the right state 0368 if evt and evt.IsChecked(): 0369 enable=True 0370 self._check_for_exclusive(evt.GetEventObject()) 0371 else: 0372 enable=False 0373 for _w in self._widgets.values(): 0374 if _w['cb'].GetValue(): 0375 enable=True 0376 break 0377 self._btn_ok.Enable(enable) 0378 if evt is not None: 0379 evt.Skip() 0380 0381 class SendPhoneDialog(GetPhoneDialog): 0382 HELPID=helpids.ID_SEND_PHONE_DATA 0383 0384 # actions ("Pretty Name", "name used to query profile") 0385 actions = ( ("Send", "write"), ) 0386 0387 def __init__(self, frame, title, id=-1): 0388 GetPhoneDialog.__init__(self, frame, title, id) 0389 0390 # this is what BitPim itself doesn't supports - the phones may support less 0391 _notsupported=( 0392 ('call_history', 'write', None),) 0393 0394 0395 ### 0396 ### The master config dialog 0397 ### 0398 0399 class ConfigDialog(wx.Dialog): 0400 phonemodels=phones.phonemodels 0401 update_choices=('Never', 'Daily', 'Weekly', 'Monthly') 0402 setme="<setme>" 0403 ID_DIRBROWSE=wx.NewId() 0404 ID_COMBROWSE=wx.NewId() 0405 ID_RETRY=wx.NewId() 0406 ID_BITFLING=wx.NewId() 0407 def __init__(self, mainwindow, frame, title="BitPim Settings", id=-1): 0408 wx.Dialog.__init__(self, frame, id, title, 0409 style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE) 0410 self.mw=mainwindow 0411 0412 self.bitflingresponsequeues={} 0413 0414 gs=wx.GridBagSizer(10, 10) 0415 gs.AddGrowableCol(1) 0416 _row=0 0417 # safemode 0418 gs.Add( wx.StaticText(self, -1, "Read Only"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 0419 self.safemode=wx.CheckBox(self, wx.NewId(), "Block writing anything to the phone") 0420 gs.Add( self.safemode, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 0421 _row+=1 0422 0423 # where we store our files 0424 gs.Add( wx.StaticText(self, -1, "Disk storage"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 0425 gs.Add(wx.StaticText(self, -1, self.mw.config.Read('path', '<Unknown>')), 0426 pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 0427 _row+=1 0428 gs.Add(wx.StaticText(self, -1, 'Config File'), pos=(_row,0), 0429 flag=wx.ALIGN_CENTER_VERTICAL) 0430 gs.Add(wx.StaticText(self, -1, self.mw.config.Read('config')), 0431 pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 0432 _row+=1 0433 0434 # phone type 0435 gs.Add( wx.StaticText(self, -1, "Phone Type"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 0436 keys=self.phonemodels 0437 keys.sort() 0438 self.phonebox=wx.ComboBox(self, -1, "LG-VX4400", style=wx.CB_DROPDOWN|wx.CB_READONLY,choices=keys) 0439 self.phonebox.SetValue("LG-VX4400") 0440 gs.Add( self.phonebox, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 0441 _phone_btn=wx.Button(self, -1, 'Phone Wizard...') 0442 wx.EVT_BUTTON(self, _phone_btn.GetId(), self.OnPhoneWizard) 0443 gs.Add(_phone_btn, pos=(_row, 2), flag=wx.ALIGN_CENTER_VERTICAL) 0444 _row+=1 0445 0446 # com port 0447 gs.Add( wx.StaticText(self, -1, "Com Port"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 0448 self.commbox=wx.TextCtrl(self, -1, self.setme, size=(200,-1)) 0449 gs.Add( self.commbox, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 0450 gs.Add( wx.Button(self, self.ID_COMBROWSE, "Browse ..."), pos=(_row,2), flag=wx.ALIGN_CENTER_VERTICAL) 0451 _row+=1 0452 # com timeout 0453 gs.Add(wx.StaticText(self, -1, 'Com Timeout (sec)'), pos=(_row, 0), 0454 flag=wx.ALIGN_CENTER_VERTICAL) 0455 self.commtimeout=NumCtrl(self, -1, 0456 integerWidth=2, fractionWidth=1, 0457 allowNegative=False) 0458 gs.Add(self.commtimeout, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 0459 _row+=1 0460 0461 # Automatic check for update 0462 gs.Add(wx.StaticText(self, -1, 'Check for Update'), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 0463 self.updatebox=wx.ComboBox(self, -1, self.update_choices[0], 0464 style=wx.CB_DROPDOWN|wx.CB_READONLY, 0465 choices=self.update_choices) 0466 gs.Add(self.updatebox, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 0467 _row+=1 0468 0469 # always start with the 'Today' tab 0470 gs.Add(wx.StaticText(self, -1, 'Startup'), pos=(_row,0), 0471 flag=wx.ALIGN_CENTER_VERTICAL) 0472 self.startup=wx.CheckBox(self, wx.NewId(), 'Always start with the Today tab') 0473 gs.Add(self.startup, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 0474 _row+=1 0475 0476 # whether or not to use TaskBarIcon 0477 if guihelper.IsMSWindows(): 0478 gs.Add(wx.StaticText(self, -1, 'Task Bar Icon'), pos=(_row,0), 0479 flag=wx.ALIGN_CENTER_VERTICAL) 0480 self.taskbaricon=wx.CheckBox(self, wx.NewId(), 0481 'Place BitPim Icon in the System Tray when Minimized') 0482 gs.Add(self.taskbaricon, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 0483 wx.EVT_CHECKBOX(self, self.taskbaricon.GetId(), 0484 self.OnTaskbarChkbox) 0485 _row+=1 0486 self.taskbaricon1=wx.CheckBox(self, wx.NewId(), 0487 'Place BitPim Icon in the System Tray when Closed') 0488 self.taskbaricon1.Enable(False) 0489 gs.Add(self.taskbaricon1, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 0490 _row+=1 0491 else: 0492 self.taskbaricon=None 0493 0494 # whether or not to run autodetect at startup 0495 gs.Add(wx.StaticText(self, -1, 'Autodetect at Startup'), pos=(_row,0), 0496 flag=wx.ALIGN_CENTER_VERTICAL) 0497 self.autodetect_start=wx.CheckBox(self, wx.NewId(), 0498 'Detect phone at bitpim startup') 0499 gs.Add(self.autodetect_start, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 0500 _row+=1 0501 # Splashscreen time 0502 gs.Add(wx.StaticText(self, -1, 'SplashScreen Time (sec)'), 0503 pos=(_row, 0), 0504 flag=wx.ALIGN_CENTER_VERTICAL) 0505 self.splashscreen=NumCtrl(self, -1, 0506 integerWidth=2, fractionWidth=1, 0507 allowNegative=False) 0508 gs.Add(self.splashscreen, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 0509 _row+=1 0510 # Developer's Console 0511 if __debug__: 0512 gs.Add(wx.StaticText(self, -1, 'Developer Console'), 0513 pos=(_row,0), 0514 flag=wx.ALIGN_CENTER_VERTICAL) 0515 self.dev_console=wx.CheckBox(self, wx.NewId(), 0516 'Display Developer Console') 0517 gs.Add(self.dev_console, pos=(_row, 1), 0518 flag=wx.ALIGN_CENTER_VERTICAL) 0519 _row+=1 0520 # bitfling 0521 if bitflingscan.IsBitFlingEnabled(): 0522 self.SetupBitFlingCertVerification() 0523 gs.Add( wx.StaticText( self, -1, "BitFling"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 0524 self.bitflingenabled=wx.CheckBox(self, self.ID_BITFLING, "Enabled") 0525 gs.Add(self.bitflingenabled, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 0526 gs.Add( wx.Button(self, self.ID_BITFLING, "Settings ..."), pos=(_row,2), flag=wx.ALIGN_CENTER_VERTICAL) 0527 wx.EVT_BUTTON(self, self.ID_BITFLING, self.OnBitFlingSettings) 0528 wx.EVT_CHECKBOX(self, self.ID_BITFLING, self.ApplyBitFlingSettings) 0529 if self.mw.config.Read("bitfling/password","<unconfigured>") \ 0530 == "<unconfigured>": 0531 self.mw.config.WriteInt("bitfling/enabled", 0) 0532 self.bitflingenabled.SetValue(False) 0533 self.bitflingenabled.Enable(False) 0534 else: 0535 self.bitflingenabled=None 0536 # crud at the bottom 0537 bs=wx.BoxSizer(wx.VERTICAL) 0538 bs.Add(gs, 0, wx.EXPAND|wx.ALL, 10) 0539 bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) 0540 0541 but=self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP) 0542 bs.Add(but, 0, wx.CENTER|wx.ALL, 10) 0543 0544 wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) 0545 wx.EVT_BUTTON(self, self.ID_COMBROWSE, self.OnComBrowse) 0546 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOK) 0547 0548 self.setdefaults() 0549 0550 self.SetSizer(bs) 0551 self.SetAutoLayout(True) 0552 bs.Fit(self) 0553 0554 # Retrieve saved settings... (we only care about position) 0555 set_size("ConfigDialog", self, screenpct=-1, aspect=3.5) 0556 0557 wx.EVT_CLOSE(self, self.OnClose) 0558 0559 def OnCancel(self, _): 0560 self.saveSize() 0561 0562 def OnOK(self, _): 0563 self.saveSize() 0564 self.EndModal(wx.ID_OK) 0565 self.ApplyBitFlingSettings() 0566 0567 def OnHelp(self, _): 0568 wx.GetApp().displayhelpid(helpids.ID_SETTINGS_DIALOG) 0569 0570 def OnComBrowse(self, _): 0571 self.saveSize() 0572 if self.mw.wt is not None: 0573 self.mw.wt.clearcomm() 0574 # remember its size 0575 # w=self.mw.config.ReadInt("combrowsewidth", 640) 0576 # h=self.mw.config.ReadInt("combrowseheight", 480) 0577 p=self.mw.config.ReadInt("combrowsesash", 200) 0578 with guihelper.WXDialogWrapper(CommPortDialog(self, common.importas(phones.module(self.phonebox.GetValue())), defaultport=self.commbox.GetValue(), sashposition=p), 0579 True) as (dlg, res): 0580 self.mw.config.WriteInt("combrowsesash", dlg.sashposition) 0581 if res==wx.ID_OK: 0582 self.commbox.SetValue(dlg.GetPort()) 0583 0584 def ApplyBitFlingSettings(self, _=None): 0585 if self.bitflingenabled is not None: 0586 if self.bitflingenabled.GetValue(): 0587 bitflingscan.flinger.configure(self.mw.config.Read("bitfling/username", "<unconfigured>"), 0588 bitflingscan.decode(self.mw.config.Read("bitfling/password", 0589 "<unconfigured>")), 0590 self.mw.config.Read("bitfling/host", "<unconfigured>"), 0591 self.mw.config.ReadInt("bitfling/port", 12652)) 0592 else: 0593 bitflingscan.flinger.unconfigure() 0594 0595 def OnBitFlingSettings(self, _): 0596 with guihelper.WXDialogWrapper(BitFlingSettingsDialog(None, self.mw.config), 0597 True) as (dlg, retcode): 0598 if retcode==wx.ID_OK: 0599 dlg.SaveSettings() 0600 self.ApplyBitFlingSettings() 0601 if self.mw.config.Read("bitfling/password","<unconfigured>") \ 0602 != "<unconfigured>": 0603 self.bitflingenabled.Enable(True) 0604 0605 def SetupBitFlingCertVerification(self): 0606 "Setup all the voodoo needed for certificate verification to happen, not matter which thread wants it" 0607 EVT_BITFLINGCERTIFICATEVERIFICATION(self, self._wrapVerifyBitFlingCert) 0608 bitflingscan.flinger.SetCertVerifier(self.dispatchVerifyBitFlingCert) 0609 bitflingscan.flinger.setthreadeventloop(wx.SafeYield) 0610 0611 def dispatchVerifyBitFlingCert(self, addr, key): 0612 """Handle a certificate verification from any thread 0613 0614 The request is handed to the main gui thread, and then we wait for the 0615 results""" 0616 print thread.get_ident(),"dispatchVerifyBitFlingCert called" 0617 q=self.bitflingresponsequeues.get(thread.get_ident(), None) 0618 if q is None: 0619 q=Queue.Queue() 0620 self.bitflingresponsequeues[thread.get_ident()]=q 0621 print thread.get_ident(), "Posting BitFlingCertificateVerificationEvent" 0622 wx.PostEvent(self, BitFlingCertificateVerificationEvent(addr=addr, key=key, q=q)) 0623 print thread.get_ident(), "After posting BitFlingCertificateVerificationEvent, waiting for response" 0624 res, exc = q.get() 0625 print thread.get_ident(), "Got response", res, exc 0626 if exc is not None: 0627 ex=exc[1] 0628 ex.gui_exc_info=exc[2] 0629 raise ex 0630 return res 0631 0632 def _wrapVerifyBitFlingCert(self, evt): 0633 """Receive the event in the main gui thread for cert verification 0634 0635 We unpack the parameters, call the verification method""" 0636 print "_wrapVerifyBitFlingCert" 0637 0638 addr, hostkey, q = evt.addr, evt.key, evt.q 0639 self.VerifyBitFlingCert(addr, hostkey, q) 0640 0641 def VerifyBitFlingCert(self, addr, key, q): 0642 print "VerifyBitFlingCert for", addr, "type",key.get_name() 0643 # ::TODO:: reject if not dsa 0644 # get fingerprint 0645 fingerprint=common.hexify(key.get_fingerprint()) 0646 # do we already know about it? 0647 existing=wx.GetApp().config.Read("bitfling/certificates/%s" % (addr[0],), "") 0648 if len(existing): 0649 fp=existing 0650 if fp==fingerprint: 0651 q.put( (True, None) ) 0652 return 0653 # throw up the dialog 0654 print "asking user" 0655 dlg=AcceptCertificateDialog(None, wx.GetApp().config, addr, fingerprint, q) 0656 dlg.ShowModal() 0657 0658 def OnClose(self, evt): 0659 self.saveSize() 0660 # Don't destroy the dialong, just put it away... 0661 self.EndModal(wx.ID_CANCEL) 0662 0663 def OnTaskbarChkbox(self, evt): 0664 if evt.IsChecked(): 0665 self.taskbaricon1.Enable(True) 0666 else: 0667 self.taskbaricon1.SetValue(False) 0668 self.taskbaricon1.Enable(False) 0669 0670 def setfromconfig(self): 0671 if len(self.mw.config.Read("lgvx4400port")): 0672 self.commbox.SetValue(self.mw.config.Read("lgvx4400port", "")) 0673 self.commtimeout.SetValue(self.mw.config.ReadFloat('commtimeout', 3.0)) 0674 if self.mw.config.Read("phonetype", "") in self.phonemodels: 0675 self.phonebox.SetValue(self.mw.config.Read("phonetype")) 0676 if self.bitflingenabled is not None: 0677 self.bitflingenabled.SetValue(self.mw.config.ReadInt("bitfling/enabled", 0)) 0678 self.ApplyBitFlingSettings() 0679 self.safemode.SetValue(self.mw.config.ReadInt("Safemode", 0)) 0680 self.updatebox.SetValue(self.mw.config.Read("updaterate", 0681 self.update_choices[0])) 0682 self.startup.SetValue(self.mw.config.ReadInt("startwithtoday", 0)) 0683 if self.taskbaricon: 0684 if self.mw.config.ReadInt('taskbaricon', 0): 0685 self.taskbaricon.SetValue(True) 0686 self.taskbaricon1.Enable(True) 0687 self.taskbaricon1.SetValue(self.mw.config.ReadInt('taskbaricon1', 0)) 0688 else: 0689 self.taskbaricon.SetValue(False) 0690 self.taskbaricon1.SetValue(False) 0691 self.taskbaricon1.Enable(False) 0692 self.autodetect_start.SetValue(self.mw.config.ReadInt("autodetectstart", 0)) 0693 self.splashscreen.SetValue(self.mw.config.ReadInt('splashscreentime', 2500)/1000.0) 0694 if __debug__: 0695 self.dev_console.SetValue(self.mw.config.ReadInt('console', 0)) 0696 0697 def setdefaults(self): 0698 if self.commbox.GetValue()==self.setme: 0699 comm="auto" 0700 self.commbox.SetValue(comm) 0701 0702 def updatevariables(self): 0703 path=self.mw.config.Read('path') 0704 self.mw.configpath=path 0705 self.mw.commportsetting=str(self.commbox.GetValue()) 0706 self.mw.config.Write("lgvx4400port", self.mw.commportsetting) 0707 self.mw.config.WriteFloat('commtimeout', 0708 float(self.commtimeout.GetValue())) 0709 if self.mw.wt is not None: 0710 self.mw.wt.clearcomm() 0711 # comm parameters (retry, timeouts, flow control etc) 0712 commparm={} 0713 commparm['retryontimeout']=self.mw.config.ReadInt("commretryontimeout", False) 0714 commparm['timeout']=self.mw.config.ReadFloat('commtimeout', 3.0) 0715 commparm['hardwareflow']=self.mw.config.ReadInt('commhardwareflow', False) 0716 commparm['softwareflow']=self.mw.config.ReadInt('commsoftwareflow', False) 0717 commparm['baud']=self.mw.config.ReadInt('commbaud', 115200) 0718 self.mw.commparams=commparm 0719 # phone model 0720 self.mw.config.Write("phonetype", self.phonebox.GetValue()) 0721 # do not touch this module importing code unless you check 0722 # that it also works with the freezing tools (py2exe, py2app, 0723 # cxfreeze etc). doing the sane sensible thing (__import__) 0724 # results in the wrong module being loaded! 0725 mod=phones.module(self.phonebox.GetValue()) 0726 exec("import "+mod) 0727 self.mw.phonemodule=eval(mod) 0728 self.mw.phoneprofile=self.mw.phonemodule.Profile() 0729 pubsub.publish(pubsub.PHONE_MODEL_CHANGED, self.mw.phonemodule) 0730 # bitfling 0731 if self.bitflingenabled is not None: 0732 self.mw.bitflingenabled=self.bitflingenabled.GetValue() 0733 self.mw.config.WriteInt("bitfling/enabled", self.mw.bitflingenabled) 0734 # safemode - make sure you have to restart to disable 0735 self.mw.config.WriteInt("SafeMode", self.safemode.GetValue()) 0736 if self.safemode.GetValue(): 0737 wx.GetApp().SAFEMODE=True 0738 wx.GetApp().ApplySafeMode() 0739 # check for update rate 0740 self.mw.config.Write('updaterate', self.updatebox.GetValue()) 0741 # startup option 0742 self.mw.config.WriteInt('startwithtoday', self.startup.GetValue()) 0743 # Task Bar Icon option 0744 if self.taskbaricon: 0745 self.mw.config.WriteInt('taskbaricon', self.taskbaricon.GetValue()) 0746 self.mw.config.WriteInt('taskbaricon1', self.taskbaricon1.GetValue()) 0747 else: 0748 self.mw.config.WriteInt('taskbaricon', 0) 0749 self.mw.config.WriteInt('taskbaricon1', 0) 0750 # startup autodetect option 0751 self.mw.config.WriteInt('autodetectstart', self.autodetect_start.GetValue()) 0752 # SplashScreen Time 0753 self.mw.config.WriteInt('splashscreentime', 0754 int(self.splashscreen.GetValue()*1000)) 0755 # developer console 0756 if __debug__: 0757 self.mw.config.WriteInt('console', 0758 self.dev_console.GetValue()) 0759 # ensure config is saved 0760 self.mw.config.Flush() 0761 # update the status bar 0762 self.mw.SetPhoneModelStatus() 0763 # update the cache path 0764 self.mw.update_cache_path() 0765 0766 def needconfig(self): 0767 # Set base config 0768 self.setfromconfig() 0769 # do we know the phone? 0770 if self.mw.config.Read("phonetype", "") not in self.phonemodels: 0771 return True 0772 # are any at unknown settings 0773 if self.commbox.GetValue()==self.setme: 0774 # fill in and set defaults 0775 self.setdefaults() 0776 self.updatevariables() 0777 # any still unset? 0778 if self.commbox.GetValue()==self.setme: 0779 return True 0780 0781 return False 0782 0783 def ShowModal(self): 0784 self.setfromconfig() 0785 ec=wx.Dialog.ShowModal(self) 0786 if ec==wx.ID_OK: 0787 self.updatevariables() 0788 return ec 0789 0790 def saveSize(self): 0791 save_size("ConfigDialog", self.GetRect()) 0792 0793 def OnPhoneWizard(self, _): 0794 # clear the port 0795 if self.mw.wt is not None: 0796 self.mw.wt.clearcomm() 0797 # running the set phone wizard 0798 _wz=setphone_wizard.SetPhoneWizard(self) 0799 if _wz.RunWizard(): 0800 _res=_wz.get() 0801 self.commbox.SetValue(_res.get('com', '')) 0802 self.phonebox.SetValue(_res.get('phone', '')) 0803 0804 ### 0805 ### The select a comm port dialog box 0806 ### 0807 0808 class CommPortDialog(wx.Dialog): 0809 ID_LISTBOX=1 0810 ID_TEXTBOX=2 0811 ID_REFRESH=3 0812 ID_SASH=4 0813 ID_SAVE=5 0814 0815 def __init__(self, parent, selectedphone, id=-1, title="Choose a comm port", defaultport="auto", sashposition=0): 0816 wx.Dialog.__init__(self, parent, id, title, style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 0817 self.parent=parent 0818 self.port=defaultport 0819 self.sashposition=sashposition 0820 self.selectedphone=selectedphone 0821 0822 p=self # parent widget 0823 0824 # the listbox and textbox in a splitter 0825 splitter=wx.SplitterWindow(p, self.ID_SASH, style=wx.SP_3D|wx.SP_LIVE_UPDATE) 0826 self.lb=wx.ListBox(splitter, self.ID_LISTBOX, style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB) 0827 self.tb=wx.html.HtmlWindow(splitter, self.ID_TEXTBOX, size=wx.Size(400,400)) # default style is auto scrollbar 0828 # On Mac, top pane will go to zero size on startup completely ignoring the sashposition passed in. 0829 # We ensure that the top pane is always visible ... 0830 splitter.SetMinimumPaneSize(100) 0831 splitter.SplitHorizontally(self.lb, self.tb, sashposition) 0832 0833 # the buttons 0834 buttsizer=wx.GridSizer(1, 5) 0835 buttsizer.Add(wx.Button(p, wx.ID_OK, "OK"), 0, wx.ALL, 10) 0836 buttsizer.Add(wx.Button(p, self.ID_REFRESH, "Refresh"), 0, wx.ALL, 10) 0837 buttsizer.Add(wx.Button(p, self.ID_SAVE, "Save..."), 0, wx.ALL, 10) 0838 buttsizer.Add(wx.Button(p, wx.ID_HELP, "Help"), 0, wx.ALL, 10) 0839 buttsizer.Add(wx.Button(p, wx.ID_CANCEL, "Cancel"), 0, wx.ALL, 10) 0840 0841 # vertical join of the two 0842 vbs=wx.BoxSizer(wx.VERTICAL) 0843 vbs.Add(splitter, 1, wx.EXPAND) 0844 vbs.Add(buttsizer, 0, wx.CENTER) 0845 0846 # hook into self 0847 p.SetSizer(vbs) 0848 p.SetAutoLayout(True) 0849 vbs.Fit(p) 0850 0851 # update dialog 0852 wx.CallAfter(self.OnRefresh) 0853 0854 # hook in all the widgets 0855 wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel) 0856 wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) 0857 wx.EVT_BUTTON(self, self.ID_REFRESH, self.OnRefresh) 0858 wx.EVT_BUTTON(self, self.ID_SAVE, self.OnSave) 0859 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) 0860 wx.EVT_LISTBOX(self, self.ID_LISTBOX, self.OnListBox) 0861 wx.EVT_LISTBOX_DCLICK(self, self.ID_LISTBOX, self.OnListBox) 0862 wx.EVT_SPLITTER_SASH_POS_CHANGED(self, self.ID_SASH, self.OnSashChange) 0863 0864 # Retrieve saved settings... Use 60% of screen if not specified 0865 set_size("CommDialog", self, screenpct=60) 0866 wx.EVT_CLOSE(self, self.OnClose) 0867 0868 def OnSashChange(self, _=None): 0869 self.sashposition=self.FindWindowById(self.ID_SASH).GetSashPosition() 0870 0871 def OnRefresh(self, _=None): 0872 self.tb.SetPage("<p><b>Refreshing</b> ...") 0873 self.lb.Clear() 0874 self.Update() 0875 ports=comscan.comscan()+usbscan.usbscan() 0876 if bitflingscan.IsBitFlingEnabled(): 0877 ports=ports+bitflingscan.flinger.scan() 0878 self.portinfo=comdiagnose.diagnose(ports, self.selectedphone) 0879 if len(self.portinfo): 0880 self.portinfo=[ ("Automatic", "auto", 0881 "<p>BitPim will try to detect the correct port automatically when accessing your phone" 0882 ) ]+\ 0883 self.portinfo 0884 self.lb.Clear() 0885 sel=-1 0886 for name, actual, description in self.portinfo: 0887 if sel<0 and self.GetPort()==actual: 0888 sel=self.lb.GetCount() 0889 self.lb.Append(name) 0890 if sel<0: 0891 sel=0 0892 if self.lb.GetCount(): 0893 self.lb.SetSelection(sel) 0894 self.OnListBox() 0895 else: 0896 self.FindWindowById(wx.ID_OK).Enable(False) 0897 self.tb.SetPage("<html><body>You do not have any com/serial ports on your system</body></html>") 0898 0899 def OnListBox(self, _=None): 0900 # enable/disable ok button 0901 p=self.portinfo[self.lb.GetSelection()] 0902 if p[1] is None: 0903 self.FindWindowById(wx.ID_OK).Enable(False) 0904 else: 0905 self.port=p[1] 0906 self.FindWindowById(wx.ID_OK).Enable(True) 0907 self.tb.SetPage(p[2]) 0908 0909 0910 def OnSave(self, _): 0911 html=StringIO.StringIO() 0912 0913 print >>html, "<html><head><title>BitPim port listing - %s</title></head>" % (time.ctime(), ) 0914 print >>html, "<body><h1>BitPim port listing - %s</h1><table>" % (time.ctime(),) 0915 0916 for long,actual,desc in self.portinfo: 0917 if actual is None or actual=="auto": continue 0918 print >>html, '<tr bgcolor="#77ff77"><td colspan=2>%s</td><td>%s</td></tr>' % (long,actual) 0919 print >>html, "<tr><td colspan=3>%s</td></tr>" % (desc,) 0920 print >>html, "<tr><td colspan=3><hr></td></tr>" 0921 print >>html, "</table></body></html>" 0922 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Save port details as", defaultFile="bitpim-ports.html", wildcard="HTML files (*.html)|*.html", 0923 style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR), 0924 True) as (dlg, retcode): 0925 if retcode==wx.ID_OK: 0926 file(dlg.GetPath(), "wt").write(html.getvalue()) 0927 0928 def OnCancel(self, _): 0929 self.saveSize() 0930 self.EndModal(wx.ID_CANCEL) 0931 0932 def OnOk(self, _): 0933 self.saveSize() 0934 self.EndModal(wx.ID_OK) 0935 0936 def OnHelp(self, _): 0937 wx.GetApp().displayhelpid(helpids.ID_COMMSETTINGS_DIALOG) 0938 0939 def OnClose(self, evt): 0940 self.saveSize() 0941 # Don't destroy the dialong, just put it away... 0942 self.EndModal(wx.ID_CANCEL) 0943 0944 def GetPort(self): 0945 return self.port 0946 0947 def saveSize(self): 0948 save_size("CommDialog", self.GetRect()) 0949 0950 ### 0951 ### Accept certificate dialog 0952 ### 0953 0954 0955 class AcceptCertificateDialog(wx.Dialog): 0956 0957 def __init__(self, parent, config, addr, fingerprint, q): 0958 parent=self.FindAGoodParent(parent) 0959 wx.Dialog.__init__(self, parent, -1, "Accept certificate?", style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 0960 self.config=config 0961 self.q=q 0962 self.addr=addr 0963 self.fingerprint=fingerprint 0964 hbs=wx.BoxSizer(wx.HORIZONTAL) 0965 hbs.Add(wx.StaticText(self, -1, "Host:"), 0, wx.ALL, 5) 0966 hbs.Add(wx.StaticText(self, -1, addr[0]), 0, wx.ALL, 5) 0967 hbs.Add(wx.StaticText(self, -1, " Fingerprint:"), 0, wx.ALL, 5) 0968 hbs.Add(wx.StaticText(self, -1, fingerprint), 1, wx.ALL, 5) 0969 vbs=wx.BoxSizer(wx.VERTICAL) 0970 vbs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 0971 vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) 0972 but=self.CreateButtonSizer(wx.YES|wx.NO|wx.HELP) 0973 vbs.Add(but, 0, wx.ALIGN_CENTER|wx.ALL, 10) 0974 0975 self.SetSizer(vbs) 0976 vbs.Fit(self) 0977 0978 wx.EVT_BUTTON(self, wx.ID_YES, self.OnYes) 0979 wx.EVT_BUTTON(self, wx.ID_NO, self.OnNo) 0980 wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnNo) 0981 0982 0983 0984 def OnYes(self, _): 0985 wx.GetApp().config.Write("bitfling/certificates/%s" % (self.addr[0],), self.fingerprint) 0986 wx.GetApp().config.Flush() 0987 if self.IsModal(): 0988 self.EndModal(wx.ID_YES) 0989 else: 0990 self.Show(False) 0991 wx.CallAfter(self.Destroy) 0992 print "returning true from AcceptCertificateDialog" 0993 self.q.put( (True, None) ) 0994 0995 def OnNo(self, _): 0996 if self.IsModal(): 0997 self.EndModal(wx.ID_NO) 0998 else: 0999 self.Show(False) 1000 wx.CallAfter(self.Destroy) 1001 print "returning false from AcceptCertificateDialog" 1002 self.q.put( (False, None) ) 1003 1004 def FindAGoodParent(self, suggestion): 1005 win=wx.Window_FindFocus() 1006 while win is not None: 1007 try: 1008 if win.IsModal(): 1009 print "FindAGoodParent is",win 1010 return win 1011 except AttributeError: 1012 parent=win.GetParent() 1013 win=parent 1014 return suggestion 1015 1016 ### 1017 ### BitFling settings dialog 1018 ### 1019 1020 class BitFlingSettingsDialog(wx.Dialog): 1021 1022 ID_USERNAME=wx.NewId() 1023 ID_PASSWORD=wx.NewId() 1024 ID_HOST=wx.NewId() 1025 ID_PORT=wx.NewId() 1026 ID_TEST=wx.NewId() 1027 passwordsentinel="@+_-3@<," 1028 1029 def __init__(self, parent, config): 1030 wx.Dialog.__init__(self, parent, -1, "Edit BitFling settings", style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 1031 self.config=config 1032 gs=wx.FlexGridSizer(1, 2, 5, 5) 1033 gs.AddGrowableCol(1) 1034 gs.AddMany([ 1035 (wx.StaticText(self, -1, "Username"), 0, wx.ALIGN_CENTER_VERTICAL), 1036 (wx.TextCtrl(self, self.ID_USERNAME), 1, wx.EXPAND), 1037 (wx.StaticText(self, -1, "Password"), 0, wx.ALIGN_CENTER_VERTICAL), 1038 (wx.TextCtrl(self, self.ID_PASSWORD, style=wx.TE_PASSWORD), 1, wx.EXPAND), 1039 (wx.StaticText(self, -1, "Host"), 0, wx.ALIGN_CENTER_VERTICAL), 1040 (wx.TextCtrl(self, self.ID_HOST), 1, wx.EXPAND), 1041 (wx.StaticText(self, -1, "Port"), 0, wx.ALIGN_CENTER_VERTICAL), 1042 (wx.lib.intctrl.IntCtrl(self, self.ID_PORT, value=12652, min=1, max=65535), 0) 1043 ]) 1044 vbs=wx.BoxSizer(wx.VERTICAL) 1045 vbs.Add(gs, 0, wx.EXPAND|wx.ALL, 5) 1046 vbs.Add((1,1), 1, wx.EXPAND) 1047 vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 10) 1048 1049 gs=wx.GridSizer(1,4, 5,5) 1050 gs.Add(wx.Button(self, wx.ID_OK, "OK")) 1051 gs.Add(wx.Button(self, self.ID_TEST, "Test")) 1052 gs.Add(wx.Button(self, wx.ID_HELP, "Help")) 1053 gs.Add(wx.Button(self, wx.ID_CANCEL, "Cancel")) 1054 vbs.Add(gs, 0, wx.ALIGN_CENTER|wx.ALL, 10) 1055 self.SetSizer(vbs) 1056 vbs.Fit(self) 1057 set_size("BitFlingConfigDialog", self, -20, 0.5) 1058 1059 # event handlers 1060 wx.EVT_BUTTON(self, self.ID_TEST, self.OnTest) 1061 1062 # fill in data 1063 defaultuser="user" 1064 try: 1065 defaultuser=getpass.getuser() 1066 except: 1067 pass 1068 self.FindWindowById(self.ID_USERNAME).SetValue(config.Read("bitfling/username", defaultuser)) 1069 if len(config.Read("bitfling/password", "")): 1070 self.FindWindowById(self.ID_PASSWORD).SetValue(self.passwordsentinel) 1071 self.FindWindowById(self.ID_HOST).SetValue(config.Read("bitfling/host", "")) 1072 self.FindWindowById(self.ID_PORT).SetValue(config.ReadInt("bitfling/port", 12652)) 1073 1074 def ShowModal(self): 1075 res=wx.Dialog.ShowModal(self) 1076 save_size("BitFlingConfigDialog", self.GetRect()) 1077 return res 1078 1079 def GetSettings(self): 1080 username=self.FindWindowById(self.ID_USERNAME).GetValue() 1081 pwd=self.FindWindowById(self.ID_PASSWORD).GetValue() 1082 if pwd==self.passwordsentinel: 1083 pwd=bitflingscan.decode(self.config.Read("bitfling/password", self.passwordsentinel)) 1084 host=self.FindWindowById(self.ID_HOST).GetValue() 1085 port=self.FindWindowById(self.ID_PORT).GetValue() 1086 return username, pwd, host, port 1087 1088 def SaveSettings(self): 1089 "Copy settings from dialog fields into config object" 1090 username,pwd,host,port=self.GetSettings() 1091 self.config.Write("bitfling/username", username) 1092 self.config.Write("bitfling/password", bitflingscan.encode(pwd)) 1093 self.config.Write("bitfling/host", host) 1094 self.config.WriteInt("bitfling/port", port) 1095 1096 def OnTest(self, _): 1097 wx.CallAfter(self._OnTest) 1098 1099 def _OnTest(self, _=None): 1100 try: 1101 bitflingscan.flinger.configure(*self.GetSettings()) 1102 res=bitflingscan.flinger.getversion() 1103 guihelper.MessageDialog(self, "Succeeded. Remote version is %s" % (res,) , "Success", wx.OK|wx.ICON_INFORMATION) 1104 except Exception,ex: 1105 res="Failed: %s: %s" % sys.exc_info()[:2] 1106 if hasattr(ex, "gui_exc_info"): 1107 print common.formatexception( ex.gui_exc_info) 1108 else: 1109 print common.formatexception() 1110 guihelper.MessageDialog(self, res, "Failed", wx.OK|wx.ICON_ERROR) 1111 1112 1113 ### 1114 ### Various platform independent filename functions 1115 ### 1116 1117 basename=common.basename 1118 stripext=common.stripext 1119 getext=common.getext 1120 1121 1122 ### 1123 ### A dialog showing a message in a fixed font, with a help button 1124 ### 1125 1126 class MyFixedScrolledMessageDialog(wx.Dialog): 1127 """A dialog displaying a readonly text control with a fixed width font""" 1128 def __init__(self, parent, msg, caption, helpid, pos = wx.DefaultPosition, size = (850,600)): 1129 wx.Dialog.__init__(self, parent, -1, caption, pos, size, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 1130 1131 text=wx.TextCtrl(self, 1, 1132 style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | 1133 wx.TE_DONTWRAP ) 1134 # Fixed width font 1135 f=wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL ) 1136 ta=wx.TextAttr(font=f) 1137 text.SetDefaultStyle(ta) 1138 1139 text.AppendText(msg) # if i supply this in constructor then the font doesn't take 1140 text.SetInsertionPoint(0) 1141 text.ShowPosition(text.XYToPosition(0,0)) 1142 1143 # vertical sizer 1144 vbs=wx.BoxSizer(wx.VERTICAL) 1145 vbs.Add(text, 1, wx.EXPAND|wx.ALL, 10) 1146 1147 # buttons 1148 vbs.Add(self.CreateButtonSizer(wx.OK|wx.HELP), 0, wx.ALIGN_RIGHT|wx.ALL, 10) 1149 1150 # plumb 1151 self.SetSizer(vbs) 1152 self.SetAutoLayout(True) 1153 wx.EVT_BUTTON(self, wx.ID_HELP, lambda _,helpid=helpid: wx.GetApp().displayhelpid(helpid)) 1154 1155 ### 1156 ### Dialog that deals with exceptions 1157 ### 1158 import StringIO 1159 1160 class ExceptionDialog(wx.Dialog): 1161 def __init__(self, parent, exception, title="Exception"): 1162 wx.Dialog.__init__(self, parent, title=title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.THICK_FRAME|wx.MAXIMIZE_BOX, size=(740, 580)) 1163 self.maintext=wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2|wx.HSCROLL) 1164 vbs=wx.BoxSizer(wx.VERTICAL) 1165 vbs.Add(self.maintext, 1, wx.EXPAND|wx.ALL, 5) 1166 1167 buttsizer=wx.GridSizer(1, 4) 1168 buttsizer.Add(wx.Button(self, wx.ID_CANCEL, "Abort BitPim"), 0, wx.ALL, 10) 1169 buttsizer.Add(wx.Button(self, wx.ID_HELP, "Help"), 0, wx.ALL, 10) 1170 buttsizer.Add(wx.Button(self, wx.ID_OK, "Continue"), 0, wx.ALL, 10) 1171 _id=wx.NewId() 1172 buttsizer.Add(wx.Button(self, _id, "Create Trouble Report"), 0, wx.ALL, 10) 1173 1174 vbs.Add(buttsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 5) 1175 1176 wx.EVT_BUTTON(self, wx.ID_CANCEL, self.abort) 1177 wx.EVT_BUTTON(self, wx.ID_HELP, lambda _: wx.GetApp().displayhelpid(helpids.ID_EXCEPTION_DIALOG)) 1178 wx.EVT_BUTTON(self, _id, self.OnCreateReport) 1179 1180 self.SetSizer(vbs) 1181 self._text="" 1182 self.addexception(exception) 1183 1184 def abort(self,_): 1185 import os 1186 os._exit(1) 1187 1188 def addexception(self, exception): 1189 s=StringIO.StringIO() 1190 s.write("BitPim version: "+version.versionstring+"-"+version.vendor+"\nAn unexpected exception has occurred.\nPlease see the help for details on what to do.\n\n") 1191 if hasattr(exception, 'gui_exc_info'): 1192 s.write(common.formatexception(exception.gui_exc_info)) 1193 else: 1194 s.write("Exception with no extra info.\n%s\n" % (exception.str(),)) 1195 self._text=s.getvalue() 1196 self.maintext.SetValue(self._text) 1197 1198 def getexceptiontext(self): 1199 return self._text 1200 1201 def OnCreateReport(self, _): 1202 with guihelper.WXDialogWrapper(CreateTroubleReportDialog(self.GetParent()), 1203 True) as (_dlg, retcode): 1204 if retcode==wx.ID_OK: 1205 try: 1206 self._create_report(_dlg.GetValue()) 1207 guihelper.MessageDialog(self, 1208 'Trouble Report created successfully!', 1209 'BitPim Trouble Report', style=wx.OK) 1210 except: 1211 guihelper.MessageDialog(self, 1212 'Failed to Create Trouble Report', 1213 'Trouble Report Error', 1214 style=wx.OK|wx.ICON_ERROR) 1215 1216 def _create_report(self, vals): 1217 with contextlib.closing(gzip.GzipFile(vals['filename'], 'wb')) as _s: 1218 _s.write('BitPim Trouble Report\n') 1219 _s.write(time.asctime()+'\n') 1220 _s.write('BitPim Version: %s - %s\n'%(version.versionstring, version.vendor)) 1221 _s.write('Platorm: %s, Architecture: %s %s, Dist: %s %s %s\n'%\ 1222 ((platform.platform(),)+platform.architecture()+\ 1223 platform.dist())) 1224 # phone model if available 1225 try: 1226 _model=self.GetParent().phonemodule.Phone.desc 1227 except: 1228 _model='Not Available' 1229 _s.write('Phone Model: %s\n'%_model) 1230 _s.write('Name: %s\n'%vals['name']) 1231 _s.write('email: %s\n'%vals['email']) 1232 _s.write('Description: %s\n'%vals['description']) 1233 _s.write('Exception:\n%s\n'%self._text) 1234 # write out log data if evailable 1235 try: 1236 _log=self.GetParent().tree.lw.GetValue() 1237 except: 1238 # don't care if we can't get the log 1239 _log='Not Available' 1240 _s.write('BitPim Log:\n%s\n'%_log) 1241 # write out protocol data if available 1242 try: 1243 _log=self.GetParent().tree.lwdata.GetValue() 1244 except: 1245 _log='Not Available' 1246 _s.write('BitPim Protocol Data:\n%s\n'%_log) 1247 1248 class CreateTroubleReportDialog(wx.Dialog): 1249 1250 _default_filename='bpbug.gz' 1251 def __init__(self, parent): 1252 wx.Dialog.__init__(self, parent, -1, 'BitPim Trouble Report', 1253 style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE) 1254 1255 gs=wx.GridBagSizer(10, 10) 1256 gs.AddGrowableCol(1) 1257 _width=300 1258 _row=0 1259 # Name 1260 gs.Add( wx.StaticText(self, -1, "Name:"), pos=(_row,0), 1261 flag=wx.ALIGN_CENTER_VERTICAL) 1262 self._name=wx.TextCtrl(self, -1, '', size=(_width,-1)) 1263 gs.Add(self._name, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 1264 _row+=1 1265 # email 1266 gs.Add( wx.StaticText(self, -1, "email:"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 1267 self._email=wx.TextCtrl(self, -1, '', size=(_width,-1)) 1268 gs.Add(self._email, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 1269 _row+=1 1270 # trouble report file name 1271 gs.Add( wx.StaticText(self, -1, "File Name:"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 1272 self._filename=wx.TextCtrl(self, -1, self._default_filename, 1273 size=(_width,-1)) 1274 gs.Add(self._filename, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 1275 _browseid=wx.NewId() 1276 gs.Add(wx.Button(self, _browseid, 'Browse ...'), pos=(_row, 2), 1277 flag=wx.ALIGN_CENTER_VERTICAL) 1278 _row+=1 1279 # Trouble Description 1280 gs.Add(wx.StaticText(self, -1, 'Trouble Description:'), pos=(_row, 0), 1281 flag=wx.ALIGN_CENTER_VERTICAL) 1282 self._desc=wx.TextCtrl(self, -1, '', 1283 style=wx.TE_MULTILINE|wx.TE_BESTWRAP, 1284 size=(_width, 100)) 1285 gs.Add(self._desc, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 1286 _row+=1 1287 1288 # crud at the bottom 1289 bs=wx.BoxSizer(wx.VERTICAL) 1290 bs.Add(gs, 0, wx.EXPAND|wx.ALL, 10) 1291 bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) 1292 1293 but=self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP) 1294 bs.Add(but, 0, wx.CENTER|wx.ALL, 10) 1295 1296 wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) 1297 wx.EVT_BUTTON(self, _browseid, self.OnBrowse) 1298 1299 self.SetSizer(bs) 1300 self.SetAutoLayout(True) 1301 bs.Fit(self) 1302 1303 def OnHelp(self, _): 1304 wx.GetApp().displayhelpid(helpids.ID_TROUBLEREPORT) 1305 def OnBrowse(self, _): 1306 # how to select a source, default to select a file 1307 with guihelper.WXDialogWrapper(wx.FileDialog(self, self._filename.GetValue(), 1308 defaultFile=self._filename.GetValue(), 1309 style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT, 1310 wildcard='gzip files|*.gz'), 1311 True) as (dlg, retcode): 1312 if retcode==wx.ID_OK: 1313 self._filename.SetValue(dlg.GetPath()) 1314 def GetValue(self): 1315 # return a dict of values of this dialog 1316 return { 'name': self._name.GetValue(), 1317 'email': self._email.GetValue(), 1318 'filename': self._filename.GetValue(), 1319 'description': self._desc.GetValue(), 1320 } 1321 1322 ### 1323 ### Too much freaking effort for a simple statusbar. Mostly copied from the demo. 1324 ### 1325 BUFFERED=0 1326 class StatusText(stattext.GenStaticText): 1327 # Status value 1328 red=None 1329 green=None 1330 yellow=None 1331 def __init__(self, parent, ID, label, 1332 pos = wx.DefaultPosition, size = wx.DefaultSize, 1333 style = 0, 1334 name = "statustext"): 1335 # margin between the stoplight and the text 1336 self._text_margin=5 1337 # relative size of the status stoplight, default to 100% of the height 1338 self._status_size=1.0 1339 # color for the stop light 1340 if StatusText.red is None: 1341 StatusText.red=wx.RED 1342 StatusText.green=wx.GREEN 1343 StatusText.yellow=wx.NamedColour('YELLOW') 1344 self._stat=StatusText.green 1345 super(StatusText, self).__init__(parent, ID, label, 1346 pos=pos, style=style, name=name) 1347 def DoGetBestSize(self): 1348 """ 1349 Overridden base class virtual. Determines the best size of 1350 the control based on the label size and the current font. 1351 """ 1352 _s=super(StatusText, self).DoGetBestSize() 1353 _s.SetWidth(_s.GetWidth()+_s.GetHeight()*self._status_size+\ 1354 self._text_margin) 1355 self.CacheBestSize(_s) 1356 return _s 1357 def _draw_status(self, dc, w, h): 1358 # draw the status stoplight 1359 dc.BeginDrawing() 1360 dc.SetBrush(wx.Brush(self._stat, wx.SOLID)) 1361 _r=(h*self._status_size)/2 1362 dc.DrawCircle(_r, h/2, _r) 1363 dc.EndDrawing 1364 1365 def OnPaint(self, event): 1366 if BUFFERED: 1367 dc = wx.BufferedPaintDC(self) 1368 else: 1369 dc = wx.PaintDC(self) 1370 width, height = self.GetClientSize() 1371 if not width or not height: 1372 return 1373 1374 if BUFFERED: 1375 clr = self.GetBackgroundColour() 1376 backBrush = wx.Brush(clr, wx.SOLID) 1377 if wx.Platform == "__WXMAC__" and clr == self.defBackClr: 1378 # if colour is still the default then use the striped background on Mac 1379 backBrush.MacSetTheme(1) # 1 == kThemeBrushDialogBackgroundActive 1380 dc.SetBackground(backBrush) 1381 dc.Clear() 1382 self._draw_status(dc, width, height) 1383 dc.SetTextForeground(self.GetForegroundColour()) 1384 dc.SetFont(self.GetFont()) 1385 label = self.GetLabel() 1386 style = self.GetWindowStyleFlag() 1387 y = 0 1388 x=height*self._status_size+self._text_margin 1389 for line in label.split('\n'): 1390 if line == '': 1391 w, h = self.GetTextExtent('W') # empty lines have height too 1392 else: 1393 w, h = self.GetTextExtent(line) 1394 if style & wx.ALIGN_RIGHT: 1395 x = width - w 1396 if style & wx.ALIGN_CENTER: 1397 x = (width - w)/2 1398 dc.DrawText(line, x, y) 1399 y += h 1400 1401 def SetLabel(self, label, status=None): 1402 if status: 1403 self._stat=status 1404 super(StatusText, self).SetLabel(label) 1405 def SetStatus(self, status): 1406 self._stat=status 1407 self.Refresh() 1408 1409 SB_Phone_Set=0 1410 SB_Phone_Detected=1 1411 SB_Phone_Unavailable=2 1412 class MyStatusBar(wx.StatusBar): 1413 __total_panes=3 1414 __version_index=2 1415 __phone_model_index=2 1416 __app_status_index=0 1417 __gauge_index=1 1418 __major_progress_index=2 1419 __minor_progress_index=2 1420 __help_str_index=2 1421 __general_pane=2 1422 __pane_width=[70, 180, -1] 1423 1424 def __init__(self, parent, id=-1): 1425 wx.StatusBar.__init__(self, parent, id) 1426 self.__major_progress_text=self.__version_text=self.__phone_text='' 1427 self.sizechanged=False 1428 wx.EVT_SIZE(self, self.OnSize) 1429 wx.EVT_IDLE(self, self.OnIdle) 1430 self.gauge=wx.Gauge(self, 1000, 1) 1431 self._status=StatusText(self, -1, '', style=wx.ST_NO_AUTORESIZE) 1432 self._phone_model=StatusText(self, -1, '', style=wx.ST_NO_AUTORESIZE) 1433 self.SetFieldsCount(self.__total_panes) 1434 self.SetStatusWidths(self.__pane_width) 1435 ## self.Reposition() 1436 1437 def OnSize(self,_): 1438 self.sizechanged=True 1439 1440 def OnIdle(self,_): 1441 if not len(self.GetStatusText(self.__general_pane)): 1442 self.__set_version_phone_text() 1443 if self.sizechanged: 1444 try: 1445 self.Reposition() 1446 except: 1447 # this works around a bug in wx (on Windows only) 1448 # where we get a bogus exception. See SF bug 1449 # 873155 1450 pass 1451 1452 def Reposition(self): 1453 self.sizechanged = False 1454 rect=self.GetFieldRect(self.__gauge_index) 1455 self.gauge.SetPosition(wx.Point(rect.x+2, rect.y+2)) 1456 self.gauge.SetSize(wx.Size(rect.width-4, rect.height-4)) 1457 rect=self.GetFieldRect(self.__app_status_index) 1458 self._status.SetPosition(wx.Point(rect.x+2, rect.y+2)) 1459 self._status.SetSize(wx.Size(rect.width-4, rect.height-4)) 1460 rect=self.GetFieldRect(self.__phone_model_index) 1461 self._phone_model.SetPosition(wx.Point(rect.x+rect.width/2+2, rect.y+2)) 1462 self._phone_model.SetSize(wx.Size(rect.width/2-4, rect.height-4)) 1463 1464 def progressminor(self, pos, max, desc=""): 1465 self.gauge.SetRange(max) 1466 self.gauge.SetValue(pos) 1467 if len(self.__major_progress_text): 1468 s=self.__major_progress_text 1469 if len(desc): 1470 s+=' - '+desc 1471 else: 1472 s=desc 1473 self.SetStatusText(s, self.__minor_progress_index) 1474 1475 def progressmajor(self, pos, max, desc=""): 1476 if len(desc) and max: 1477 self.__major_progress_text="%d/%d %s" % (pos+1, max, desc) 1478 else: 1479 self.__major_progress_text=desc 1480 self.progressminor(0,1) 1481 1482 def GetHelpPane(self): 1483 return self.__help_str_index 1484 def set_app_status_ready(self): 1485 self._status.SetLabel('Ready', StatusText.green) 1486 def set_app_status_busy(self): 1487 self._status.SetLabel('BUSY', StatusText.red) 1488 def set_phone_model(self, str='', status=SB_Phone_Set): 1489 if status==SB_Phone_Detected: 1490 self.__phone_text=str+' - Detected' 1491 _stat=StatusText.green 1492 elif status==SB_Phone_Set: 1493 self.__phone_text=str+' - Manually Set' 1494 _stat=StatusText.yellow 1495 else: 1496 self.__phone_text=str+' - Unavailable' 1497 _stat=StatusText.red 1498 self._phone_model.SetLabel(self.__phone_text, _stat) 1499 def set_versions(self, current, latest=''): 1500 s='BitPim '+current 1501 if len(latest): 1502 s+='/Latest '+latest 1503 else: 1504 s+='/Latest <Unknown>' 1505 self.__version_text=s 1506 self.__set_version_phone_text() 1507 def __set_version_phone_text(self): 1508 self.SetStatusText(self.__version_text, self.__version_index) 1509 1510 ### 1511 ### A MessageBox with a help button 1512 ### 1513 1514 class AlertDialogWithHelp(wx.Dialog): 1515 """A dialog box with Ok button and a help button""" 1516 def __init__(self, parent, message, caption, helpfn, style=wx.DEFAULT_DIALOG_STYLE, icon=wx.ICON_EXCLAMATION): 1517 wx.Dialog.__init__(self, parent, -1, caption, style=style|wx.DEFAULT_DIALOG_STYLE) 1518 1519 p=self # parent widget 1520 1521 # horiz sizer for bitmap and text 1522 hbs=wx.BoxSizer(wx.HORIZONTAL) 1523 hbs.Add(wx.StaticBitmap(p, -1, wx.ArtProvider_GetBitmap(self.icontoart(icon), wx.ART_MESSAGE_BOX)), 0, wx.CENTER|wx.ALL, 10) 1524 hbs.Add(wx.StaticText(p, -1, message), 1, wx.CENTER|wx.ALL, 10) 1525 1526 # the buttons 1527 buttsizer=self.CreateButtonSizer(wx.HELP|style) 1528 1529 # Both vertical 1530 vbs=wx.BoxSizer(wx.VERTICAL) 1531 vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 10) 1532 vbs.Add(buttsizer, 0, wx.CENTER|wx.ALL, 10) 1533 1534 # wire it in 1535 self.SetSizer(vbs) 1536 self.SetAutoLayout(True) 1537 vbs.Fit(self) 1538 1539 wx.EVT_BUTTON(self, wx.ID_HELP, helpfn) 1540 1541 def icontoart(self, id): 1542 if id&wx.ICON_EXCLAMATION: 1543 return wx.ART_WARNING 1544 if id&wx.ICON_INFORMATION: 1545 return wx.ART_INFORMATION 1546 # ::TODO:: rest of these 1547 # fallthru 1548 return wx.ART_INFORMATION 1549 1550 ### 1551 ### Yet another dialog with user selectable buttons 1552 ### 1553 1554 class AnotherDialog(wx.Dialog): 1555 """A dialog box with user supplied buttons""" 1556 def __init__(self, parent, message, caption, buttons, helpfn=None, 1557 style=wx.DEFAULT_DIALOG_STYLE, icon=wx.ICON_EXCLAMATION): 1558 """Constructor 1559 1560 @param message: Text displayed in body of dialog 1561 @param caption: Title of dialog 1562 @param buttons: A list of tuples. Each tuple is a string and an integer id. 1563 The result of calling ShowModal() is the id 1564 @param helpfn: The function called if the user presses the help button (wx.ID_HELP) 1565 """ 1566 wx.Dialog.__init__(self, parent, -1, caption, style=style) 1567 1568 p=self # parent widget 1569 1570 # horiz sizer for bitmap and text 1571 hbs=wx.BoxSizer(wx.HORIZONTAL) 1572 hbs.Add(wx.StaticBitmap(p, -1, wx.ArtProvider_GetBitmap(self.icontoart(icon), wx.ART_MESSAGE_BOX)), 0, wx.CENTER|wx.ALL, 10) 1573 hbs.Add(wx.StaticText(p, -1, message), 1, wx.CENTER|wx.ALL, 10) 1574 1575 # the buttons 1576 buttsizer=wx.BoxSizer(wx.HORIZONTAL) 1577 for label,id in buttons: 1578 buttsizer.Add( wx.Button(self, id, label), 0, wx.ALL|wx.ALIGN_CENTER, 5) 1579 if id!=wx.ID_HELP: 1580 wx.EVT_BUTTON(self, id, self.OnButton) 1581 else: 1582 wx.EVT_BUTTON(self, wx.ID_HELP, helpfn) 1583 1584 # Both vertical 1585 vbs=wx.BoxSizer(wx.VERTICAL) 1586 vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 10) 1587 vbs.Add(buttsizer, 0, wx.CENTER|wx.ALL, 10) 1588 1589 # wire it in 1590 self.SetSizer(vbs) 1591 self.SetAutoLayout(True) 1592 vbs.Fit(self) 1593 1594 def OnButton(self, event): 1595 self.EndModal(event.GetId()) 1596 1597 def icontoart(self, id): 1598 if id&wx.ICON_EXCLAMATION: 1599 return wx.ART_WARNING 1600 if id&wx.ICON_INFORMATION: 1601 return wx.ART_INFORMATION 1602 # ::TODO:: rest of these 1603 # fallthru 1604 return wx.ART_INFORMATION 1605 1606 ### 1607 ### Window geometry/positioning memory 1608 ### 1609 1610 1611 def set_size(confname, window, screenpct=50, aspect=1.0): 1612 """Sets remembered/calculated dimensions/position for window 1613 1614 @param confname: subkey to store/get this windows's settings from 1615 @param window: the window object itself 1616 @param screenpct: percentage of the screen the window should occupy. 1617 If this value is negative then the window will not be resized, 1618 only repositioned (unless the current size is silly) 1619 @param aspect: aspect ratio. If greater than one then it is 1620 how much wider than tall the window is, and if less 1621 than one then the other way round 1622 """ 1623 confobj=wx.GetApp().config 1624 1625 # frig confname 1626 confname="windows/"+confname 1627 1628 # Get screen size, scale according to percentage supplied 1629 screenSize = wx.GetClientDisplayRect() 1630 if (aspect >= 1): 1631 newWidth = screenSize.width * abs(screenpct) / 100 1632 newHeight = screenSize.height * abs(screenpct) / aspect / 100 1633 else: 1634 newWidth = screenSize.width * abs(screenpct) * aspect / 100 1635 newHeight = screenSize.height * abs(screenpct) / 100 1636 1637 if screenpct<=0: 1638 rs_width,rs_height=window.GetSizeTuple() 1639 else: 1640 # Retrieve values (if any) from config database for this config object 1641 rs_width = confobj.ReadInt(confname + "/width", int(newWidth)) 1642 rs_height = confobj.ReadInt(confname + "/height", int(newHeight)) 1643 1644 # suitable magic number to show not configured. it is an exercise for the reader 1645 # why it isn't -65536 (hint: virtual desktops) 1646 unconfigured=-65245 1647 1648 rs_x = confobj.ReadInt(confname + "/x", unconfigured) 1649 rs_y = confobj.ReadInt(confname + "/y", unconfigured) 1650 1651 # Check for small window 1652 if rs_height < 96: 1653 rs_height = newHeight 1654 if rs_width < 96: 1655 rs_width = newWidth 1656 1657 # Make sure window is no larger than about screen size 1658 # 1659 # determine ratio of original oversized window so we keep the ratio if we resize... 1660 rs_aspect = rs_width/rs_height 1661 if rs_aspect >= 1: 1662 if rs_width > screenSize.width: 1663 rs_width = screenSize.width 1664 if rs_height > (screenSize.height): 1665 rs_height = (screenSize.height / rs_aspect) - screenSize.y 1666 else: 1667 if rs_width > screenSize.width: 1668 rs_width = screenSize.width * rs_aspect 1669 if rs_height > screenSize.height - screenSize.y: 1670 rs_height = screenSize.height - screenSize.y 1671 1672 # Off the screen? Just pull it back a little bit so it's visible.... 1673 if rs_x!=unconfigured and rs_x > screenSize.width: 1674 rs_x = screenSize.width - 50 1675 if rs_x!=unconfigured and rs_x + rs_width < screenSize.x: 1676 rs_x = screenSize.x 1677 if rs_y!=unconfigured and rs_y > screenSize.height: 1678 rs_y = screenSize.height - 50 1679 if rs_y!=unconfigured and rs_y + rs_height < screenSize.y: 1680 rs_y = screenSize.y 1681 1682 1683 if screenpct<=0 and (rs_width,rs_height)==window.GetSizeTuple(): 1684 # set position only, and no need to resize 1685 if rs_x!=unconfigured and rs_y!=unconfigured: 1686 print "setting %s to position %d, %d" % (confname, rs_x, rs_y) 1687 window.SetPosition(wx.Point(rs_x, rs_y)) 1688 else: 1689 if rs_x==unconfigured or rs_y==unconfigured: 1690 print "setting %s to size %d x %d" % (confname, rs_width, rs_height) 1691 window.SetSize(wx.Size(rs_width, rs_height)) 1692 else: 1693 print "setting %s to position %d, %d - size %d x %d" % (confname, rs_x, rs_y, rs_width, rs_height) 1694 window.SetDimensions(rs_x, rs_y, rs_width, rs_height) 1695 1696 def save_size(confname, myRect): 1697 """Saves size to config. L{set_size} 1698 1699 @param confname: Same string as in set_size 1700 @param myRect: Window size you want remembered, typically window.GetRect() 1701 """ 1702 confobj=wx.GetApp().config 1703 1704 confname="windows/"+confname 1705 1706 x = myRect.x 1707 y = myRect.y 1708 width = myRect.width 1709 height = myRect.height 1710 1711 confobj.WriteInt(confname + "/x", x) 1712 confobj.WriteInt(confname + "/y", y) 1713 confobj.WriteInt(confname + "/width", width) 1714 confobj.WriteInt(confname + "/height", height) 1715 confobj.Flush() 1716 1717 class LogProgressDialog(wx.ProgressDialog): 1718 """ display log string and progress bar at the same time 1719 """ 1720 def __init__(self, title, message, maximum=100, parent=None, 1721 style=wx.PD_AUTO_HIDE|wx.PD_APP_MODAL): 1722 super(LogProgressDialog, self).__init__(title, message, maximum, 1723 parent, style) 1724 self.__progress_value=0 1725 def Update(self, value, newmsg='', skip=None): 1726 self.__progress_value=value 1727 super(LogProgressDialog, self).Update(value, newmsg, skip) 1728 def log(self, msgstr): 1729 super(LogProgressDialog, self).Update(self.__progress_value, msgstr) 1730 1731 class AskPhoneNameDialog(wx.Dialog): 1732 def __init__(self, parent, message, caption="Enter phone owner's name", style=wx.DEFAULT_DIALOG_STYLE): 1733 """ Ask a user to enter an owner's name of a phone. 1734 Similar to the wx.TextEntryDialog but has 3 buttons, Ok, No Thanks, and 1735 Maybe latter. 1736 """ 1737 super(AskPhoneNameDialog, self).__init__(parent, -1, caption, style=style) 1738 vbs=wx.BoxSizer(wx.VERTICAL) 1739 vbs.Add(wx.StaticText(self, -1, message), 0, wx.ALL, 5) 1740 self.__text_ctrl=wx.TextCtrl(self, -1, style=wx.TE_PROCESS_ENTER) 1741 vbs.Add(self.__text_ctrl, 0, wx.EXPAND|wx.ALL, 5) 1742 vbs.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5) 1743 hbs=wx.BoxSizer(wx.HORIZONTAL) 1744 ok_btn=wx.Button(self, wx.ID_OK, 'OK') 1745 hbs.Add(ok_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1746 cancel_btn=wx.Button(self, wx.ID_CANCEL, 'No Thanks') 1747 hbs.Add(cancel_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1748 maybe_btn=wx.Button(self, wx.NewId(), 'Maybe next time') 1749 hbs.Add(maybe_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1750 vbs.Add(hbs, 1, wx.ALL, 5) 1751 wx.EVT_BUTTON(self, maybe_btn.GetId(), self.__OnMaybe) 1752 wx.EVT_TEXT_ENTER(self, self.__text_ctrl.GetId(), self.__OnTextEnter) 1753 self.SetSizer(vbs) 1754 self.SetAutoLayout(True) 1755 vbs.Fit(self) 1756 def GetValue(self): 1757 return self.__text_ctrl.GetValue() 1758 def __OnMaybe(self, evt): 1759 self.EndModal(evt.GetId()) 1760 def __OnTextEnter(self, _): 1761 self.EndModal(wx.ID_OK) 1762 1763 class HistoricalDataDialog(wx.Dialog): 1764 Current_Data=0 1765 Historical_Data=1 1766 _Historical_Date=1 1767 _Historical_Event=2 1768 def __init__(self, parent, caption='Historical Data Selection', 1769 current_choice=Current_Data, 1770 historical_date=None, 1771 historical_events=None): 1772 super(HistoricalDataDialog, self).__init__(parent, -1, caption) 1773 vbs=wx.BoxSizer(wx.VERTICAL) 1774 hbs=wx.BoxSizer(wx.HORIZONTAL) 1775 self.data_selector=wx.RadioBox(self, wx.NewId(), 1776 'Data Selection:', 1777 choices=('Current', 'Historical Date', 1778 'Historical Event'), 1779 style=wx.RA_SPECIFY_ROWS) 1780 self.data_selector.SetSelection(current_choice) 1781 wx.EVT_RADIOBOX(self, self.data_selector.GetId(), self.OnSelectData) 1782 hbs.Add(self.data_selector, 0, wx.ALL, 5) 1783 static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 1784 'Historical Date:'), 1785 wx.VERTICAL) 1786 self.data_date=wx.DatePickerCtrl(self, 1787 style=wx.DP_DROPDOWN | wx.DP_SHOWCENTURY) 1788 if historical_date is not None: 1789 self.data_date.SetValue(wx.DateTimeFromTimeT(historical_date)) 1790 self.data_date.Enable(current_choice==self._Historical_Date) 1791 static_bs.Add(self.data_date, 1, wx.EXPAND, 0) 1792 hbs.Add(static_bs, 0, wx.ALL, 5) 1793 # historical events 1794 static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Historical Events:'), 1795 wx.VERTICAL) 1796 self.hist_events=wx.ListBox(self, -1, style=wx.LB_SINGLE) 1797 if historical_events: 1798 self._populate_historical_events(historical_events) 1799 self.hist_events.Enable(current_choice==self._Historical_Event) 1800 static_bs.Add(self.hist_events, 1, wx.EXPAND, 0) 1801 hbs.Add(static_bs, 0, wx.ALL, 5) 1802 1803 vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5) 1804 vbs.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5) 1805 vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL), 0, 1806 wx.ALIGN_CENTER|wx.ALL, 5) 1807 self.SetSizer(vbs) 1808 self.SetAutoLayout(True) 1809 vbs.Fit(self) 1810 1811 def OnSelectData(self, evt): 1812 self.data_date.Enable(evt.GetInt()==self._Historical_Date) 1813 self.hist_events.Enable(evt.GetInt()==self._Historical_Event) 1814 1815 def GetValue(self): 1816 choice=self.data_selector.GetSelection() 1817 if choice==self.Current_Data: 1818 mode=self.Current_Data 1819 time_t=None 1820 elif choice==self._Historical_Date: 1821 dt=self.data_date.GetValue() 1822 dt.SetHour(23) 1823 dt.SetMinute(59) 1824 dt.SetSecond(59) 1825 mode=self.Historical_Data 1826 time_t=dt.GetTicks() 1827 else: 1828 sel=self.hist_events.GetSelection() 1829 if sel==wx.NOT_FOUND: 1830 mode=self.Current_Data 1831 time_t=None 1832 else: 1833 mode=self.Historical_Data 1834 time_t=self.hist_events.GetClientData(sel) 1835 return mode, time_t 1836 1837 def _populate_historical_events(self, historical_events): 1838 keys=historical_events.keys() 1839 keys.sort() 1840 keys.reverse() 1841 for k in keys: 1842 # build the string 1843 self.hist_events.Append('%s %02d-Adds %02d-Dels %02d-Mods'%\ 1844 (time.strftime('%b %d, %y %H:%M:%S', 1845 time.localtime(k)), 1846 historical_events[k]['add'], 1847 historical_events[k]['del'], 1848 historical_events[k]['mod']), 1849 k) 1850 1851 1852 class BitPimListCtrl(wx.ListCtrl, listmix.ColumnSorterMixin): 1853 def __init__(self, parent, column_info): 1854 self.lcparent=parent 1855 wx.ListCtrl.__init__(self, self.lcparent, wx.NewId(), style=wx.LC_REPORT|wx.LC_VIRTUAL) 1856 index=0 1857 self.column_type=[] 1858 for info in column_info: 1859 text, width, int_sort=info 1860 self.InsertColumn(index, text, width=width) 1861 self.column_type.append(int_sort) 1862 index+=1 1863 self.handle_paint=False 1864 listmix.ColumnSorterMixin.__init__(self, index) 1865 self.font=wx.TheFontList.FindOrCreateFont(10, family=wx.SWISS, style=wx.NORMAL, weight=wx.NORMAL) 1866 self.image_list=wx.ImageList(16, 16) 1867 a={"sm_up":"GO_UP","sm_dn":"GO_DOWN","w_idx":"WARNING","e_idx":"ERROR","i_idx":"QUESTION"} 1868 for k,v in a.items(): 1869 s="self.%s= self.image_list.Add(wx.ArtProvider_GetBitmap(wx.ART_%s,wx.ART_TOOLBAR,(16,16)))" % (k,v) 1870 exec(s) 1871 self.SetImageList(self.image_list, wx.IMAGE_LIST_SMALL) 1872 1873 def SortItems(self,sorter=None): 1874 col=self._col 1875 sf=self._colSortFlag[col] 1876 1877 #creating pairs [column item defined by col, key] 1878 items=[] 1879 for k,v in self.itemDataMap.items(): 1880 if self.column_type[col]: 1881 items.append([int(v[col]),k]) 1882 else: 1883 items.append([v[col],k]) 1884 1885 items.sort() 1886 k=[key for value, key in items] 1887 1888 # False is descending 1889 if sf==False: 1890 k.reverse() 1891 1892 self.itemIndexMap=k 1893 1894 #redrawing the list 1895 self.Refresh() 1896 1897 def GetListCtrl(self): 1898 return self 1899 1900 def GetSortImages(self): 1901 return (self.sm_dn, self.sm_up) 1902 1903 def OnGetItemText(self, item, col): 1904 index=self.itemIndexMap[item] 1905 s = self.itemDataMap[index][col] 1906 return s 1907 1908 def OnGetItemImage(self, item): 1909 return -1 1910 1911 def OnGetItemAttr(self, item): 1912 return None 1913 1914 def GetItemData(self, item): 1915 index=self.itemIndexMap[item] 1916 return self.itemPyDataMap[index] 1917 1918 def SelectAll(self): 1919 item=self.GetTopItem() 1920 while item!=-1: 1921 self.Select(item) 1922 item=self.GetNextItem(item) 1923 1924 def ResetView(self, display_data, data_keys): 1925 self.itemDataMap = display_data 1926 self.itemIndexMap = display_data.keys() 1927 self.itemPyDataMap = data_keys 1928 count=len(self.lcparent.nodes) 1929 self.SetItemCount(count) 1930 self.SortListItems() 1931 if count==0 and not self.handle_paint: 1932 wx.EVT_PAINT(self, self.OnPaint) 1933 self.handle_paint=True 1934 elif count!=0 and self.handle_paint: 1935 self.Unbind(wx.EVT_PAINT) 1936 self.handle_paint=False 1937 1938 def OnPaint(self, evt): 1939 w,h=self.GetSize() 1940 self.Refresh() 1941 dc=wx.PaintDC(self) 1942 dc.BeginDrawing() 1943 dc.SetFont(self.font) 1944 x,y= dc.GetTextExtent("There are no items to show in this view") 1945 # center the text 1946 xx=(w-x)/2 1947 if xx<0: 1948 xx=0 1949 dc.DrawText("There are no items to show in this view", xx, h/3) 1950 dc.EndDrawing() 1951 1952 def GetSelections(self): 1953 sels_idx={} 1954 index=0 1955 sels_idx[index]=self.GetFirstSelected() 1956 # build up a list of all selected items 1957 while sels_idx[index]!=-1: 1958 index+=1 1959 sels_idx[index]=self.GetNextSelected(sels_idx[index-1]) 1960 del sels_idx[index] 1961 return sels_idx 1962 1963 class DRRecFileDialog(wx.Dialog): 1964 """ 1965 A dialog to ask for and provide the file name for a Data Recording file 1966 """ 1967 def __init__(self, parent): 1968 super(DRRecFileDialog, self).__init__(parent, -1, 1969 'BitPim Data Recording') 1970 vbs=wx.BoxSizer(wx.VERTICAL) 1971 fgs=wx.GridBagSizer(5, 5) 1972 fgs.Add(wx.StaticText(self, -1, 'File Name:'), pos=(0,0), 1973 flag=wx.EXPAND|wx.ALL) 1974 self._file_name=wx.TextCtrl(self, -1, 'bitpim.dat') 1975 fgs.Add(self._file_name, pos=(0, 1), flag=wx.EXPAND|wx.ALL) 1976 _brw_btn=wx.Button(self, -1, 'Browse') 1977 fgs.Add(_brw_btn, pos=(0, 2), flag=wx.EXPAND|wx.ALL) 1978 wx.EVT_BUTTON(self, _brw_btn.GetId(), self.OnBrowse) 1979 fgs.Add(wx.StaticText(self, -1, 'Open Mode:'), pos=(1,0), 1980 flag=wx.EXPAND|wx.ALL) 1981 self._append=wx.CheckBox(self, -1, 'Append to existing file') 1982 fgs.Add(self._append, pos=(1, 1), flag=wx.EXPAND|wx.ALL) 1983 if __debug__: 1984 _setstart_btn=wx.Button(self, -1, 'Set Start') 1985 wx.EVT_BUTTON(self, _setstart_btn.GetId(), self.OnSetStart) 1986 fgs.Add(_setstart_btn, pos=(1, 2), flag=wx.EXPAND|wx.ALL) 1987 fgs.Add(wx.StaticText(self, -1, 'Status:'), pos=(2,0), 1988 flag=wx.EXPAND|wx.ALL) 1989 self._status=wx.StaticText(self, -1, 'None') 1990 fgs.Add(self._status, pos=(2,1), flag=wx.EXPAND|wx.ALL) 1991 vbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 5) 1992 vbs.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5) 1993 hbs=wx.BoxSizer(wx.HORIZONTAL) 1994 if __debug__: 1995 _btn=wx.Button(self, -1, 'View') 1996 wx.EVT_BUTTON(self,_btn.GetId(), self.OnView) 1997 hbs.Add(_btn, 0, wx.EXPAND|wx.ALL, 5) 1998 _btn=wx.Button(self, -1, 'Record') 1999 wx.EVT_BUTTON(self, _btn.GetId(), self.OnRecord) 2000 hbs.Add(_btn, 0, wx.EXPAND|wx.ALL, 5) 2001 if __debug__: 2002 _btn=wx.Button(self, -1, 'Play') 2003 wx.EVT_BUTTON(self, _btn.GetId(), self.OnPlay) 2004 hbs.Add(_btn, 0, wx.EXPAND|wx.ALL, 5) 2005 _btn=wx.Button(self, -1, 'Stop') 2006 wx.EVT_BUTTON(self, _btn.GetId(), self.OnStop) 2007 hbs.Add(_btn, 0, wx.EXPAND|wx.ALL, 5) 2008 _btn=wx.Button(self, wx.ID_CANCEL, 'Close') 2009 hbs.Add(_btn, 0, wx.EXPAND|wx.ALL, 5) 2010 vbs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 2011 2012 self._update_status() 2013 2014 self.SetSizer(vbs) 2015 self.SetAutoLayout(True) 2016 vbs.Fit(self) 2017 2018 def _update_status(self): 2019 _stat='None' 2020 _fname=None 2021 if data_recording.DR_On: 2022 _stat='Recording ...' 2023 _fname=data_recording.filename() 2024 elif data_recording.DR_Play: 2025 _stat='Playing back ...' 2026 _fname=data_recording.filename() 2027 else: 2028 _stat='None' 2029 self._status.SetLabel(_stat) 2030 if _fname: 2031 self._file_name.SetValue(_fname) 2032 2033 def OnBrowse(self, _): 2034 _dlg=wx.FileDialog(self) 2035 _dlg.SetPath(self._file_name.GetValue()) 2036 with guihelper.WXDialogWrapper(_dlg, True) as (_dlg, retcode): 2037 if retcode==wx.ID_OK: 2038 self._file_name.SetValue(_dlg.GetPath()) 2039 2040 def OnView(self, _): 2041 _dr_file=data_recording.DR_Read_File(self._file_name.GetValue()) 2042 analyser.Analyser(data=_dr_file.get_string_data()).Show() 2043 2044 def OnRecord(self, _): 2045 data_recording.record_to_file(self._file_name.GetValue()) 2046 self._update_status() 2047 2048 def OnPlay(self, _=None): 2049 data_recording.playback_from_file(self._file_name.GetValue()) 2050 self._update_status() 2051 2052 def OnStop(self, _): 2053 data_recording.stop() 2054 self._update_status() 2055 2056 def OnSetStart(self, _): 2057 if not data_recording.DR_Play: 2058 # not playing back, start playing 2059 self.OnPlay() 2060 with guihelper.WXDialogWrapper(wx.SingleChoiceDialog(self, 'Select the Starting Point', 2061 'Data Recording Set Start', 2062 choices=data_recording.get_headers()), 2063 True) as (_dlg, retcode): 2064 if retcode==wx.ID_OK: 2065 data_recording.set_start(_dlg.GetSelection()) 2066 2067 # About Dialog----------------------------------------------------------------- 2068 _license="""The BitPim code is under the GNU General Public License as detailed 2069 below. Specific permission is granted for this code to be linked to 2070 OpenSSL (this is necessary because the OpenSSL license is not 2071 GPL-compatible). 2072 2073 In addition, as a special exception, the BitPim copyright holders 2074 give permission to link the code of this program with the OpenSSL 2075 library (or with modified versions of OpenSSL), and distribute 2076 linked combinations including the two. You must obey the GNU 2077 General Public License in all respects for all of the code used 2078 other than OpenSSL. If you modify any files, you may extend this 2079 exception to your version of the file, but you are not obligated to 2080 do so. If you do not wish to do so, delete this exception statement 2081 from your version. 2082 2083 Please also note that some code is taken from other projects with a 2084 GPL compatible license. This is noted in the specific files. 2085 2086 BitPim also uses several other components with GPL compatible 2087 licenses. The online help details those components, credits the 2088 authors and details the licenses. 2089 --------------------------------------------------------------------- 2090 This program is free software; you can redistribute it and/or modify 2091 it under the terms of the GNU General Public License version 2 as published by 2092 the Free Software Foundation. 2093 2094 This program is distributed in the hope that it will be useful, 2095 but WITHOUT ANY WARRANTY; without even the implied warranty of 2096 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2097 GNU General Public License for more details. Also see the BitPim Help for more details 2098 """ 2099 2100 _copyright="""(C) 2003-2007 The code in BitPim is copyright by several people. Please note the 2101 comments at the top of each file, as well as version control history. 2102 """ 2103 _description=""" BitPim is a program that allows you to view and manipulate data on many CDMA phones 2104 from LG, Samsung, Sanyo and other manufacturers. This includes the PhoneBook, 2105 Calendar, WallPapers, RingTones (functionality varies by phone) and the 2106 Filesystem for most Qualcomm CDMA chipset based phones. To see when phones will 2107 be supported, which ones are already supported and which features are supported, 2108 see online help. 2109 2110 [%s] 2111 """ 2112 def _component_string(): 2113 """return a CSV string of various software components being used by BitPim""" 2114 _res=[] 2115 _str=[] 2116 # Python version 2117 _str.append('Python %s'%sys.version.split()[0]) 2118 _str.append('wxPython %s'%wx.version()) 2119 _res.append(', '.join(_str)) 2120 _str=[] 2121 _str.append('APSW %s'%apsw.apswversion()) 2122 _str.append('SQLITE %s'%apsw.sqlitelibversion()) 2123 _res.append(', '.join(_str)) 2124 _str=[] 2125 _str.append('serial %s'%serial.VERSION) 2126 # pywin32 version 2127 try: 2128 _pywin32ver=file(os.path.join(sys.prefix,'lib','site-packages', 'pywin32.version.txt'), 2129 'rt').read()[:-1] 2130 _str.append('pywin32 %s'%_pywin32ver) 2131 except: 2132 pass 2133 _res.append(', '.join(_str)) 2134 return '\n'.join(_res) 2135 2136 def show_about_dlg(parent): 2137 global _license, _copyright, _description 2138 info = wx.AboutDialogInfo() 2139 info.Name = "BitPim" 2140 info.Version = version.versionstring+" - "+version.vendor 2141 info.Copyright=_copyright 2142 info.Description = _description%_component_string() 2143 info.WebSite = ("http://www.bitpim.org", "www.bitpim.org") 2144 info.Developers = [ "Joe Pham", 2145 "Stephen Wood", 2146 "Sean Burke", 2147 "Nathan Hjelm", 2148 "and others ..."] 2149 2150 info.License = _license 2151 # Then we call wx.AboutBox giving it that info object 2152 wx.AboutBox(info) 2153 2154 # Generic Print Dialog---------------------------------------------------------- 2155 class PrintDialog(wx.Dialog): 2156 """A generic print dialog from which other can subclass for their own use""" 2157 _template_filename=None 2158 _style_filename=None 2159 2160 def __init__(self, widget, mainwindow, config, title): 2161 super(PrintDialog, self).__init__(mainwindow, -1, title) 2162 self._widget=widget 2163 self._xcp=self._html=self._dns=None 2164 self._tmp_file=common.gettempfilename("htm") 2165 # main box sizer 2166 vbs=wx.BoxSizer(wx.VERTICAL) 2167 # create the main contents of the dialog 2168 self._create_contents(vbs) 2169 # create the separator & default buttons 2170 self._create_buttons(vbs) 2171 # all done 2172 self.SetSizer(vbs) 2173 self.SetAutoLayout(True) 2174 vbs.Fit(self) 2175 2176 def _create_contents(self, vbs): 2177 # subclass must implement 2178 raise NotImplementedError 2179 2180 def _create_buttons(self, vbs): 2181 vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 2182 hbs=wx.BoxSizer(wx.HORIZONTAL) 2183 for b in ((None, wx.ID_PRINT, self.OnPrint), 2184 ('Page Setup', wx.ID_PAGE_SETUP, self.OnPageSetup), 2185 (None, wx.ID_PREVIEW, self.OnPrintPreview), 2186 ('Save as HTML', -1, self.OnSaveHTML), 2187 (None, wx.ID_CLOSE, self.OnClose)): 2188 if b[0]: 2189 btn=wx.Button(self, b[1], b[0]) 2190 else: 2191 btn=wx.Button(self, b[1]) 2192 hbs.Add(btn, 0, wx.ALIGN_CENTER|wx.ALL, 5) 2193 if b[2] is not None: 2194 wx.EVT_BUTTON(self, btn.GetId(), b[2]) 2195 vbs.Add(hbs, 0, wx.ALIGN_CENTRE|wx.EXPAND|wx.ALL, 5) 2196 2197 def _init_print_data(self): 2198 # Initialize the dns dict with empty data 2199 self._dns={ 'common': __import__('common') } 2200 self._dns['guihelper']=__import__('guihelper') 2201 self._dns['pagebreakstr']='<div style="page-break-before:always"/>' 2202 2203 def _get_print_data(self): 2204 raise NotImplementedError 2205 2206 def _gen_print_data(self): 2207 # generate the html page of the print data 2208 if self._xcp is None: 2209 # build the whole document template 2210 self._xcp=xyaptu.xcopier(None) 2211 tmpl=file(guihelper.getresourcefile(self._template_filename), 2212 'rt').read() 2213 self._xcp.setupxcopy(tmpl) 2214 if self._dns is None: 2215 self._init_print_data() 2216 self._get_print_data() 2217 self._html=self._xcp.xcopywithdns(self._dns.copy()) 2218 # apply styles 2219 sd={'styles': {}, '__builtins__': __builtins__ } 2220 try: 2221 if self._style_filename: 2222 execfile(guihelper.getresourcefile(self._style_filename), 2223 sd, sd) 2224 except UnicodeError: 2225 common.unicode_execfile(guihelper.getresourcefile(self._style_filename), 2226 sd, sd) 2227 try: 2228 self._html=bphtml.applyhtmlstyles(self._html, sd['styles']) 2229 except: 2230 if __debug__: 2231 file('debug.html', 'wt').write(self._html) 2232 raise 2233 2234 # standard handlers 2235 def OnPrint(self, _): 2236 self._gen_print_data() 2237 wx.GetApp().htmlprinter.PrintText(self._html) 2238 def OnPageSetup(self, _): 2239 wx.GetApp().htmlprinter.PageSetup() 2240 def OnPrintPreview(self, _): 2241 self._gen_print_data() 2242 wx.GetApp().htmlprinter.PreviewText(self._html) 2243 def OnSaveHTML(self, _): 2244 with guihelper.WXDialogWrapper(wx.FileDialog(self, wildcard="Web Page (*.htm;*.html)|*.htm;*html", 2245 style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT), 2246 True) as (_dlg, retcode): 2247 if retcode==wx.ID_OK: 2248 self._gen_print_data() 2249 file(_dlg.GetPath(), 'wt').write(self._html) 2250 def OnClose(self, _): 2251 try: 2252 # remove the temp file, ignore exception if file does not exist 2253 os.remove(self._tmp_file) 2254 except: 2255 pass 2256 self.EndModal(wx.ID_CANCEL) 2257 2258 # SMS Print Dialog-------------------------------------------------------------- 2259 class SMSPrintDialog(PrintDialog): 2260 2261 _template_filename='sms.xy' 2262 _title='SMS Print' 2263 _item_name='SMS Messages' 2264 2265 def __init__(self, smswidget, mainwindow, config): 2266 self._sel_data=smswidget.get_selected_data() 2267 self._data=smswidget.get_data() 2268 super(SMSPrintDialog, self).__init__(smswidget, mainwindow, 2269 config, self._title) 2270 2271 def _create_contents(self, vbs): 2272 rbs=wx.StaticBoxSizer(wx.StaticBox(self, -1, self._item_name), wx.VERTICAL) 2273 lsel=len(self._sel_data) 2274 lall=len(self._data) 2275 self.rows_selected=wx.RadioButton(self, wx.NewId(), "Selected (%d)" % (lsel,), style=wx.RB_GROUP) 2276 self.rows_all=wx.RadioButton(self, wx.NewId(), "All (%d)" % (lall,)) 2277 if lsel==0: 2278 self.rows_selected.Enable(False) 2279 self.rows_selected.SetValue(0) 2280 self.rows_all.SetValue(1) 2281 rbs.Add(self.rows_selected, 0, wx.EXPAND|wx.ALL, 2) 2282 rbs.Add(self.rows_all, 0, wx.EXPAND|wx.ALL, 2) 2283 vbs.Add(rbs, 0, wx.EXPAND|wx.ALL, 5) 2284 2285 def _init_print_data(self): 2286 # Initialize the dns dict with empty data 2287 super(SMSPrintDialog, self)._init_print_data() 2288 self._dns['items']={} 2289 self._dns['keys']=[] 2290 2291 def _get_print_data(self): 2292 if self.rows_all.GetValue(): 2293 _items=self._data 2294 _keys=self._widget.get_keys() 2295 else: 2296 _items=self._sel_data 2297 _keys=self._widget.get_selected_keys() 2298 if not _keys: 2299 _keys=_items.keys() 2300 self._dns['items']=_items 2301 self._dns['keys']=_keys 2302 2303 # Memo Print Dialog------------------------------------------------------------- 2304 class MemoPrintDialog(SMSPrintDialog): 2305 _template_filename='memo.xy' 2306 _title='Memo Print' 2307 _item_name='Memo Entries' 2308
Generated by PyXR 0.9.4