Prepare for release, mention why there is still no Windows version.
[wammu.git] / Wammu / PhoneWizard.py
blobe48757b5ea92df8913afa84fb5134451a82ac8c6
1 # -*- coding: UTF-8 -*-
2 # vim: expandtab sw=4 ts=4 sts=4:
3 '''
4 Wammu - Phone manager
5 Phone configuration wizard
6 '''
7 __author__ = 'Michal Čihař'
8 __email__ = 'michal@cihar.com'
9 __license__ = '''
10 Copyright © 2003 - 2008 Michal Čihař
12 This program is free software; you can redistribute it and/or modify it
13 under the terms of the GNU General Public License version 2 as published by
14 the Free Software Foundation.
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 more details.
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 '''
26 import wx
27 import wx.wizard
28 import Wammu.Paths
29 import Wammu.Wizard
30 import Wammu.Data
31 import Wammu.SettingsStorage
32 import Wammu.PhoneSearch
33 import Wammu.Events
34 import Wammu.Utils
35 import wx.lib.hyperlink
36 from Wammu.Locales import StrConv
38 class FinalPage(Wammu.Wizard.InputPage):
39 """
40 Shows result of configuration, and allow to name phone.
41 """
42 def __init__(self, parent):
43 Wammu.Wizard.InputPage.__init__(self, parent,
44 _('Configuration done'),
45 _('Thank you for configuring phone connection.'),
46 parent.settings.GetName(),
47 _('You can enter any name which you will use to identify your phone.')
50 def GetNext(self):
51 return Wammu.Wizard.InputPage.GetNext(self)
53 def Blocked(self, evt):
54 self.parent.settings.SetName(self.edit.GetValue())
55 return False
57 def Activated(self, evt):
58 self.edit.SetValue(self.parent.settings.GetName())
60 class TestPage(Wammu.Wizard.SimplePage):
61 """
62 Tests phone connection.
63 """
64 def __init__(self, parent):
65 Wammu.Wizard.SimplePage.__init__(self, parent, _('Connection test'))
66 self.detail = wx.StaticText(
67 self,
68 -1,
69 _('Wammu is now testing phone connection, please wait...'))
70 self.detail.Wrap(400)
71 self.sizer.Add(self.detail, 0, wx.ALL, 5)
72 self.name = ''
73 self.thread = None
74 self.Bind(Wammu.Events.EVT_DATA, self.OnSearchEnd)
76 def GetNext(self):
77 self.parent.pg_final.SetPrev(self)
78 return self.parent.pg_final
80 def Activated(self, evt):
81 if evt.GetDirection():
82 self.detail.SetLabel(
83 _('Wammu is now testing phone connection, please wait...'))
84 device = self.parent.settings.GetPort()
85 connection = self.parent.settings.GetGammuDriver()
86 self.thread = Wammu.PhoneSearch.PhoneInfoThread(self, device, connection)
87 self.thread.start()
88 self.name = ''
90 def OnSearchEnd(self, evt):
91 self.thread = None
92 if evt.data is None:
93 self.detail.SetLabel('%s\n%s' % (
94 evt.error[0],
95 evt.error[1]
97 self.detail.Wrap(400)
98 else:
99 manuf = evt.data['Manufacturer']
100 model = evt.data['Model'][0]
101 self.name = '%s %s' % (manuf, model)
102 self.parent.settings.SetName(self.name)
103 self.detail.SetLabel(
104 _('Phone has been found.') +
105 (_('Manufacturer: %(manufacturer)s\nModel: %(model)s') %
106 { 'manufacturer' : manuf, 'model' : model}))
107 self.detail.Wrap(400)
109 def Blocked(self, evt):
110 if self.thread is not None and self.thread.isAlive():
111 wx.MessageDialog(self,
112 _('Phone connection test is still active, you can not continue.'),
113 _('Testing still active!'),
114 wx.OK | wx.ICON_ERROR).ShowModal()
115 return True
116 if evt.GetDirection() and self.name == '':
117 if wx.MessageDialog(self,
118 _('Phone has not been found, are you sure you want to continue?'),
119 _('Phone not found!'),
120 wx.YES_NO | wx.NO_DEFAULT | wx.ICON_ERROR).ShowModal() == wx.ID_YES:
121 return False
122 return True
123 return False
125 def Cancel(self, evt):
126 # FIXME: we should abort test here
127 if self.thread is not None and self.thread.isAlive():
128 wx.MessageDialog(self,
129 _('Phone connection test is still active, you can not continue.'),
130 _('Testing still active!'),
131 wx.OK | wx.ICON_ERROR).ShowModal()
132 return False
133 return True
135 class PhoneSearchPage(Wammu.Wizard.TextPage):
137 Search for phone.
139 def __init__(self, parent):
140 Wammu.Wizard.TextPage.__init__(self, parent,
141 _('Phone search'),
142 _('Phone searching status') + ':')
143 self.Bind(Wammu.Events.EVT_DONE, self.OnDone)
144 self.Bind(Wammu.Events.EVT_TEXT, self.OnText)
145 self.Bind(Wammu.Events.EVT_SHOW_MESSAGE, self.OnShowMessage)
146 self.results = []
147 self.thread = None
149 def GetNext(self):
150 self.parent.pg_test.SetPrev(self)
151 return self.parent.pg_test
153 def Blocked(self, evt):
154 if self.thread is not None and self.thread.isAlive():
155 wx.MessageDialog(self,
156 _('Phone search is still active, you can not continue.'),
157 _('Searching still active!'),
158 wx.OK | wx.ICON_ERROR).ShowModal()
159 return True
160 if evt.GetDirection() and len(self.results) == 0:
161 wx.MessageDialog(self,
162 _('No phone has not been found, you can not continue.'),
163 _('No phone found!'),
164 wx.OK | wx.ICON_ERROR).ShowModal()
165 return True
166 return False
168 def Cancel(self, evt):
169 # FIXME: we should abort searching here
170 if self.thread is not None and self.thread.isAlive():
171 wx.MessageDialog(self,
172 _('Phone search is still active, you can not continue.'),
173 _('Searching still active!'),
174 wx.OK | wx.ICON_ERROR).ShowModal()
175 return False
176 return True
178 def Activated(self, evt):
179 if evt.GetDirection():
180 self.edit.Clear()
181 self.edit.AppendText(_('Wammu is now searching for phone:') + '\n')
182 self.thread = Wammu.PhoneSearch.AllSearchThread(
183 lock = 'no',
184 callback = self.SearchDone,
185 msgcallback = self.SearchMessage,
186 noticecallback = self.SearchNotice,
187 limit = self.parent.settings.GetConnection(),
188 win = self)
189 self.thread.start()
190 self.results = []
192 def SearchNotice(self, title, text):
193 evt = Wammu.Events.ShowMessageEvent(
194 message = text,
195 title = title,
196 type = wx.ICON_WARNING)
197 wx.PostEvent(self, evt)
199 def SearchMessage(self, text):
201 This has to send message as it is called from different thread.
203 evt = Wammu.Events.TextEvent(text = text + '\n')
204 wx.PostEvent(self, evt)
206 def SearchDone(self, lst):
208 This has to send message as it is called from different thread.
210 self.results = lst
211 evt = Wammu.Events.DoneEvent()
212 wx.PostEvent(self, evt)
214 def OnText(self, evt):
215 self.edit.AppendText(StrConv(evt.text))
217 def OnShowMessage(self, evt):
218 wx.MessageDialog(self.parent,
219 StrConv(evt.message),
220 StrConv(evt.title),
221 wx.OK | evt.type).ShowModal()
223 def OnDone(self, evt):
225 Select one config to use.
227 if len(self.results) == 0:
228 self.edit.AppendText(_('No phone has been found!') + '\n')
229 return
230 if len(self.results) > 1:
231 # Allow user to select phone
232 # FIXME: Might be in wizard, but this should be rare...
233 choices = []
234 for phone in self.results:
235 choices.append(
236 _('Model %(model)s (%(manufacturer)s) on %(port)s port using connection %(connection)s') %
238 'model': phone[2][0],
239 'manufacturer': phone[3],
240 'port': phone[0],
241 'connection': phone[1]
243 dlg = wx.SingleChoiceDialog(self, _('Select phone to use from bellow list'), _('Select phone'),
244 choices)
245 if dlg.ShowModal() == wx.ID_OK:
246 idx = dlg.GetSelection()
247 config = self.results[idx]
248 else:
249 self.results = []
250 config = None
251 else:
252 # Use directly only found phone
253 config = self.results[0]
255 if config is not None:
256 self.parent.settings.SetPort(config[0])
257 self.parent.settings.SetGammuDriver(config[1])
258 self.edit.AppendText(_('Following phone will be used:') + '\n')
259 self.edit.AppendText(
260 _('Model %(model)s (%(manufacturer)s) on %(port)s port using connection %(connection)s') %
262 'model': config[2][0],
263 'manufacturer': config[3],
264 'port': config[0],
265 'connection': config[1]
267 else:
268 self.edit.AppendText(_('No phone selected!') + '\n')
271 class ManualPage(Wammu.Wizard.MultiInputPage):
273 Manual phone configuration.
275 def __init__(self, parent):
276 Wammu.Wizard.MultiInputPage.__init__(self, parent,
277 _('Manual configuration'),
279 _('Port where phone is connected') + ':',
280 _('Connection type') + ':',
283 parent.settings.GetDevices()[0],
284 Wammu.Data.Connections,
287 def GetNext(self):
288 self.parent.settings.SetPort(self.edits[0].GetValue())
289 self.parent.settings.SetGammuDriver(self.edits[1].GetValue())
290 self.parent.pg_test.SetPrev(self)
291 return self.parent.pg_test
293 def Blocked(self, evt):
294 if evt.GetDirection():
295 if self.edits[0].GetValue() == '':
296 wx.MessageDialog(self,
297 _('You need to select port which will be used.'),
298 _('No port selected!'),
299 wx.OK | wx.ICON_ERROR).ShowModal()
300 return True
301 if self.edits[1].GetValue() == '':
302 wx.MessageDialog(self,
303 _('You need to select connection type which will be used.'),
304 _('No connection selected!'),
305 wx.OK | wx.ICON_ERROR).ShowModal()
306 return True
307 return False
309 class PhonePortPage(Wammu.Wizard.InputPage):
311 Selects phone port.
313 def __init__(self, parent):
314 ports, helptext = parent.settings.GetDevices()
315 Wammu.Wizard.InputPage.__init__(self, parent,
316 _('Phone port'),
317 _('Please enter port where phone is connected') + ':',
318 ports,
319 helptext)
321 def GetNext(self):
322 self.parent.settings.SetPort(self.edit.GetValue())
323 self.parent.pg_test.SetPrev(self)
324 return self.parent.pg_test
326 def Blocked(self, evt):
327 if evt.GetDirection() and self.edit.GetValue() == '':
328 wx.MessageDialog(self,
329 _('You need to select port which will be used.'),
330 _('No port selected!'),
331 wx.OK | wx.ICON_ERROR).ShowModal()
332 return True
333 return False
335 class PhoneGammuDriverPage(Wammu.Wizard.ChoicePage):
337 Selects real Gammu phone driver.
339 def __init__(self, parent):
340 self.names, connections, helps = parent.settings.GetGammuDrivers()
342 if len(self.names) == 0:
343 Wammu.Wizard.SimplePage.__init__(self, parent,
344 _('Driver to use'),
345 _('Sorry no driver matches your configuration, please return back and try different settings or manual configuration.'))
346 else:
347 Wammu.Wizard.ChoicePage.__init__(self, parent,
348 _('Driver to use'),
349 _('Driver to use'),
350 connections, helps,
351 extratext = _('Please select which driver you want to use. Follow the help text shown bellow to select the best one.')
354 def GetNext(self):
356 Dynamically create next page for current settings.
358 if len(self.names) == 0:
359 return None
360 self.parent.settings.SetGammuDriver(self.names[self.GetType()])
361 next = PhonePortPage(self.parent)
362 next.SetPrev(self)
363 return next
365 class PhoneDriverPage(Wammu.Wizard.ChoicePage):
367 Selects Gammu phone driver type.
369 def __init__(self, parent):
370 self.names, connections, helps = parent.settings.GetDrivers()
372 Wammu.Wizard.ChoicePage.__init__(self, parent,
373 _('Connection type'),
374 _('Connection type'),
375 connections, helps,
376 extratext = _('Please select connection type, default choice should be best in most cases.')
380 def GetNext(self):
382 Dynamically create next page for current settings.
384 self.parent.settings.SetDriver(self.names[self.GetType()])
385 next = PhoneGammuDriverPage(self.parent)
386 next.SetPrev(self)
387 return next
389 class PhoneManufacturerPage(Wammu.Wizard.ChoicePage):
391 Selects phone manufacturer.
393 def __init__(self, parent):
394 self.names, connections, helps = parent.settings.GetManufacturers()
396 Wammu.Wizard.ChoicePage.__init__(self, parent,
397 _('Phone type'),
398 _('Phone type'),
399 connections, helps,
400 extratext = _('Please select phone manufacturer or type. Try to be as specific as possible.'),
403 def GetNext(self):
405 Dynamically create next page for current settings.
407 self.parent.settings.SetManufacturer(self.names[self.GetType()])
408 next = PhoneDriverPage(self.parent)
409 next.SetPrev(self)
410 return next
412 class PhoneConnectionPage(Wammu.Wizard.ChoicePage):
414 Selects phone connection type.
416 def __init__(self, parent, search = True):
417 self.names = []
418 self.search = search
419 connections = []
420 helps = []
422 if search:
423 self.names.append('all')
424 connections.append(_('Search all connections'))
425 helps.append(_('Wizard will search for all possible connections. It might take quite long time to search all possible connection types.'))
427 self.names.append('usb')
428 connections.append(_('USB cable'))
429 helps.append(_('Many phones now come with USB cable, select this if you\'re using this connection type.'))
431 self.names.append('bluetooth')
432 connections.append(_('Bluetooth'))
433 helps.append(_('Bluetooth connection is wireless and does not require direct visibility. Phone needs to be properly paired with computer before proceeding.'))
435 self.names.append('irda')
436 connections.append(_('IrDA'))
437 helps.append(_('IrDA wireless connection requires direct visibility, please make sure this is fullfilled and computer can see phone.'))
439 self.names.append('serial')
440 connections.append(_('Serial cable'))
441 helps.append(_('This is not often used connection, but was very popular for older phones.'))
443 Wammu.Wizard.ChoicePage.__init__(self, parent,
444 _('Connection type'),
445 _('Connection type'),
446 connections, helps,
447 extratext = _('How is your phone connected?'),
450 def GetNext(self):
451 self.parent.settings.SetConnection(self.names[self.GetType()])
452 return Wammu.Wizard.ChoicePage.GetNext(self)
454 class ConfigTypePage(Wammu.Wizard.ChoicePage):
456 Allows user to select how to configure phone.
458 def __init__(self, parent, pg0, pg1, pg2):
459 Wammu.Wizard.ChoicePage.__init__(self, parent,
460 _('Configuration style'),
461 _('Configuration style'),
463 _('Guided configuration'),
464 _('Automatically search for a phone'),
465 _('Manual configuration'),
469 _('You will be guided through configuration by phone connection type and vendor.'),
470 _('Wizard will attempt to search phone on usual ports.'),
471 _('You know what you are doing and know exact parameters you need for connecting to phone.'),
473 [ pg0, pg1, pg2],
474 extratext = _('How do you want to configure your phone connection?'),
476 self.info = wx.StaticText(
477 self,
479 _('If you have no idea how to configure your phone connection, you can look at Gammu Phone Database for other users experiences:'))
480 self.info.Wrap(400)
481 self.sizer.Add(self.info, 0, wx.ALL, 5)
482 self.link = wx.lib.hyperlink.HyperLinkCtrl(
483 self,
485 'http://%scihar.com/gammu/phonedb' % Wammu.Utils.GetWebsiteLang())
486 self.sizer.Add(self.link, 0, wx.ALL, 5)
488 class WelcomePage(Wammu.Wizard.SimplePage):
490 First page of Wizard.
492 def __init__(self, parent):
493 Wammu.Wizard.SimplePage.__init__(self, parent, _('Welcome'),
494 _('This wizard will help you with configuring phone connection in Wammu.'),
497 _('Please make sure you have phone ready, powered on and one of connection methods is set up:'),
498 ' - %s' %
499 _('Cable is connected.'),
500 ' - %s' %
501 _('You have enabled IrDA and phone is in visible range.'),
502 ' - %s' %
503 _('You have paired Bluetooth with computer.'),
505 _('As soon as your phone is ready, you can continue.'),
508 class ConfigureWizard:
509 def __init__(self, parent, position = 0):
510 bmp = wx.Bitmap(Wammu.Paths.MiscPath('phonewizard'))
511 self.wiz = wx.wizard.Wizard(
512 parent,
514 _('Wammu Phone Configuration Wizard'),
515 bmp)
516 self.wiz.settings = Wammu.SettingsStorage.Settings()
517 self.wiz.settings.SetPosition(position)
519 self.wiz.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
520 self.wiz.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
521 self.wiz.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
523 # Create pages
524 self.pg_title = WelcomePage(self.wiz)
526 self.pg_search1 = PhoneConnectionPage(self.wiz)
527 self.pg_search2 = PhoneSearchPage(self.wiz)
529 self.pg_guide1 = PhoneConnectionPage(self.wiz, False)
530 self.pg_guide2 = PhoneManufacturerPage(self.wiz)
532 self.pg_manual1 = ManualPage(self.wiz)
534 self.pg_type = ConfigTypePage(
535 self.wiz,
536 self.pg_guide1,
537 self.pg_search1,
538 self.pg_manual1)
540 self.pg_final = FinalPage(self.wiz)
541 self.pg_test = TestPage(self.wiz)
542 self.wiz.pg_final = self.pg_final
543 self.wiz.pg_test = self.pg_test
545 # Set their order
546 self.pg_title.SetNext(self.pg_type)
547 self.pg_type.SetPrev(self.pg_title)
549 self.pg_type.SetNext(self.pg_search1) # overrided by it's GetNext
551 # Set previous page for all types
552 self.pg_search1.SetPrev(self.pg_type)
553 self.pg_guide1.SetPrev(self.pg_type)
554 self.pg_manual1.SetPrev(self.pg_type)
556 self.pg_guide1.SetNext(self.pg_guide2)
557 self.pg_guide2.SetPrev(self.pg_guide1)
558 # rest of guide is created dynamically
560 self.pg_search1.SetNext(self.pg_search2)
561 self.pg_search2.SetPrev(self.pg_search1)
562 # rest of search is created dynamically
564 # Resize wizard
565 self.wiz.FitToPage(self.pg_title)
567 def OnPageChanging(self, evt):
568 if evt.GetPage().Blocked(evt):
569 evt.Veto()
571 def OnPageChanged(self, evt):
572 evt.GetPage().Activated(evt)
574 def OnCancel(self, evt):
575 if not evt.GetPage().Cancel(evt):
576 evt.Veto()
578 def Run(self):
579 return self.wiz.RunWizard(self.pg_title)
581 def Execute(self):
582 if self.Run():
583 return self.wiz.settings.GetSettings()
584 else:
585 return None
588 def RunConfigureWizard(parent, position = 0):
590 Executes wizard for configuring phone
592 return ConfigureWizard(parent, position).Execute()
594 class WizardApp(wx.App):
595 def OnInit(self):
597 self.SetAppName('Wammu Phone Configuration Wizard')
598 vendor = StrConv(u'Michal Čihař')
599 if vendor.find('?') != -1:
600 vendor = 'Michal Čihař'
601 self.SetVendorName(vendor)
603 wx.InitAllImageHandlers()
605 # Return a success flag
606 return True