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

Source Code for Module guiwidgets

   1  #!/usr/bin/env python 
   2   
   3  ### BITPIM 
   4  ### 
   5  ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 
   6  ### 
   7  ### This program is free software; you can redistribute it and/or modify 
   8  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   9  ### 
  10  ### $Id: guiwidgets.py 4675 2008-08-12 21:52:12Z djpham $ 
  11   
  12  """Most of the graphical user interface elements making up BitPim""" 
  13   
  14  # standard modules 
  15  from __future__ import with_statement 
  16  import contextlib 
  17  import os 
  18  import sys 
  19  import time 
  20  import copy 
  21  import StringIO 
  22  import getpass 
  23  import sha,md5 
  24  import gzip 
  25  import base64 
  26  import thread 
  27  import Queue 
  28  import shutil 
  29  import platform 
  30   
  31  # wx. modules 
  32  import wx 
  33  import wx.html 
  34  import wx.lib.intctrl 
  35  import wx.lib.newevent 
  36  import wx.lib.mixins.listctrl  as  listmix 
  37  import wx.lib.stattext as stattext 
  38  from wx.lib.masked import NumCtrl 
  39   
  40  # my modules 
  41  import apsw 
  42  import common 
  43  import version 
  44  import helpids 
  45  import comscan 
  46  import usbscan 
  47  import comdiagnose 
  48  import analyser 
  49  import guihelper 
  50  import pubsub 
  51  import bphtml 
  52  import bitflingscan 
  53  import aggregatedisplay 
  54  import phone_media_codec 
  55  import pubsub 
  56  import widgets 
  57  import phones 
  58  import setphone_wizard 
  59  import data_recording 
  60  import xyaptu 
  61  import serial 
  62   
  63  ### 
  64  ### BitFling cert stuff 
  65  ### 
  66   
  67  BitFlingCertificateVerificationEvent, EVT_BITFLINGCERTIFICATEVERIFICATION = wx.lib.newevent.NewEvent() 
  68   
  69   
  70  #### 
  71  #### A simple text widget that does nice pretty logging. 
  72  ####         
  73   
  74       
75 -class LogWindow(wx.Panel, widgets.BitPimWidget):
76 77 theanalyser=None 78
79 - def __init__(self, parent):
80 wx.Panel.__init__(self,parent, -1) 81 # have to use rich2 otherwise fixed width font isn't used on windows 82 self.tb=wx.TextCtrl(self, 1, style=wx.TE_MULTILINE| wx.TE_RICH2|wx.TE_DONTWRAP|wx.TE_READONLY) 83 f=wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL ) 84 ta=wx.TextAttr(font=f) 85 self.tb.SetDefaultStyle(ta) 86 self.sizer=wx.BoxSizer(wx.VERTICAL) 87 self.sizer.Add(self.tb, 1, wx.EXPAND) 88 self.SetSizer(self.sizer) 89 self.SetAutoLayout(True) 90 self.sizer.Fit(self) 91 wx.EVT_IDLE(self, self.OnIdle) 92 wx.EVT_SHOW(self, self.OnShow) 93 self.outstandingtext=StringIO.StringIO() 94 95 wx.EVT_KEY_UP(self.tb, self.OnKeyUp)
96
97 - def Clear(self):
98 self.tb.Clear()
99
100 - def OnSelectAll(self, _):
101 self.tb.SetSelection(-1, -1)
102
103 - def OnShow(self, show):
104 if show.GetShow(): 105 wx.CallAfter(self.CleanupView)
106
107 - def CleanupView(self):
108 self.tb.SetInsertionPoint(0) 109 self.tb.SetInsertionPointEnd() 110 self.tb.Refresh()
111
112 - def OnIdle(self,_):
113 if self.outstandingtext.tell(): 114 # this code is written to be re-entrant 115 newt=self.outstandingtext.getvalue() 116 self.outstandingtext.seek(0) 117 self.outstandingtext.truncate() 118 self.tb.AppendText(newt)
119
120 - def log(self, str, nl=True):
121 now=time.time() 122 t=time.localtime(now) 123 self.outstandingtext.write("%d:%02d:%02d.%03d " % ( t[3], t[4], t[5], int((now-int(now))*1000))) 124 self.outstandingtext.write(str) 125 if nl: 126 self.outstandingtext.write("\n")
127
128 - def logdata(self, str, data, klass=None):
129 o=self.outstandingtext 130 self.log(str, nl=False) 131 if data is not None: 132 o.write(" Data - "+`len(data)`+" bytes\n") 133 if klass is not None: 134 try: 135 o.write("<#! %s.%s !#>\n" % (klass.__module__, klass.__name__)) 136 except: 137 klass=klass.__class__ 138 o.write("<#! %s.%s !#>\n" % (klass.__module__, klass.__name__)) 139 o.write(common.datatohexstring(data)) 140 o.write("\n")
141
142 - def OnKeyUp(self, evt):
143 keycode=evt.GetKeyCode() 144 if keycode==ord('P') and evt.ControlDown() and evt.AltDown(): 145 # analyse what was selected 146 data=self.tb.GetStringSelection() 147 # or the whole buffer if it was nothing 148 if data is None or len(data)==0: 149 data=self.tb.GetValue() 150 try: 151 self.theanalyser.Show() 152 except: 153 self.theanalyser=None 154 155 if self.theanalyser is None: 156 self.theanalyser=analyser.Analyser(data=data) 157 158 self.theanalyser.Show() 159 self.theanalyser.newdata(data) 160 evt.Skip()
161
162 - def GetValue(self):
163 """return the log text""" 164 return self.tb.GetValue()
165 166 167 ### 168 ### Dialog asking what you want to sync 169 ### 170
171 -class GetPhoneDialog(wx.Dialog):
172 # sync sources ("Pretty Name", "name used to query profile") 173 sources= ( ('PhoneBook', 'phonebook'), 174 ('Calendar', 'calendar'), 175 ('Wallpaper', 'wallpaper'), 176 ('Ringtone', 'ringtone'), 177 ('Memo', 'memo'), 178 ('Todo', 'todo'), 179 ('SMS', 'sms'), 180 ('Call History', 'call_history'), 181 ('Play List', 'playlist'), 182 ('T9 User DB','t9_udb')) 183 184 # actions ("Pretty Name", "name used to query profile") 185 actions = ( ("Get", "read"), ) 186 187 NOTREQUESTED=0 188 MERGE=1 189 OVERWRITE=2 190 191 # type of action ("pretty name", "name used to query profile") 192 types= ( ("Add", MERGE), 193 ("Replace All", OVERWRITE)) 194 typename={ MERGE: 'MERGE', 195 OVERWRITE: 'OVERWRITE', 196 NOTREQUESTED: 'NOTREQUESTED', 197 } 198 199 HELPID=helpids.ID_GET_PHONE_DATA 200
201 - def __init__(self, frame, title, id=-1):
202 wx.Dialog.__init__(self, frame, id, title, 203 style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE) 204 gs=wx.FlexGridSizer(2+len(self.sources), 1+len(self.types),5 ,10) 205 gs.AddGrowableCol(1) 206 gs.AddMany( [ 207 (wx.StaticText(self, -1, "Source"), 0, wx.EXPAND),]) 208 209 for pretty,_ in self.types: 210 gs.Add(wx.StaticText(self, -1, pretty), 0, wx.ALIGN_CENTRE) 211 212 self._widgets={} 213 for desc, source in self.sources: 214 _cb=wx.CheckBox(self, wx.NewId(), desc) 215 _cb.exclusive=False 216 wx.EVT_CHECKBOX(self, _cb.GetId(), self.DoOkStatus) 217 gs.Add(_cb, 0, wx.EXPAND) 218 self._widgets[source]={ 'cb': _cb, 219 'rb': {}, 220 } 221 first=True 222 for tdesc,tval in self.types: 223 if first: 224 style=wx.RB_GROUP 225 first=0 226 else: 227 style=0 228 _rb=wx.RadioButton(self, -1, "", style=style) 229 if self._dowesupport(source, self.actions[0][1], tval): 230 wx.EVT_RADIOBUTTON(self, _rb.GetId(), self.OnOptionSelected) 231 else: 232 _rb.SetValue(False) 233 _rb.Enable(False) 234 gs.Add(_rb, 0, wx.ALIGN_CENTRE) 235 self._widgets[source]['rb'][tval]=_rb 236 237 bs=wx.BoxSizer(wx.VERTICAL) 238 bs.Add(gs, 0, wx.EXPAND|wx.ALL, 10) 239 bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) 240 241 but=self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP) 242 self._btn_ok=self.FindWindowById(wx.ID_OK) 243 bs.Add(but, 0, wx.EXPAND|wx.ALL, 10) 244 245 self.SetSizer(bs) 246 self.SetAutoLayout(True) 247 bs.Fit(self) 248 249 wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp)
250
251 - def _setting(self, sourcetype):
252 _w=self._widgets[sourcetype] 253 if not _w['cb'].GetValue(): 254 # Not selected 255 return self.NOTREQUESTED 256 for _typeval,_rb in _w['rb'].items(): 257 if _rb.GetValue(): 258 return _typeval 259 # should not get here 260 raise ValueError
261
262 - def GetPhoneBookSetting(self):
263 return self._setting("phonebook")
264
265 - def GetCalendarSetting(self):
266 return self._setting("calendar")
267
268 - def GetWallpaperSetting(self):
269 return self._setting("wallpaper")
270
271 - def GetRingtoneSetting(self):
272 return self._setting("ringtone")
273
274 - def GetMemoSetting(self):
275 return self._setting("memo")
276
277 - def GetTodoSetting(self):
278 return self._setting("todo")
279
280 - def GetSMSSetting(self):
281 return self._setting("sms")
282 - def GetCallHistorySetting(self):
283 return self._setting("call_history")
284 - def GetPlaylistSetting(self):
285 return self._setting('playlist')
286 - def GetT9Setting(self):
287 return self._setting('t9_udb')
288
289 - def OnHelp(self,_):
290 wx.GetApp().displayhelpid(self.HELPID)
291 292 # this is what BitPim itself supports - the phones may support a subset 293 _notsupported=( 294 # ('phonebook', 'read', MERGE), # sort of is 295 ('calendar', 'read', MERGE), 296 ('wallpaper', 'read', MERGE), 297 ('ringtone', 'read', MERGE), 298 ('memo', 'read', MERGE), 299 ('todo', 'read', MERGE), 300 ('playlist', 'read', MERGE), 301 ('t9_udb', 'read', MERGE)) 302
303 - def _dowesupport(self, source, action, type):
304 if (source,action,type) in self._notsupported: 305 return False 306 return True
307
308 - def UpdateWithProfile(self, profile):
309 assert len(self.types)==2 310 _action=self.actions[0][1] 311 for source,_w in self._widgets.items(): 312 _cb=_w['cb'] 313 _cb.Enable(False) 314 # are any radio buttons enabled 315 _rb_on=False 316 for _type,_rb in _w['rb'].items(): 317 if self._dowesupport(source, _action, _type) and \ 318 profile.SyncQuery(source, _action, self.typename[_type]): 319 _cb.Enable(True) 320 _cb.exclusive=profile.SyncQuery(source, _action, 'EXCLUSIVE') 321 _rb.Enable(True) 322 _rb_on|=bool(_rb.GetValue()) 323 else: 324 _rb.SetValue(False) 325 _rb.Enable(False) 326 if _cb.IsEnabled(): 327 # make sure at least one radio button is set 328 if not _rb_on: 329 for _rb in _w['rb'].values(): 330 if _rb.IsEnabled(): 331 _rb.SetValue(True) 332 break 333 else: 334 # uncheck of not enabled 335 _cb.SetValue(False)
336
337 - def ShowModal(self):
338 # we ensure the OK button is in the correct state 339 self.DoOkStatus() 340 return wx.Dialog.ShowModal(self)
341
342 - def _check_for_exclusive(self, w):
343 if w.exclusive: 344 # this one is exclusive, turn off all others 345 for _w in self._widgets.values(): 346 if _w['cb'] is not w: 347 _w['cb'].SetValue(False) 348 else: 349 # this one is not exclusive, turn off all exclusive ones 350 for _w in self._widgets.values(): 351 if _w['cb'] is not w and \ 352 _w['cb'].exclusive: 353 _w['cb'].SetValue(False)
354
355 - def OnOptionSelected(self, evt):
356 # User clicked on an option 357 # Turn on the row to which this option belongs 358 _rb=evt.GetEventObject() 359 for _w1 in self._widgets.values(): 360 if _rb in _w1['rb'].values(): 361 _w1['cb'].SetValue(True) 362 # and turn on the OK button 363 self.DoOkStatus() 364 return
365
366 - def DoOkStatus(self, evt=None):
367 # ensure the OK button is in the right state 368 if evt and evt.IsChecked(): 369 enable=True 370 self._check_for_exclusive(evt.GetEventObject()) 371 else: 372 enable=False 373 for _w in self._widgets.values(): 374 if _w['cb'].GetValue(): 375 enable=True 376 break 377 self._btn_ok.Enable(enable) 378 if evt is not None: 379 evt.Skip()
380
381 -class SendPhoneDialog(GetPhoneDialog):
382 HELPID=helpids.ID_SEND_PHONE_DATA 383 384 # actions ("Pretty Name", "name used to query profile") 385 actions = ( ("Send", "write"), ) 386
387 - def __init__(self, frame, title, id=-1):
388 GetPhoneDialog.__init__(self, frame, title, id)
389 390 # this is what BitPim itself doesn't supports - the phones may support less 391 _notsupported=( 392 ('call_history', 'write', None),)
393 394 395 ### 396 ### The master config dialog 397 ### 398
399 -class ConfigDialog(wx.Dialog):
400 phonemodels=phones.phonemodels 401 update_choices=('Never', 'Daily', 'Weekly', 'Monthly') 402 setme="<setme>" 403 ID_DIRBROWSE=wx.NewId() 404 ID_COMBROWSE=wx.NewId() 405 ID_RETRY=wx.NewId() 406 ID_BITFLING=wx.NewId()
407 - def __init__(self, mainwindow, frame, title="BitPim Settings", id=-1):
408 wx.Dialog.__init__(self, frame, id, title, 409 style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE) 410 self.mw=mainwindow 411 412 self.bitflingresponsequeues={} 413 414 gs=wx.GridBagSizer(10, 10) 415 gs.AddGrowableCol(1) 416 _row=0 417 # safemode 418 gs.Add( wx.StaticText(self, -1, "Read Only"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 419 self.safemode=wx.CheckBox(self, wx.NewId(), "Block writing anything to the phone") 420 gs.Add( self.safemode, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 421 _row+=1 422 423 # where we store our files 424 gs.Add( wx.StaticText(self, -1, "Disk storage"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 425 gs.Add(wx.StaticText(self, -1, self.mw.config.Read('path', '<Unknown>')), 426 pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 427 _row+=1 428 gs.Add(wx.StaticText(self, -1, 'Config File'), pos=(_row,0), 429 flag=wx.ALIGN_CENTER_VERTICAL) 430 gs.Add(wx.StaticText(self, -1, self.mw.config.Read('config')), 431 pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 432 _row+=1 433 434 # phone type 435 gs.Add( wx.StaticText(self, -1, "Phone Type"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 436 keys=self.phonemodels 437 keys.sort() 438 self.phonebox=wx.ComboBox(self, -1, "LG-VX4400", style=wx.CB_DROPDOWN|wx.CB_READONLY,choices=keys) 439 self.phonebox.SetValue("LG-VX4400") 440 gs.Add( self.phonebox, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 441 _phone_btn=wx.Button(self, -1, 'Phone Wizard...') 442 wx.EVT_BUTTON(self, _phone_btn.GetId(), self.OnPhoneWizard) 443 gs.Add(_phone_btn, pos=(_row, 2), flag=wx.ALIGN_CENTER_VERTICAL) 444 _row+=1 445 446 # com port 447 gs.Add( wx.StaticText(self, -1, "Com Port"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 448 self.commbox=wx.TextCtrl(self, -1, self.setme, size=(200,-1)) 449 gs.Add( self.commbox, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 450 gs.Add( wx.Button(self, self.ID_COMBROWSE, "Browse ..."), pos=(_row,2), flag=wx.ALIGN_CENTER_VERTICAL) 451 _row+=1 452 # com timeout 453 gs.Add(wx.StaticText(self, -1, 'Com Timeout (sec)'), pos=(_row, 0), 454 flag=wx.ALIGN_CENTER_VERTICAL) 455 self.commtimeout=NumCtrl(self, -1, 456 integerWidth=2, fractionWidth=1, 457 allowNegative=False) 458 gs.Add(self.commtimeout, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 459 _row+=1 460 461 # Automatic check for update 462 gs.Add(wx.StaticText(self, -1, 'Check for Update'), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 463 self.updatebox=wx.ComboBox(self, -1, self.update_choices[0], 464 style=wx.CB_DROPDOWN|wx.CB_READONLY, 465 choices=self.update_choices) 466 gs.Add(self.updatebox, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 467 _row+=1 468 469 # always start with the 'Today' tab 470 gs.Add(wx.StaticText(self, -1, 'Startup'), pos=(_row,0), 471 flag=wx.ALIGN_CENTER_VERTICAL) 472 self.startup=wx.CheckBox(self, wx.NewId(), 'Always start with the Today tab') 473 gs.Add(self.startup, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 474 _row+=1 475 476 # whether or not to use TaskBarIcon 477 if guihelper.IsMSWindows(): 478 gs.Add(wx.StaticText(self, -1, 'Task Bar Icon'), pos=(_row,0), 479 flag=wx.ALIGN_CENTER_VERTICAL) 480 self.taskbaricon=wx.CheckBox(self, wx.NewId(), 481 'Place BitPim Icon in the System Tray when Minimized') 482 gs.Add(self.taskbaricon, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 483 wx.EVT_CHECKBOX(self, self.taskbaricon.GetId(), 484 self.OnTaskbarChkbox) 485 _row+=1 486 self.taskbaricon1=wx.CheckBox(self, wx.NewId(), 487 'Place BitPim Icon in the System Tray when Closed') 488 self.taskbaricon1.Enable(False) 489 gs.Add(self.taskbaricon1, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 490 _row+=1 491 else: 492 self.taskbaricon=None 493 494 # whether or not to run autodetect at startup 495 gs.Add(wx.StaticText(self, -1, 'Autodetect at Startup'), pos=(_row,0), 496 flag=wx.ALIGN_CENTER_VERTICAL) 497 self.autodetect_start=wx.CheckBox(self, wx.NewId(), 498 'Detect phone at bitpim startup') 499 gs.Add(self.autodetect_start, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 500 _row+=1 501 # Splashscreen time 502 gs.Add(wx.StaticText(self, -1, 'SplashScreen Time (sec)'), 503 pos=(_row, 0), 504 flag=wx.ALIGN_CENTER_VERTICAL) 505 self.splashscreen=NumCtrl(self, -1, 506 integerWidth=2, fractionWidth=1, 507 allowNegative=False) 508 gs.Add(self.splashscreen, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 509 _row+=1 510 # Developer's Console 511 if __debug__: 512 gs.Add(wx.StaticText(self, -1, 'Developer Console'), 513 pos=(_row,0), 514 flag=wx.ALIGN_CENTER_VERTICAL) 515 self.dev_console=wx.CheckBox(self, wx.NewId(), 516 'Display Developer Console') 517 gs.Add(self.dev_console, pos=(_row, 1), 518 flag=wx.ALIGN_CENTER_VERTICAL) 519 _row+=1 520 # bitfling 521 if bitflingscan.IsBitFlingEnabled(): 522 self.SetupBitFlingCertVerification() 523 gs.Add( wx.StaticText( self, -1, "BitFling"), pos=(_row,0), flag=wx.ALIGN_CENTER_VERTICAL) 524 self.bitflingenabled=wx.CheckBox(self, self.ID_BITFLING, "Enabled") 525 gs.Add(self.bitflingenabled, pos=(_row,1), flag=wx.ALIGN_CENTER_VERTICAL) 526 gs.Add( wx.Button(self, self.ID_BITFLING, "Settings ..."), pos=(_row,2), flag=wx.ALIGN_CENTER_VERTICAL) 527 wx.EVT_BUTTON(self, self.ID_BITFLING, self.OnBitFlingSettings) 528 wx.EVT_CHECKBOX(self, self.ID_BITFLING, self.ApplyBitFlingSettings) 529 if self.mw.config.Read("bitfling/password","<unconfigured>") \ 530 == "<unconfigured>": 531 self.mw.config.WriteInt("bitfling/enabled", 0) 532 self.bitflingenabled.SetValue(False) 533 self.bitflingenabled.Enable(False) 534 else: 535 self.bitflingenabled=None 536 # crud at the bottom 537 bs=wx.BoxSizer(wx.VERTICAL) 538 bs.Add(gs, 0, wx.EXPAND|wx.ALL, 10) 539 bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) 540 541 but=self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP) 542 bs.Add(but, 0, wx.CENTER|wx.ALL, 10) 543 544 wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) 545 wx.EVT_BUTTON(self, self.ID_COMBROWSE, self.OnComBrowse) 546 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOK) 547 548 self.setdefaults() 549 550 self.SetSizer(bs) 551 self.SetAutoLayout(True) 552 bs.Fit(self) 553 554 # Retrieve saved settings... (we only care about position) 555 set_size("ConfigDialog", self, screenpct=-1, aspect=3.5) 556 557 wx.EVT_CLOSE(self, self.OnClose)
558
559 - def OnCancel(self, _):
560 self.saveSize()
561
562 - def OnOK(self, _):
563 self.saveSize() 564 self.EndModal(wx.ID_OK) 565 self.ApplyBitFlingSettings()
566
567 - def OnHelp(self, _):
569
570 - def OnComBrowse(self, _):
571 self.saveSize() 572 if self.mw.wt is not None: 573 self.mw.wt.clearcomm() 574 # remember its size 575 # w=self.mw.config.ReadInt("combrowsewidth", 640) 576 # h=self.mw.config.ReadInt("combrowseheight", 480) 577 p=self.mw.config.ReadInt("combrowsesash", 200) 578 with guihelper.WXDialogWrapper(CommPortDialog(self, common.importas(phones.module(self.phonebox.GetValue())), defaultport=self.commbox.GetValue(), sashposition=p), 579 True) as (dlg, res): 580 self.mw.config.WriteInt("combrowsesash", dlg.sashposition) 581 if res==wx.ID_OK: 582 self.commbox.SetValue(dlg.GetPort())
583
584 - def ApplyBitFlingSettings(self, _=None):
585 if self.bitflingenabled is not None: 586 if self.bitflingenabled.GetValue(): 587 bitflingscan.flinger.configure(self.mw.config.Read("bitfling/username", "<unconfigured>"), 588 bitflingscan.decode(self.mw.config.Read("bitfling/password", 589 "<unconfigured>")), 590 self.mw.config.Read("bitfling/host", "<unconfigured>"), 591 self.mw.config.ReadInt("bitfling/port", 12652)) 592 else: 593 bitflingscan.flinger.unconfigure()
594
595 - def OnBitFlingSettings(self, _):
596 with guihelper.WXDialogWrapper(BitFlingSettingsDialog(None, self.mw.config), 597 True) as (dlg, retcode): 598 if retcode==wx.ID_OK: 599 dlg.SaveSettings() 600 self.ApplyBitFlingSettings() 601 if self.mw.config.Read("bitfling/password","<unconfigured>") \ 602 != "<unconfigured>": 603 self.bitflingenabled.Enable(True)
604
606 "Setup all the voodoo needed for certificate verification to happen, not matter which thread wants it" 607 EVT_BITFLINGCERTIFICATEVERIFICATION(self, self._wrapVerifyBitFlingCert) 608 bitflingscan.flinger.SetCertVerifier(self.dispatchVerifyBitFlingCert) 609 bitflingscan.flinger.setthreadeventloop(wx.SafeYield)
610
611 - def dispatchVerifyBitFlingCert(self, addr, key):
612 """Handle a certificate verification from any thread 613 614 The request is handed to the main gui thread, and then we wait for the 615 results""" 616 print thread.get_ident(),"dispatchVerifyBitFlingCert called" 617 q=self.bitflingresponsequeues.get(thread.get_ident(), None) 618 if q is None: 619 q=Queue.Queue() 620 self.bitflingresponsequeues[thread.get_ident()]=q 621 print thread.get_ident(), "Posting BitFlingCertificateVerificationEvent" 622 wx.PostEvent(self, BitFlingCertificateVerificationEvent(addr=addr, key=key, q=q)) 623 print thread.get_ident(), "After posting BitFlingCertificateVerificationEvent, waiting for response" 624 res, exc = q.get() 625 print thread.get_ident(), "Got response", res, exc 626 if exc is not None: 627 ex=exc[1] 628 ex.gui_exc_info=exc[2] 629 raise ex 630 return res
631
632 - def _wrapVerifyBitFlingCert(self, evt):
633 """Receive the event in the main gui thread for cert verification 634 635 We unpack the parameters, call the verification method""" 636 print "_wrapVerifyBitFlingCert" 637 638 addr, hostkey, q = evt.addr, evt.key, evt.q 639 self.VerifyBitFlingCert(addr, hostkey, q)
640
641 - def VerifyBitFlingCert(self, addr, key, q):
642 print "VerifyBitFlingCert for", addr, "type",key.get_name() 643 # ::TODO:: reject if not dsa 644 # get fingerprint 645 fingerprint=common.hexify(key.get_fingerprint()) 646 # do we already know about it? 647 existing=wx.GetApp().config.Read("bitfling/certificates/%s" % (addr[0],), "") 648 if len(existing): 649 fp=existing 650 if fp==fingerprint: 651 q.put( (True, None) ) 652 return 653 # throw up the dialog 654 print "asking user" 655 dlg=AcceptCertificateDialog(None, wx.GetApp().config, addr, fingerprint, q) 656 dlg.ShowModal()
657
658 - def OnClose(self, evt):
659 self.saveSize() 660 # Don't destroy the dialong, just put it away... 661 self.EndModal(wx.ID_CANCEL)
662
663 - def OnTaskbarChkbox(self, evt):
664 if evt.IsChecked(): 665 self.taskbaricon1.Enable(True) 666 else: 667 self.taskbaricon1.SetValue(False) 668 self.taskbaricon1.Enable(False)
669
670 - def setfromconfig(self):
671 if len(self.mw.config.Read("lgvx4400port")): 672 self.commbox.SetValue(self.mw.config.Read("lgvx4400port", "")) 673 self.commtimeout.SetValue(self.mw.config.ReadFloat('commtimeout', 3.0)) 674 if self.mw.config.Read("phonetype", "") in self.phonemodels: 675 self.phonebox.SetValue(self.mw.config.Read("phonetype")) 676 if self.bitflingenabled is not None: 677 self.bitflingenabled.SetValue(self.mw.config.ReadInt("bitfling/enabled", 0)) 678 self.ApplyBitFlingSettings() 679 self.safemode.SetValue(self.mw.config.ReadInt("Safemode", 0)) 680 self.updatebox.SetValue(self.mw.config.Read("updaterate", 681 self.update_choices[0])) 682 self.startup.SetValue(self.mw.config.ReadInt("startwithtoday", 0)) 683 if self.taskbaricon: 684 if self.mw.config.ReadInt('taskbaricon', 0): 685 self.taskbaricon.SetValue(True) 686 self.taskbaricon1.Enable(True) 687 self.taskbaricon1.SetValue(self.mw.config.ReadInt('taskbaricon1', 0)) 688 else: 689 self.taskbaricon.SetValue(False) 690 self.taskbaricon1.SetValue(False) 691 self.taskbaricon1.Enable(False) 692 self.autodetect_start.SetValue(self.mw.config.ReadInt("autodetectstart", 0)) 693 self.splashscreen.SetValue(self.mw.config.ReadInt('splashscreentime', 2500)/1000.0) 694 if __debug__: 695 self.dev_console.SetValue(self.mw.config.ReadInt('console', 0))
696
697 - def setdefaults(self):
698 if self.commbox.GetValue()==self.setme: 699 comm="auto" 700 self.commbox.SetValue(comm)
701
702 - def updatevariables(self):
703 path=self.mw.config.Read('path') 704 self.mw.configpath=path 705 self.mw.commportsetting=str(self.commbox.GetValue()) 706 self.mw.config.Write("lgvx4400port", self.mw.commportsetting) 707 self.mw.config.WriteFloat('commtimeout', 708 float(self.commtimeout.GetValue())) 709 if self.mw.wt is not None: 710 self.mw.wt.clearcomm() 711 # comm parameters (retry, timeouts, flow control etc) 712 commparm={} 713 commparm['retryontimeout']=self.mw.config.ReadInt("commretryontimeout", False) 714 commparm['timeout']=self.mw.config.ReadFloat('commtimeout', 3.0) 715 commparm['hardwareflow']=self.mw.config.ReadInt('commhardwareflow', False) 716 commparm['softwareflow']=self.mw.config.ReadInt('commsoftwareflow', False) 717 commparm['baud']=self.mw.config.ReadInt('commbaud', 115200) 718 self.mw.commparams=commparm 719 # phone model 720 self.mw.config.Write("phonetype", self.phonebox.GetValue()) 721 # do not touch this module importing code unless you check 722 # that it also works with the freezing tools (py2exe, py2app, 723 # cxfreeze etc). doing the sane sensible thing (__import__) 724 # results in the wrong module being loaded! 725 mod=phones.module(self.phonebox.GetValue()) 726 exec("import "+mod) 727 self.mw.phonemodule=eval(mod) 728 self.mw.phoneprofile=self.mw.phonemodule.Profile() 729 pubsub.publish(pubsub.PHONE_MODEL_CHANGED, self.mw.phonemodule) 730 # bitfling 731 if self.bitflingenabled is not None: 732 self.mw.bitflingenabled=self.bitflingenabled.GetValue() 733 self.mw.config.WriteInt("bitfling/enabled", self.mw.bitflingenabled) 734 # safemode - make sure you have to restart to disable 735 self.mw.config.WriteInt("SafeMode", self.safemode.GetValue()) 736 if self.safemode.GetValue(): 737 wx.GetApp().SAFEMODE=True 738 wx.GetApp().ApplySafeMode() 739 # check for update rate 740 self.mw.config.Write('updaterate', self.updatebox.GetValue()) 741 # startup option 742 self.mw.config.WriteInt('startwithtoday', self.startup.GetValue()) 743 # Task Bar Icon option 744 if self.taskbaricon: 745 self.mw.config.WriteInt('taskbaricon', self.taskbaricon.GetValue()) 746 self.mw.config.WriteInt('taskbaricon1', self.taskbaricon1.GetValue()) 747 else: 748 self.mw.config.WriteInt('taskbaricon', 0) 749 self.mw.config.WriteInt('taskbaricon1', 0) 750 # startup autodetect option 751 self.mw.config.WriteInt('autodetectstart', self.autodetect_start.GetValue()) 752 # SplashScreen Time 753 self.mw.config.WriteInt('splashscreentime', 754 int(self.splashscreen.GetValue()*1000)) 755 # developer console 756 if __debug__: 757 self.mw.config.WriteInt('console', 758 self.dev_console.GetValue()) 759 # ensure config is saved 760 self.mw.config.Flush() 761 # update the status bar 762 self.mw.SetPhoneModelStatus() 763 # update the cache path 764 self.mw.update_cache_path()
765
766 - def needconfig(self):
767 # Set base config 768 self.setfromconfig() 769 # do we know the phone? 770 if self.mw.config.Read("phonetype", "") not in self.phonemodels: 771 return True 772 # are any at unknown settings 773 if self.commbox.GetValue()==self.setme: 774 # fill in and set defaults 775 self.setdefaults() 776 self.updatevariables() 777 # any still unset? 778 if self.commbox.GetValue()==self.setme: 779 return True 780 781 return False
782
783 - def ShowModal(self):
784 self.setfromconfig() 785 ec=wx.Dialog.ShowModal(self) 786 if ec==wx.ID_OK: 787 self.updatevariables() 788 return ec
789
790 - def saveSize(self):
791 save_size("ConfigDialog", self.GetRect())
792
793 - def OnPhoneWizard(self, _):
794 # clear the port 795 if self.mw.wt is not None: 796 self.mw.wt.clearcomm() 797 # running the set phone wizard 798 _wz=setphone_wizard.SetPhoneWizard(self) 799 if _wz.RunWizard(): 800 _res=_wz.get() 801 self.commbox.SetValue(_res.get('com', '')) 802 self.phonebox.SetValue(_res.get('phone', ''))
803 804 ### 805 ### The select a comm port dialog box 806 ### 807
808 -class CommPortDialog(wx.Dialog):
809 ID_LISTBOX=1 810 ID_TEXTBOX=2 811 ID_REFRESH=3 812 ID_SASH=4 813 ID_SAVE=5 814
815 - def __init__(self, parent, selectedphone, id=-1, title="Choose a comm port", defaultport="auto", sashposition=0):
816 wx.Dialog.__init__(self, parent, id, title, style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 817 self.parent=parent 818 self.port=defaultport 819 self.sashposition=sashposition 820 self.selectedphone=selectedphone 821 822 p=self # parent widget 823 824 # the listbox and textbox in a splitter 825 splitter=wx.SplitterWindow(p, self.ID_SASH, style=wx.SP_3D|wx.SP_LIVE_UPDATE) 826 self.lb=wx.ListBox(splitter, self.ID_LISTBOX, style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB) 827 self.tb=wx.html.HtmlWindow(splitter, self.ID_TEXTBOX, size=wx.Size(400,400)) # default style is auto scrollbar 828 # On Mac, top pane will go to zero size on startup completely ignoring the sashposition passed in. 829 # We ensure that the top pane is always visible ... 830 splitter.SetMinimumPaneSize(100) 831 splitter.SplitHorizontally(self.lb, self.tb, sashposition) 832 833 # the buttons 834 buttsizer=wx.GridSizer(1, 5) 835 buttsizer.Add(wx.Button(p, wx.ID_OK, "OK"), 0, wx.ALL, 10) 836 buttsizer.Add(wx.Button(p, self.ID_REFRESH, "Refresh"), 0, wx.ALL, 10) 837 buttsizer.Add(wx.Button(p, self.ID_SAVE, "Save..."), 0, wx.ALL, 10) 838 buttsizer.Add(wx.Button(p, wx.ID_HELP, "Help"), 0, wx.ALL, 10) 839 buttsizer.Add(wx.Button(p, wx.ID_CANCEL, "Cancel"), 0, wx.ALL, 10) 840 841 # vertical join of the two 842 vbs=wx.BoxSizer(wx.VERTICAL) 843 vbs.Add(splitter, 1, wx.EXPAND) 844 vbs.Add(buttsizer, 0, wx.CENTER) 845 846 # hook into self 847 p.SetSizer(vbs) 848 p.SetAutoLayout(True) 849 vbs.Fit(p) 850 851 # update dialog 852 wx.CallAfter(self.OnRefresh) 853 854 # hook in all the widgets 855 wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel) 856 wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) 857 wx.EVT_BUTTON(self, self.ID_REFRESH, self.OnRefresh) 858 wx.EVT_BUTTON(self, self.ID_SAVE, self.OnSave) 859 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) 860 wx.EVT_LISTBOX(self, self.ID_LISTBOX, self.OnListBox) 861 wx.EVT_LISTBOX_DCLICK(self, self.ID_LISTBOX, self.OnListBox) 862 wx.EVT_SPLITTER_SASH_POS_CHANGED(self, self.ID_SASH, self.OnSashChange) 863 864 # Retrieve saved settings... Use 60% of screen if not specified 865 set_size("CommDialog", self, screenpct=60) 866 wx.EVT_CLOSE(self, self.OnClose)
867
868 - def OnSashChange(self, _=None):
869 self.sashposition=self.FindWindowById(self.ID_SASH).GetSashPosition()
870
871 - def OnRefresh(self, _=None):
872 self.tb.SetPage("<p><b>Refreshing</b> ...") 873 self.lb.Clear() 874 self.Update() 875 ports=comscan.comscan()+usbscan.usbscan() 876 if bitflingscan.IsBitFlingEnabled(): 877 ports=ports+bitflingscan.flinger.scan() 878 self.portinfo=comdiagnose.diagnose(ports, self.selectedphone) 879 if len(self.portinfo): 880 self.portinfo=[ ("Automatic", "auto", 881 "<p>BitPim will try to detect the correct port automatically when accessing your phone" 882 ) ]+\ 883 self.portinfo 884 self.lb.Clear() 885 sel=-1 886 for name, actual, description in self.portinfo: 887 if sel<0 and self.GetPort()==actual: 888 sel=self.lb.GetCount() 889 self.lb.Append(name) 890 if sel<0: 891 sel=0 892 if self.lb.GetCount(): 893 self.lb.SetSelection(sel) 894 self.OnListBox() 895 else: 896 self.FindWindowById(wx.ID_OK).Enable(False) 897 self.tb.SetPage("<html><body>You do not have any com/serial ports on your system</body></html>")
898
899 - def OnListBox(self, _=None):
900 # enable/disable ok button 901 p=self.portinfo[self.lb.GetSelection()] 902 if p[1] is None: 903 self.FindWindowById(wx.ID_OK).Enable(False) 904 else: 905 self.port=p[1] 906 self.FindWindowById(wx.ID_OK).Enable(True) 907 self.tb.SetPage(p[2])
908 909
910 - def OnSave(self, _):
911 html=StringIO.StringIO() 912 913 print >>html, "<html><head><title>BitPim port listing - %s</title></head>" % (time.ctime(), ) 914 print >>html, "<body><h1>BitPim port listing - %s</h1><table>" % (time.ctime(),) 915 916 for long,actual,desc in self.portinfo: 917 if actual is None or actual=="auto": continue 918 print >>html, '<tr bgcolor="#77ff77"><td colspan=2>%s</td><td>%s</td></tr>' % (long,actual) 919 print >>html, "<tr><td colspan=3>%s</td></tr>" % (desc,) 920 print >>html, "<tr><td colspan=3><hr></td></tr>" 921 print >>html, "</table></body></html>" 922 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Save port details as", defaultFile="bitpim-ports.html", wildcard="HTML files (*.html)|*.html", 923 style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR), 924 True) as (dlg, retcode): 925 if retcode==wx.ID_OK: 926 file(dlg.GetPath(), "wt").write(html.getvalue())
927
928 - def OnCancel(self, _):
929 self.saveSize() 930 self.EndModal(wx.ID_CANCEL)
931
932 - def OnOk(self, _):
933 self.saveSize() 934 self.EndModal(wx.ID_OK)
935
936 - def OnHelp(self, _):
938
939 - def OnClose(self, evt):
940 self.saveSize() 941 # Don't destroy the dialong, just put it away... 942 self.EndModal(wx.ID_CANCEL)
943
944 - def GetPort(self):
945 return self.port
946
947 - def saveSize(self):
948 save_size("CommDialog", self.GetRect())
949 950 ### 951 ### Accept certificate dialog 952 ### 953 954
955 -class AcceptCertificateDialog(wx.Dialog):
956
957 - def __init__(self, parent, config, addr, fingerprint, q):
958 parent=self.FindAGoodParent(parent) 959 wx.Dialog.__init__(self, parent, -1, "Accept certificate?", style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 960 self.config=config 961 self.q=q 962 self.addr=addr 963 self.fingerprint=fingerprint 964 hbs=wx.BoxSizer(wx.HORIZONTAL) 965 hbs.Add(wx.StaticText(self, -1, "Host:"), 0, wx.ALL, 5) 966 hbs.Add(wx.StaticText(self, -1, addr[0]), 0, wx.ALL, 5) 967 hbs.Add(wx.StaticText(self, -1, " Fingerprint:"), 0, wx.ALL, 5) 968 hbs.Add(wx.StaticText(self, -1, fingerprint), 1, wx.ALL, 5) 969 vbs=wx.BoxSizer(wx.VERTICAL) 970 vbs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 971 vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) 972 but=self.CreateButtonSizer(wx.YES|wx.NO|wx.HELP) 973 vbs.Add(but, 0, wx.ALIGN_CENTER|wx.ALL, 10) 974 975 self.SetSizer(vbs) 976 vbs.Fit(self) 977 978 wx.EVT_BUTTON(self, wx.ID_YES, self.OnYes) 979 wx.EVT_BUTTON(self, wx.ID_NO, self.OnNo) 980 wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnNo)
981 982 983
984 - def OnYes(self, _):
985 wx.GetApp().config.Write("bitfling/certificates/%s" % (self.addr[0],), self.fingerprint) 986 wx.GetApp().config.Flush() 987 if self.IsModal(): 988 self.EndModal(wx.ID_YES) 989 else: 990 self.Show(False) 991 wx.CallAfter(self.Destroy) 992 print "returning true from AcceptCertificateDialog" 993 self.q.put( (True, None) )
994
995 - def OnNo(self, _):
996 if self.IsModal(): 997 self.EndModal(wx.ID_NO) 998 else: 999 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, _):
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