PyXR

c:\projects\bitpim\src \ guiwidgets.py



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