Translated using Weblate.
[wammu.git] / Wammu / Main.py
blobd4f981009ed6e7614443c4ba996c2ac646e26b2d
1 # -*- coding: UTF-8 -*-
2 # vim: expandtab sw=4 ts=4 sts=4:
3 '''
4 Wammu - Phone manager
5 Main Wammu window
6 '''
7 __author__ = 'Michal Čihař'
8 __email__ = 'michal@cihar.com'
9 __license__ = '''
10 Copyright © 2003 - 2010 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.html
28 import sys
30 import os
31 import datetime
32 import time
33 import copy
34 import tempfile
35 import Wammu.Webbrowser
36 import locale
37 import Wammu
38 import re
40 # We can use dbus for some fancy stuff
41 try:
42 import dbus
43 import dbus.glib
44 HAVE_DBUS = True
45 except ImportError:
46 HAVE_DBUS = False
48 try:
49 import gammu
50 except SystemError, err:
51 Wammu.gammu_error = err
52 except ImportError, err:
53 Wammu.gammu_error = err
55 import Wammu.Events
56 import Wammu.Displayer
57 import Wammu.Browser
58 import Wammu.Editor
59 import Wammu.Error
60 import Wammu.Info
61 import Wammu.Utils
62 import Wammu.Logger
63 import Wammu.Message
64 import Wammu.Memory
65 import Wammu.Todo
66 import Wammu.Calendar
67 import Wammu.Settings
68 from Wammu.Paths import *
69 import wx.lib.wxpTag
70 import wx.lib.dialogs
71 import Wammu.Data
72 import Wammu.Composer
73 import Wammu.MessageDisplay
74 import Wammu.PhoneSearch
75 import Wammu.About
76 import Wammu.ErrorMessage
77 import Wammu.TalkbackDialog
78 import Wammu.WammuSettings
79 import Wammu.SMSExport
80 import Wammu.SMSXML
81 from Wammu.Locales import StrConv, ConsoleStrConv
83 TALKBACK_COUNT = 30
84 TALKBACK_DAYS = 30
86 def SortDataKeys(a, b):
87 if a == 'info':
88 return -1
89 elif b == 'info':
90 return 1
91 else:
92 return cmp(a,b)
94 def SortDataSubKeys(a, b):
95 if a == ' ':
96 return -1
97 elif b == ' ':
98 return 1
99 else:
100 return cmp(a,b)
102 displaydata = {}
103 displaydata['info'] = {}
104 displaydata['call'] = {}
105 displaydata['contact'] = {}
106 displaydata['message'] = {}
107 displaydata['todo'] = {}
108 displaydata['calendar'] = {}
110 #information
111 displaydata['info'][' '] = ('', _('Phone'), _('Phone Information'), 'phone', [
112 {'Name':_('Wammu version'), 'Value':Wammu.__version__, 'Synced': True},
114 if Wammu.gammu_error == None:
115 displaydata['info'][' '][4].append({'Name':_('Gammu version'), 'Value':gammu.Version()[0], 'Synced': True})
116 displaydata['info'][' '][4].append({'Name':_('python-gammu version'), 'Value':gammu.Version()[1], 'Synced': True})
118 # calls
119 displaydata['call'][' '] = ('info', _('Calls'), _('All Calls'), 'call', [])
120 displaydata['call']['RC'] = ('call', _('Received'), _('Received Calls'), 'call-received', [])
121 displaydata['call']['MC'] = ('call', _('Missed'), _('Missed Calls'), 'call-missed', [])
122 displaydata['call']['DC'] = ('call', _('Outgoing'), _('Outgoing Calls'), 'call-outgoing', [])
124 # contacts
125 displaydata['contact'][' '] = ('info', _('Contacts'), _('All Contacts'), 'contact', [])
126 displaydata['contact']['SM'] = ('contact', _('SIM'), _('SIM Contacts'), 'contact-sim', [])
127 displaydata['contact']['ME'] = ('contact', _('Phone'), _('Phone Contacts'), 'contact-phone', [])
129 # contacts
130 displaydata['message'][' '] = ('info', _('Messages'), _('All Messages'), 'message', [])
131 displaydata['message']['Read'] = ('message', _('Read'), _('Read Messages'), 'message-read', [])
132 displaydata['message']['UnRead'] = ('message', _('Unread'), _('Unread Messages'), 'message-unread', [])
133 displaydata['message']['Sent'] = ('message', _('Sent'), _('Sent Messages'), 'message-sent', [])
134 displaydata['message']['UnSent'] = ('message', _('Unsent'), _('Unsent Messages'), 'message-unsent', [])
136 #todos
137 displaydata['todo'][' '] = ('info', _('Todos'), _('All Todo Items'), 'todo', [])
139 #calendar
140 displaydata['calendar'][' '] = ('info', _('Calendar'), _('All Calendar Events'), 'calendar', [])
143 ## Create a new frame class, derived from the wxPython Frame.
144 class WammuFrame(wx.Frame):
146 def __init__(self, parent, id):
147 self.cfg = Wammu.WammuSettings.WammuConfig()
148 Wammu.configuration = self.cfg
149 if self.cfg.HasEntry('/Main/X') and self.cfg.HasEntry('/Main/Y'):
150 pos = wx.Point(self.cfg.ReadInt('/Main/X'), self.cfg.ReadInt('/Main/Y'))
151 else:
152 pos =wx.DefaultPosition
153 size = wx.Size(self.cfg.ReadInt('/Main/Width'), self.cfg.ReadInt('/Main/Height'))
155 wx.Frame.__init__(self, parent, id, 'Wammu', pos, size, wx.DEFAULT_FRAME_STYLE)
157 if sys.platform == 'win32':
158 img = wx.Image(AppIconPath('wammu'), wx.BITMAP_TYPE_ICO)
159 else:
160 img = wx.Image(AppIconPath('wammu'), wx.BITMAP_TYPE_PNG)
162 self.icon = wx.EmptyIcon()
163 self.icon.CopyFromBitmap(wx.BitmapFromImage(img))
165 if self.icon.GetWidth() == 16 and self.icon.GetHeight() == 16:
166 self.icon16 = self.icon
167 else:
168 img.Rescale(16, 16)
169 self.icon16 = wx.EmptyIcon()
170 self.icon16.CopyFromBitmap(wx.BitmapFromImage(img))
172 self.SetIcon(self.icon)
174 self.CreateStatusBar(2)
175 self.SetStatusWidths([-1,400])
177 # Associate some events with methods of this class
178 wx.EVT_CLOSE(self, self.CloseWindow)
179 Wammu.Events.EVT_PROGRESS(self, self.OnProgress)
180 Wammu.Events.EVT_SHOW_MESSAGE(self, self.OnShowMessage)
181 Wammu.Events.EVT_LINK(self, self.OnLink)
182 Wammu.Events.EVT_DATA(self, self.OnData)
183 Wammu.Events.EVT_SHOW(self, self.OnShow)
184 Wammu.Events.EVT_EDIT(self, self.OnEdit)
185 Wammu.Events.EVT_SEND(self, self.OnSend)
186 Wammu.Events.EVT_CALL(self, self.OnCall)
187 Wammu.Events.EVT_MESSAGE(self, self.OnMessage)
188 Wammu.Events.EVT_DUPLICATE(self, self.OnDuplicate)
189 Wammu.Events.EVT_REPLY(self, self.OnReply)
190 Wammu.Events.EVT_DELETE(self, self.OnDelete)
191 Wammu.Events.EVT_BACKUP(self, self.OnBackup)
192 Wammu.Events.EVT_EXCEPTION(self, self.OnException)
194 self.splitter = wx.SplitterWindow(self, -1)
195 il = wx.ImageList(16, 16)
197 self.tree = wx.TreeCtrl(self.splitter)
198 self.tree.AssignImageList(il)
200 self.treei = {}
201 self.values = {}
203 keys = displaydata.keys()
204 keys.sort(SortDataKeys)
205 for type in keys:
206 self.treei[type] = {}
207 self.values[type] = {}
208 subkeys = displaydata[type].keys()
209 subkeys.sort(SortDataSubKeys)
210 for subtype in subkeys:
211 self.values[type][subtype] = displaydata[type][subtype][4]
212 if displaydata[type][subtype][0] == '':
213 self.treei[type][subtype] = self.tree.AddRoot(
214 displaydata[type][subtype][1],
215 il.Add(wx.Bitmap(IconPath(displaydata[type][subtype][3]))))
216 else:
217 self.treei[type][subtype] = self.tree.AppendItem(
218 self.treei[displaydata[type][subtype][0]][' '],
219 displaydata[type][subtype][1],
220 il.Add(wx.Bitmap(IconPath(displaydata[type][subtype][3]))))
222 for type in keys:
223 self.tree.Expand(self.treei[type][' '])
225 wx.EVT_TREE_SEL_CHANGED(self, self.tree.GetId(), self.OnTreeSel)
227 # common border sizes (Gnome HIG)
228 self.separatorHalf = 3
229 self.separatorNormal = 6
230 self.separatorTwice = 12
232 # right frame
233 self.rightsplitter = wx.SplitterWindow(self.splitter, -1)
234 self.rightwin = wx.Panel(self.rightsplitter, -1)
235 self.rightwin.sizer = wx.BoxSizer(wx.VERTICAL)
237 # title text
238 self.righttitle = wx.StaticText(self.rightwin, -1, 'Wammu')
239 self.rightwin.sizer.Add(self.righttitle, 0, wx.LEFT|wx.ALL|wx.EXPAND, self.separatorNormal)
241 # line
242 self.rightwin.sizer.Add(wx.StaticLine(self.rightwin, -1), 0 , wx.EXPAND)
244 # search input
245 self.searchpanel = wx.Panel(self.rightwin, -1)
246 self.searchpanel.sizer = wx.BoxSizer(wx.HORIZONTAL)
247 self.searchpanel.sizer.Add(wx.StaticText(self.searchpanel, -1, _('Search: ')), 0, wx.LEFT | wx.CENTER)
248 self.searchinput = wx.TextCtrl(self.searchpanel, -1)
249 self.searchinput.SetToolTipString(_('Enter text to search for, please note that search type is selected next to this field. Matching is done over all fields.'))
250 self.searchpanel.sizer.Add(self.searchinput, 1, wx.CENTER | wx.ALIGN_CENTER_VERTICAL)
251 self.searchchoice = wx.Choice(self.searchpanel, choices = [_('Text'), _('Regexp'), _('Wildcard')])
252 self.searchchoice.SetToolTipString(_('Select search type'))
253 self.searchchoice.SetSelection(self.cfg.ReadInt('/Defaults/SearchType'))
254 self.searchpanel.sizer.Add(self.searchchoice, 0, wx.LEFT | wx.CENTER | wx.EXPAND, self.separatorNormal)
255 self.searchclear = wx.Button(self.searchpanel, wx.ID_CLEAR)
256 self.searchpanel.sizer.Add(self.searchclear, 0, wx.LEFT | wx.CENTER | wx.EXPAND, self.separatorNormal)
257 self.searchpanel.SetSizer(self.searchpanel.sizer)
258 self.rightwin.sizer.Add(self.searchpanel, 0, wx.LEFT | wx.ALL | wx.EXPAND, self.separatorNormal)
260 self.Bind(wx.EVT_CHOICE, self.OnSearch, self.searchchoice)
261 self.Bind(wx.EVT_TEXT, self.OnSearch, self.searchinput)
262 self.Bind(wx.EVT_BUTTON, self.ClearSearch, self.searchclear)
264 # item browser
265 self.browser = Wammu.Browser.Browser(self.rightwin, self, self.cfg)
266 self.rightwin.sizer.Add(self.browser, 1, wx.EXPAND)
267 self.rightwin.SetSizer(self.rightwin.sizer)
269 # values displayer
270 self.content = Wammu.Displayer.Displayer(self.rightsplitter, self)
272 self.splitter.SplitVertically(self.tree, self.rightsplitter, self.cfg.ReadInt('/Main/Split'))
273 self.rightsplitter.SplitHorizontally(self.rightwin, self.content, self.cfg.ReadInt('/Main/SplitRight'))
275 # initial content
276 self.content.SetContent('<font size=+1><b>%s</b></font>' % (_('Welcome to Wammu %s') % Wammu.__version__))
278 # Prepare the menu bar
279 self.menuBar = wx.MenuBar()
281 menu1 = wx.Menu()
282 menu1.Append(100, _('&Write data'), _('Write data (except messages) to file.'))
283 menu1.Append(101, _('W&rite message'), _('Write messages to file.'))
284 menu1.Append(102, _('&Read data'), _('Read data (except messages) from file (does not import to the phone).'))
285 menu1.Append(103, _('R&ead messages'), _('Read messages from file (does not import to the phone).'))
286 menu1.AppendSeparator()
287 menu1.Append(150, _('&Phone wizard'), _('Search for phone or configure it using guided wizard.'))
288 menu1.Append(151, _('Se&ttings'), _('Change Wammu settings.'))
289 menu1.AppendSeparator()
290 menu1.Append(199, '%s\tCtrl+Q' % _('E&xit'), _('Terminate Wammu.'))
291 # Add menu to the menu bar
292 self.menuBar.Append(menu1, _('&Wammu'))
294 menu2 = wx.Menu()
295 menu2.Append(201, _('&Connect'), _('Connect the device.'))
296 menu2.Append(202, _('&Disconnect'), _('Disconnect the device.'))
297 menu2.AppendSeparator()
298 menu2.Append(210, _('&Synchronise time'), _('Synchronise time in phone with PC.'))
299 menu2.AppendSeparator()
300 menu2.Append(250, _('Send &file'), _('Send file to phone.'))
301 # Add menu to the menu bar
302 self.menuBar.Append(menu2, _('&Phone'))
304 menu3 = wx.Menu()
305 menu3.Append(301, '%s\tCtrl+I' % _('&Info'), _('Retrieve phone information.'))
306 menu3.AppendSeparator()
307 menu3.Append(310, _('Contacts (&SIM)'), _('Retrieve contacts from SIM.'))
308 menu3.Append(311, _('Contacts (&phone)'), _('Retrieve contacts from phone memory.'))
309 menu3.Append(312, _('&Contacts (All)'), _('Retrieve contacts from phone and SIM memory.'))
310 menu3.AppendSeparator()
311 menu3.Append(320, _('C&alls'), _('Retrieve call history.'))
312 menu3.AppendSeparator()
313 menu3.Append(330, _('&Messages'), _('Retrieve messages.'))
314 menu3.AppendSeparator()
315 menu3.Append(340, _('&Todos'), _('Retrieve todos.'))
316 menu3.AppendSeparator()
317 menu3.Append(350, _('Calenda&r'), _('Retrieve calendar events.'))
318 # Add menu to the menu bar
319 self.menuBar.Append(menu3, _('&Retrieve'))
321 menu4 = wx.Menu()
322 menu4.Append(401, '%s\tCtrl+N' % _('&Contact'), _('Create new contact.'))
323 menu4.Append(402, '%s\tCtrl+E' % _('Calendar &event'), _('Create new calendar event.'))
324 menu4.Append(403, '%s\tCtrl+T' % _('&Todo'), _('Create new todo.'))
325 menu4.Append(404, '%s\tCtrl+M' % _('&Message'), _('Create new message.'))
326 # Add menu to the menu bar
327 self.menuBar.Append(menu4, _('&Create'))
329 menu5 = wx.Menu()
330 menu5.Append(501, _('&Save'), _('Save currently retrieved data (except messages) to backup.'))
331 menu5.Append(502, _('S&ave messages'), _('Save currently retrieved messages to backup.'))
332 menu5.Append(503, _('&Import to phone'), _('Import data from backup to phone.'))
333 menu5.Append(504, _('I&mport messages to phone'), _('Import messages from backup to phone.'))
334 menu5.AppendSeparator()
335 menu5.Append(510, _('Export messages to &emails'), _('Export messages to emails in storage you choose.'))
336 menu5.Append(511, _('Export messages to &XML'), _('Export messages to XML file you choose.'))
337 # Add menu to the menu bar
338 self.menuBar.Append(menu5, _('&Backups'))
340 menuhelp = wx.Menu()
341 menuhelp.Append(1001, _('&Website'), _('Visit Wammu website.'))
342 menuhelp.Append(1002, _('&Support'), _('Visit Wammu support website.'))
343 menuhelp.Append(1003, _('&Report bug'), _('Report bug in Wammu, please include saved debug log if possible.'))
344 menuhelp.Append(1004, _('&Save debug log'), _('Save a copy of debug log, please include this in bug report.'))
345 menuhelp.AppendSeparator()
346 menuhelp.Append(1010, _('&Gammu Phone Database'), _('Visit database of user experiences with phones.'))
347 menuhelp.Append(1011, _('&Talkback'), _('Report your experiences into Gammu Phone Database.'))
348 menuhelp.AppendSeparator()
349 menuhelp.Append(1020, _('&Donate'), _('Donate to Wammu project.'))
350 menuhelp.AppendSeparator()
351 menuhelp.Append(1100, _('&About'), _('Information about program.'))
352 # Add menu to the menu bar
353 self.menuBar.Append(menuhelp, _('&Help'))
355 # Set menu bar
356 self.SetMenuBar(self.menuBar)
358 # menu events
359 wx.EVT_MENU(self, 100, self.WriteData)
360 wx.EVT_MENU(self, 101, self.WriteSMSData)
361 wx.EVT_MENU(self, 102, self.ReadData)
362 wx.EVT_MENU(self, 103, self.ReadSMSData)
363 wx.EVT_MENU(self, 150, self.SearchPhone)
364 wx.EVT_MENU(self, 151, self.Settings)
365 wx.EVT_MENU(self, 199, self.CloseWindow)
367 wx.EVT_MENU(self, 201, self.PhoneConnect)
368 wx.EVT_MENU(self, 202, self.PhoneDisconnect)
369 wx.EVT_MENU(self, 210, self.SyncTime)
370 wx.EVT_MENU(self, 250, self.SendFile)
372 wx.EVT_MENU(self, 301, self.ShowInfo)
373 wx.EVT_MENU(self, 310, self.ShowContactsSM)
374 wx.EVT_MENU(self, 311, self.ShowContactsME)
375 wx.EVT_MENU(self, 312, self.ShowContacts)
376 wx.EVT_MENU(self, 320, self.ShowCalls)
377 wx.EVT_MENU(self, 330, self.ShowMessages)
378 wx.EVT_MENU(self, 340, self.ShowTodos)
379 wx.EVT_MENU(self, 350, self.ShowCalendar)
381 wx.EVT_MENU(self, 401, self.NewContact)
382 wx.EVT_MENU(self, 402, self.NewCalendar)
383 wx.EVT_MENU(self, 403, self.NewTodo)
384 wx.EVT_MENU(self, 404, self.NewMessage)
386 wx.EVT_MENU(self, 501, self.Backup)
387 wx.EVT_MENU(self, 502, self.BackupSMS)
388 wx.EVT_MENU(self, 503, self.Import)
389 wx.EVT_MENU(self, 504, self.ImportSMS)
390 wx.EVT_MENU(self, 510, self.SMSToMails)
391 wx.EVT_MENU(self, 511, self.SMSToXML)
394 wx.EVT_MENU(self, 1001, self.Website)
395 wx.EVT_MENU(self, 1002, self.Support)
396 wx.EVT_MENU(self, 1003, self.ReportBug)
397 wx.EVT_MENU(self, 1004, self.SaveLog)
398 wx.EVT_MENU(self, 1010, self.PhoneDB)
399 wx.EVT_MENU(self, 1011, self.Talkback)
400 wx.EVT_MENU(self, 1020, self.Donate)
401 wx.EVT_MENU(self, 1100, self.About)
403 self.timer = None
404 self.TogglePhoneMenus(False)
406 self.type = ['info',' ']
408 self.TimerId = wx.NewId()
410 if Wammu.gammu_error == None:
411 # create state machine
412 self.sm = gammu.StateMachine()
414 # create temporary file for logs
415 fd, self.logfilename = tempfile.mkstemp('.log', 'wammu')
417 # set filename to be used for error reports
418 Wammu.ErrorLog.DEBUG_LOG_FILENAME = self.logfilename
420 if sys.platform != 'win32':
421 print ConsoleStrConv(
422 _('Debug log created in temporary file <%s>. In case of crash please include it in bugreport!') % self.logfilename
425 self.logfilefd = os.fdopen(fd, 'w+')
426 # use temporary file for logs
427 gammu.SetDebugFile(self.logfilefd)
428 gammu.SetDebugLevel('textalldate')
430 if Wammu.debug:
431 self.loggerdebug = Wammu.Logger.LoggerDebug(self.logfilename)
432 self.loggerdebug.start()
434 # initialize variables
435 self.showdebug = ''
436 self.IMEI = ''
437 self.Manufacturer = ''
438 self.Model = ''
439 self.Version = ''
440 self.tbicon = None
442 def HandleGammuError(self):
444 Show error about gammu import failure. Try to help user with various
445 situation which could happened, so that he can solve this problem.
447 error = str(Wammu.gammu_error)
448 if error.find('Runtime libGammu version does not match compile time version') != -1:
449 result = re.match('Runtime libGammu version does not match compile time version \(runtime: (\S+), compiletime: (\S+)\)', error)
451 wx.MessageDialog(self,
452 _('Wammu could not import gammu module, program will be terminated.') + '\n\n' +
453 _('The import failed because python-gammu is compiled with different version of Gammu than it is now using (it was compiled with version %(compile)s and now it is using version %(runtime)s).') % {'compile': result.group(2), 'runtime': result.group(1)} + '\n\n' +
454 _('You can fix it by recompiling python-gammu against gammu library you are currently using.'),
455 _('Gammu module not working!'),
456 wx.OK | wx.ICON_ERROR).ShowModal()
457 elif error.find('No module named gammu') != -1:
458 wx.MessageDialog(self,
459 _('Wammu could not import gammu module, program will be terminated.') + '\n\n' +
460 _('Gammu module was not found, you probably don\'t have properly installed python-gammu for current python version.'),
461 _('Gammu module not working!'),
462 wx.OK | wx.ICON_ERROR).ShowModal()
463 else:
464 wx.MessageDialog(self,
465 _('Wammu could not import gammu module, program will be terminated.') + '\n\n' +
466 _('The import failed with following error:') + '\n\n%s' % error,
467 _('Gammu module not working!'),
468 wx.OK | wx.ICON_ERROR).ShowModal()
469 sys.exit()
471 def InitConfiguration(self):
473 Binds Wammu configuration to Gammu one. If at least one section
474 exists, use first one (same as Gammu), otherwise we suggest search to
475 user.
477 gammucfg = self.cfg.gammu.GetConfigs()
478 if len(gammucfg) == 0:
479 dlg = wx.MessageDialog(self,
480 _('Wammu configuration was not found and Gammu settings couldn\'t be read.') + '\n\n' +
481 _('Do you want to configure phone connection now?') + '\n',
482 _('Configuration not found'),
483 wx.YES_NO | wx.YES_DEFAULT | wx.ICON_WARNING)
484 if dlg.ShowModal() == wx.ID_YES:
485 self.SearchPhone()
486 elif not self.cfg.HasEntry('/Gammu/Section'):
487 # behave as Gammu
488 self.cfg.WriteInt('/Gammu/Section', 0)
490 def TalkbackCheck(self):
492 Do ask for talkback after month of usage and at least 30 executions.
494 firstrun = self.cfg.ReadFloat('/Wammu/FirstRun')
495 if firstrun == -1:
496 firstrun = time.time()
497 self.cfg.WriteFloat('/Wammu/FirstRun', firstrun)
498 runs = self.cfg.ReadInt('/Wammu/RunCounter')
499 self.cfg.WriteInt('/Wammu/RunCounter', runs + 1)
500 if self.cfg.Read('/Wammu/TalkbackDone') == 'no':
501 if (firstrun + (3600 * 24 * TALKBACK_DAYS) < time.time()
502 and runs > TALKBACK_COUNT):
503 dlg = wx.MessageDialog(self,
504 _('You are using Wammu for more than a month. We would like to hear from you how your phone is supported. Do you want to participate in this survey?') +
505 '\n\n' + _('Press Cancel to never show this question again.'),
506 _('Thanks for using Wammu'),
507 wx.YES_NO | wx.CANCEL | wx.ICON_INFORMATION)
508 ret = dlg.ShowModal()
509 if ret == wx.ID_YES:
510 self.Talkback()
511 elif ret == wx.ID_CANCEL:
512 self.cfg.Write('/Wammu/TalkbackDone', 'skipped')
514 def MigrateConfiguration(self):
516 Migrate configuration from pre-0.18 style one (Gammu was configured
517 inside Wammu configuration) to using .gammurc.
519 connection = self.cfg.Read('/Gammu/Connection')
520 device = self.cfg.Read('/Gammu/Device')
521 model = self.cfg.Read('/Gammu/Model')
522 gammucfg = self.cfg.gammu.GetConfigs()
523 if len(gammucfg) > 0:
524 for i in gammucfg:
525 cfg = self.cfg.gammu.GetConfig(i['Id'])
526 if cfg['Model'] == model and cfg['Connection'] == connection and cfg['Device'] == device:
527 self.cfg.WriteInt('/Gammu/Section', i['Id'])
528 break
529 if not self.cfg.HasEntry('/Gammu/Section'):
530 index = self.cfg.gammu.FirstFree()
531 self.cfg.gammu.SetConfig(index, device, connection, _('Migrated from older Wammu'), model)
532 self.cfg.WriteInt('/Gammu/Section', index)
534 def PostInit(self, appparent):
536 Do things which need window opened to behave correctly.
538 - Activate initial view.
539 - Show if something wrong has happened on gammu import.
540 - Initialize or migrate Gammu configuration.
541 - Connect to phone if required.
542 - Ask for talkback.
543 - Setup internal information.
545 self.ActivateView('info', ' ')
546 self.appparent = appparent
548 if Wammu.gammu_error != None:
549 self.HandleGammuError()
551 if not self.cfg.HasEntry('/Gammu/Section') and self.cfg.HasEntry('/Gammu/Connection'):
552 self.MigrateConfiguration()
554 self.InitConfiguration()
556 self.DoDebug(self.cfg.Read('/Debug/Show'))
558 if (self.cfg.Read('/Wammu/AutoConnect') == 'yes'):
559 self.PhoneConnect()
561 self.TalkbackCheck()
563 self.SetupNumberPrefix()
565 self.SetupStatusRefresh()
567 self.SetupTrayIcon()
569 self.InitDBUS()
571 def InitDBUS(self):
573 Initializes DBUS handlers if available.
575 self.dbus_notify = None
576 self.last_dbus_id = 0
577 if HAVE_DBUS:
578 try:
579 bus = dbus.SessionBus() #mainloop = self.appparent.MainLoop)
580 interface = 'org.freedesktop.Notifications'
581 path = '/org/freedesktop/Notifications'
582 if Wammu.Utils.DBUSServiceAvailable(bus, interface, True):
583 obj = bus.get_object(interface, path)
584 self.dbus_notify = dbus.Interface(obj, interface)
585 self.dbus_notify.connect_to_signal('ActionInvoked', self.DBUSActionCallback)
586 except dbus.DBusException:
587 self.dbus_notify = None
588 self.last_dbus_id = 0
590 def SetupTrayIcon(self):
591 if self.cfg.Read('/Wammu/TaskBarIcon') != 'yes':
592 if self.tbicon is not None:
593 self.tbicon.Destroy()
594 self.tbicon = None
595 return
596 if self.tbicon is not None:
597 # Nothing to do
598 return
599 self.tbicon = wx.TaskBarIcon()
600 self.tbicon.SetIcon(self.icon16, 'Wammu')
601 self.tbicon.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.OnTaskBarRightClick)
602 self.tbicon.Bind(wx.EVT_TASKBAR_LEFT_UP, self.OnTaskBarLeftClick)
603 self.tbicon.Bind(wx.EVT_MENU, self.Settings, id=151)
604 self.tbicon.Bind(wx.EVT_MENU, self.PhoneConnect, id=201)
605 self.tbicon.Bind(wx.EVT_MENU, self.PhoneDisconnect, id=202)
606 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarRestore, id=100000)
607 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarMinimize, id=100001)
608 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=100002)
610 def OnTaskBarRightClick(self, evt):
611 menutaskbar = wx.Menu()
612 menutaskbar.Append(201, _('Connect'))
613 menutaskbar.Append(202, _('Disconnect'))
614 menutaskbar.AppendSeparator()
615 menutaskbar.Append(151, _('Settings'))
616 menutaskbar.AppendSeparator()
617 menutaskbar.Append(100000, _('Restore'))
618 menutaskbar.Append(100001, _('Minimize'))
619 menutaskbar.AppendSeparator()
620 menutaskbar.Append(100002, _('Close'))
621 self.tbicon.PopupMenu(menutaskbar)
622 menutaskbar.Destroy()
624 def OnTaskBarLeftClick(self, evt):
625 if self.IsShown():
626 self.Show(False)
627 else:
628 self.Show(True)
630 def OnTaskBarRestore(self, evt):
631 self.Show(True)
633 def OnTaskBarMinimize(self, evt):
634 self.Show(False)
636 def OnTaskBarClose(self, evt):
637 self.CloseWindow(evt)
639 def OnTimer(self, evt = None):
640 if self.connected:
641 try:
642 s = self.sm.GetSignalQuality()
643 b = self.sm.GetBatteryCharge()
644 d = self.sm.GetDateTime()
646 # Parse power source
647 power = _('Unknown')
648 if b['ChargeState'] == 'BatteryPowered':
649 power = _('battery')
650 elif b['ChargeState'] == 'BatteryConnected':
651 power = _('AC')
652 elif b['ChargeState'] == 'BatteryNotConnected':
653 power = _('no battery')
654 elif b['ChargeState'] == 'PowerFault':
655 power = _('fault')
656 elif b['ChargeState'] == 'BatteryCharging':
657 power = _('charging')
658 elif b['ChargeState'] == 'BatteryFull':
659 power = _('charged')
661 # Time might be None if it is invalid (eg. 0.0.0000 date)
662 if d is None:
663 time = _('Unknown')
664 else:
665 time = StrConv(d.strftime('%c'))
667 # Detect unknown signal quality
668 if s['SignalPercent'] == -1:
669 signal = _('Unknown')
670 else:
671 # l10n: Formatting of signal percentage, usually you can keep this as it is.
672 signal = _('%d %%') % s['SignalPercent']
674 self.SetStatusText(_('Bat: %(battery_percent)d %% (%(power_source)s), Sig: %(signal_level)s, Time: %(time)s') %
676 'battery_percent':b['BatteryPercent'],
677 'power_source':power,
678 'signal_level':signal,
679 'time': time
680 }, 1)
681 except gammu.GSMError:
682 pass
684 def SetupNumberPrefix(self):
685 self.prefix = self.cfg.Read('/Wammu/PhonePrefix')
686 if self.prefix == 'Auto':
687 if self.connected:
688 self.prefix = None
689 try:
690 on = Wammu.Utils.ParseMemoryEntry(self.sm.GetMemory(Location = 1, Type = 'ON'), self.cfg)['Number']
691 self.prefix = Wammu.Utils.GrabNumberPrefix(on, Wammu.Data.InternationalPrefixes)
692 except gammu.GSMError:
693 pass
694 if self.prefix is None:
695 try:
696 smsc = self.sm.GetSMSC()['Number']
697 self.prefix = Wammu.Utils.GrabNumberPrefix(smsc, Wammu.Data.InternationalPrefixes)
698 except gammu.GSMError:
699 pass
700 if self.prefix is None:
701 self.prefix = self.cfg.Read('/Wammu/LastPhonePrefix')
702 else:
703 self.cfg.Write('/Wammu/LastPhonePrefix', self.prefix)
704 else:
705 self.prefix = self.cfg.Read('/Wammu/LastPhonePrefix')
706 Wammu.Utils.NumberPrefix = self.prefix
708 def SetupStatusRefresh(self):
709 repeat = self.cfg.ReadInt('/Wammu/RefreshState')
710 if repeat == 0:
711 self.timer = None
712 else:
713 self.OnTimer()
714 self.timer = wx.Timer(self, self.TimerId)
715 wx.EVT_TIMER(self, self.TimerId, self.OnTimer)
716 self.timer.Start(repeat)
718 def DoDebug(self, newdebug):
719 if newdebug != self.showdebug:
720 self.showdebug = newdebug
721 if self.showdebug == 'yes':
722 self.logwin = Wammu.Logger.LogFrame(self, self.cfg)
723 self.logwin.Show(True)
724 wx.EVT_CLOSE(self.logwin, self.LogClose)
725 self.logger = Wammu.Logger.Logger(self.logwin, self.logfilename)
726 self.logger.start()
727 else:
728 self.CloseLogWindow()
730 def SaveWinSize(self, win, key):
731 x,y = win.GetPositionTuple()
732 w,h = win.GetSizeTuple()
734 self.cfg.WriteInt('/%s/X' % key, x)
735 self.cfg.WriteInt('/%s/Y' % key, y)
736 self.cfg.WriteInt('/%s/Width' % key, w)
737 self.cfg.WriteInt('/%s/Height' % key, h)
739 def CloseLogWindow(self):
740 if hasattr(self, 'logwin'):
741 self.SaveWinSize(self.logwin, 'Debug')
742 if hasattr(self, 'logger'):
743 self.logger.canceled = True
744 del self.logger
745 if hasattr(self, 'logwin'):
746 self.logwin.Destroy()
747 del self.logwin
750 def LogClose(self, evt = None):
751 self.cfg.Write('/Debug/Show', 'no')
752 self.CloseLogWindow()
754 def TogglePhoneMenus(self, enable):
755 self.connected = enable
756 if enable:
757 self.SetStatusText(_('Connected'), 1)
758 if self.timer != None:
759 self.OnTimer()
760 else:
761 self.SetStatusText(_('Disconnected'), 1)
762 mb = self.menuBar
764 mb.Enable(201, not enable);
765 mb.Enable(202, enable);
767 mb.Enable(210, enable);
769 mb.Enable(250, enable);
771 mb.Enable(301, enable);
773 mb.Enable(310, enable);
774 mb.Enable(311, enable);
775 mb.Enable(312, enable);
777 mb.Enable(320, enable);
779 mb.Enable(330, enable);
781 mb.Enable(340, enable);
783 mb.Enable(350, enable);
785 mb.Enable(401, enable);
786 mb.Enable(402, enable);
787 mb.Enable(403, enable);
788 mb.Enable(404, enable);
790 mb.Enable(501, enable);
791 mb.Enable(502, enable);
792 mb.Enable(503, enable);
793 mb.Enable(504, enable);
795 mb.Enable(510, enable);
796 mb.Enable(511, enable);
798 def ActivateView(self, k1, k2):
799 self.tree.SelectItem(self.treei[k1][k2])
800 self.ChangeView(k1, k2)
802 def ChangeView(self, k1, k2):
803 self.ChangeBrowser(k1, k2)
804 self.righttitle.SetLabel(displaydata[k1][k2][2])
806 def ChangeBrowser(self, k1, k2):
807 self.type = [k1, k2]
808 if k2 == ' ':
809 data = []
810 for k3, v3 in self.values[k1].iteritems():
811 if k3 != '__':
812 data = data + v3
813 self.values[k1]['__'] = data
814 self.browser.Change(k1, data)
815 else:
816 self.browser.Change(k1, self.values[k1][k2])
817 self.browser.ShowRow(0)
819 def OnTreeSel(self, event):
820 item = event.GetItem()
821 for k1, v1 in self.treei.iteritems():
822 for k2, v2 in v1.iteritems():
823 if v2 == item:
824 self.ChangeView(k1, k2)
825 self.ClearSearch()
827 def OnSearch(self, event):
828 text = self.searchinput.GetValue()
829 type = self.searchchoice.GetSelection()
830 try:
831 self.browser.Filter(text, type)
832 self.searchinput.SetBackgroundColour(wx.NullColour)
833 except Wammu.Browser.FilterException:
834 self.searchinput.SetBackgroundColour(wx.RED)
836 def ClearSearch(self, event = None):
837 self.searchinput.SetValue('')
839 def Settings(self, event = None):
840 if self.connected:
841 connection_settings = {
842 'Connection': self.cfg.Read('/Gammu/Connection'),
843 'LockDevice': self.cfg.ReadBool('/Gammu/LockDevice'),
844 'Device': self.cfg.Read('/Gammu/Device'),
845 'Model': self.cfg.Read('/Gammu/Model')
848 result = Wammu.Settings.Settings(self, self.cfg).ShowModal()
849 if result == wx.ID_OK:
850 if self.connected:
851 connection_settings_new = {
852 'Connection': self.cfg.Read('/Gammu/Connection'),
853 'LockDevice': self.cfg.ReadBool('/Gammu/LockDevice'),
854 'Device': self.cfg.Read('/Gammu/Device'),
855 'Model': self.cfg.Read('/Gammu/Model')
858 if connection_settings != connection_settings_new:
859 wx.MessageDialog(self,
860 _('You changed parameters affecting phone connection, they will be used next time you connect to phone.'),
861 _('Notice'),
862 wx.OK | wx.ICON_INFORMATION).ShowModal()
863 self.DoDebug(self.cfg.Read('/Debug/Show'))
864 self.SetupNumberPrefix()
865 self.SetupStatusRefresh()
866 self.SetupTrayIcon()
868 def CloseWindow(self, event):
869 self.SaveWinSize(self, 'Main')
870 if hasattr(self, 'logwin'):
871 self.CloseLogWindow()
872 self.cfg.WriteInt('/Main/Split', self.splitter.GetSashPosition())
873 self.cfg.WriteInt('/Main/SplitRight', self.rightsplitter.GetSashPosition())
874 self.cfg.WriteInt('/Defaults/SearchType', self.searchchoice.GetCurrentSelection())
876 gammu.SetDebugFile(None)
877 gammu.SetDebugLevel('nothing')
879 self.logfilefd.close()
881 if hasattr(self, 'logger'):
882 self.logger.canceled = True
883 self.logger.join()
885 if hasattr(self, 'loggerdebug'):
886 self.loggerdebug.canceled = True
887 self.loggerdebug.join()
889 if self.tbicon is not None:
890 self.tbicon.Destroy()
892 if sys.platform != 'win32':
893 print ConsoleStrConv(
894 _('Looks like normal program termination, deleting log file.')
896 try:
897 os.unlink(self.logfilename)
898 except:
899 print ConsoleStrConv(
900 _('Failed to unlink temporary log file, please delete it yourself.')
902 print ConsoleStrConv(
903 _('Filename: %s') % self.logfilename
906 # Forcibily save configuration
907 self.cfg.Flush()
909 # tell the window to kill itself
910 self.Destroy()
912 def ShowError(self, info):
913 try:
914 gammu_config = self.gammu_config
915 except AttributeError:
916 gammu_config = Noe
917 evt = Wammu.Events.ShowMessageEvent(
918 message = Wammu.Utils.FormatError(_('Error while communicating with phone'), info, gammu_config = gammu_config),
919 title = _('Error Occured'),
920 errortype = 'gammu',
921 type = wx.ICON_ERROR)
922 wx.PostEvent(self, evt)
924 def ShowProgress(self, text):
925 self.progress = wx.ProgressDialog(
926 _('Operation in progress'),
927 text,
928 100,
929 self,
930 wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_ESTIMATED_TIME)
932 def OnProgress(self, evt):
933 if hasattr(self, 'progress'):
934 if not self.progress.Update(evt.progress):
935 try:
936 evt.cancel()
937 except:
938 pass
939 if (evt.progress == 100):
940 del self.progress
941 if hasattr(evt, 'lock'):
942 evt.lock.release()
944 def OnException(self, evt):
945 Wammu.Error.Handler(*evt.data)
947 def OnData(self, evt):
948 self.values[evt.type[0]][evt.type[1]] = evt.data
949 if evt.last:
950 if hasattr(self, 'progress'):
951 self.progress.Update(100)
952 del self.progress
954 if hasattr(self, 'nextfun'):
955 f = self.nextfun
956 a = self.nextarg
957 del self.nextfun
958 del self.nextarg
959 f (*a)
961 def ShowData(self, data):
962 text = u''
963 if data is not None:
964 for d in data:
965 if len(d) == 2:
966 text += u'<b>%s</b>: %s<br>' % (d[0], d[1])
967 else:
968 text += u'<p>%s</p>' % d[0]
969 self.content.SetContent(text)
971 def OnShow(self, evt):
972 data = v = evt.data
973 if data is None:
974 pass
975 elif self.type == ['info',' ']:
976 data = [(evt.data['Name'], evt.data['Value'])]
977 elif self.type[0] == 'contact' or self.type[0] == 'call':
978 data = [
979 (_('Location'), str(v['Location'])),
980 (_('Memory type'), v['MemoryType'])]
981 for i in v['Entries']:
982 s = Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values, linkphone = False)
983 try:
984 if i['VoiceTag']:
985 s += ', ' + (_('voice tag %x') % i['VoiceTag'])
986 except:
987 pass
988 data.append((i['Type'], s))
989 elif self.type[0] == 'message':
990 data = [
991 (_('Number'), Wammu.Utils.GetNumberLink([] + self.values['contact']['ME'] + self.values['contact']['SM'], v['Number'])),
992 (_('Date'), StrConv(v['DateTime'])),
993 (_('Location'), StrConv(v['Location'])),
994 (_('Folder'), StrConv(v['SMS'][0]['Folder'])),
995 (_('Memory'), StrConv(v['SMS'][0]['Memory'])),
996 (_('SMSC'), Wammu.Utils.GetNumberLink([] + self.values['contact']['ME'] + self.values['contact']['SM'], v['SMS'][0]['SMSC']['Number'])),
997 (_('Status'), StrConv(v['State']))]
998 if v['Name'] != '':
999 data.append((_('Name'), StrConv(v['Name'])))
1000 data.append((Wammu.MessageDisplay.SmsToHtml(self.cfg, v),))
1001 elif self.type[0] == 'todo':
1002 data = [
1003 (_('Location'), str(v['Location'])),
1004 (_('Priority'), v['Priority']),
1005 (_('Type'), v['Type']),
1007 for i in v['Entries']:
1008 data.append((i['Type'], Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values)))
1009 elif self.type[0] == 'calendar':
1010 data = [
1011 (_('Location'), str(v['Location'])),
1012 (_('Type'), v['Type']),
1014 for i in v['Entries']:
1015 data.append((i['Type'], Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values)))
1016 else:
1017 data = [('Show not yet implemented! (type = %s)' % self.type[0])]
1018 self.ShowData(data)
1020 def NewContact(self, evt):
1021 self.EditContact({})
1023 def NewCalendar(self, evt):
1024 self.EditCalendar({})
1026 def NewTodo(self, evt):
1027 self.EditTodo({})
1029 def NewMessage(self, evt):
1030 self.ComposeMessage({})
1032 def ComposeMessage(self, v, action = 'save'):
1033 if Wammu.Composer.SMSComposer(self, self.cfg, v, self.values, action).ShowModal() == wx.ID_OK:
1035 if len(v['Numbers']) == 0:
1036 v['Numbers'] = ['Wammu']
1038 for number in v['Numbers']:
1039 busy = wx.BusyInfo(_('Writing message(s)...'))
1040 time.sleep(0.1)
1041 wx.Yield()
1042 v['Number'] = number
1043 v['SMS'] = gammu.EncodeSMS(v['SMSInfo'])
1045 if v['Save']:
1046 result = {}
1047 result['SMS'] = []
1049 try:
1050 for msg in v['SMS']:
1051 msg['SMSC']['Location'] = 1
1053 msg['Folder'] = v['Folder']
1054 msg['Number'] = v['Number']
1055 msg['Type'] = v['Type']
1056 msg['State'] = v['State']
1058 if v['Save']:
1059 (msg['Location'], msg['Folder']) = self.sm.AddSMS(msg)
1060 if v['Send']:
1061 # When sending of saved message fails, send it directly:
1062 try:
1063 msg['MessageReference'] = self.sm.SendSavedSMS(0, msg['Location'])
1064 except gammu.GSMError:
1065 msg['MessageReference'] = self.sm.SendSMS(msg)
1066 try:
1067 result['SMS'].append(self.sm.GetSMS(0, msg['Location'])[0])
1068 except gammu.ERR_EMPTY:
1069 wx.MessageDialog(self, _('It was not possible to read saved message! There is most likely some bug in Gammu, please contact author with debug log of this operation. To see message in Wammu you need to reread all messsages.'), _('Could not read saved message!'), wx.OK | wx.ICON_ERROR).ShowModal()
1070 elif v['Send']:
1071 msg['MessageReference'] = self.sm.SendSMS(msg)
1073 if v['Save']:
1074 info = gammu.DecodeSMS(result['SMS'])
1075 if info != None:
1076 result['SMSInfo'] = info
1077 Wammu.Utils.ParseMessage(result, (info != None))
1078 result['Synced'] = True
1079 self.values['message'][result['State']].append(result)
1081 except gammu.GSMError, val:
1082 del busy
1083 self.ShowError(val[0])
1085 if v['Save']:
1086 try:
1087 self.ActivateView('message', result['State'])
1088 self.browser.ShowLocation(result['Location'])
1089 except KeyError:
1090 pass
1092 def EditContact(self, v):
1093 backup = copy.deepcopy(v)
1094 shoulddelete = (v == {} or v['Location'] == 0)
1095 if Wammu.Editor.ContactEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1096 try:
1097 busy = wx.BusyInfo(_('Writing contact...'))
1098 time.sleep(0.1)
1099 wx.Yield()
1100 # was entry moved => delete it from internal list
1101 if not shoulddelete:
1102 for idx in range(len(self.values['contact'][backup['MemoryType']])):
1103 if self.values['contact'][backup['MemoryType']][idx] == v:
1104 del self.values['contact'][backup['MemoryType']][idx]
1105 break
1107 # have we specified location? => add or set
1108 if v['Location'] == 0:
1109 v['Location'] = self.sm.AddMemory(v)
1110 else:
1111 try:
1112 v['Location'] = self.sm.SetMemory(v)
1113 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1114 v['Location'] = self.sm.AddMemory(v)
1116 # was entry moved => delete it from phone
1117 if not shoulddelete:
1118 if v['MemoryType'] != backup['MemoryType'] or v['Location'] != backup['Location']:
1119 # delete from phone
1120 self.sm.DeleteMemory(backup['MemoryType'], backup['Location'])
1122 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1123 try:
1124 attempts = 0
1125 while attempts < 10:
1126 try:
1127 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1128 break
1129 except gammu.ERR_EMPTY:
1130 # some phones need time till entry appears
1131 attempts = attempts + 1
1132 time.sleep(0.2)
1133 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1134 wx.MessageDialog(self, _('It was not possible to read saved entry! It might be different than one saved in phone untill you reread all entries.'), _('Could not read saved entry!'), wx.OK | wx.ICON_WARNING).ShowModal()
1135 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1136 v['Synced'] = True
1137 # append new value to list
1138 self.values['contact'][v['MemoryType']].append(v)
1140 except gammu.GSMError, val:
1141 del busy
1142 v = backup
1143 self.ShowError(val[0])
1145 if (self.type[0] == 'contact' and self.type[1] == ' ') or not v.has_key('MemoryType'):
1146 self.ActivateView('contact', ' ')
1147 try:
1148 self.browser.ShowLocation(v['Location'], ('MemoryType', v['MemoryType']))
1149 except KeyError:
1150 pass
1151 else:
1152 self.ActivateView('contact', v['MemoryType'])
1153 try:
1154 self.browser.ShowLocation(v['Location'])
1155 except KeyError:
1156 pass
1158 def EditCalendar(self, v):
1159 backup = copy.deepcopy(v)
1160 shoulddelete = (v == {} or v['Location'] == 0)
1161 if Wammu.Editor.CalendarEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1162 try:
1163 busy = wx.BusyInfo(_('Writing calendar...'))
1164 time.sleep(0.1)
1165 wx.Yield()
1166 # was entry moved => delete it from internal list
1167 if not shoulddelete:
1168 # delete from internal list
1169 for idx in range(len(self.values['calendar'][' '])):
1170 if self.values['calendar'][' '][idx] == v:
1171 del self.values['calendar'][' '][idx]
1172 break
1174 # have we specified location? => add or set
1175 if v['Location'] == 0:
1176 v['Location'] = self.sm.AddCalendar(v)
1177 else:
1178 try:
1179 v['Location'] = self.sm.SetCalendar(v)
1180 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1181 v['Location'] = self.sm.AddCalendar(v)
1183 # was entry moved => delete it from phone
1184 if not shoulddelete:
1185 if v['Location'] != backup['Location']:
1186 # delete from phone
1187 self.sm.DeleteCalendar(backup['Location'])
1189 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1190 try:
1191 v = self.sm.GetCalendar(v['Location'])
1192 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1193 wx.MessageDialog(self, _('It was not possible to read saved entry! It might be different than one saved in phone untill you reread all entries.'), _('Could not read saved entry!'), wx.OK | wx.ICON_WARNING).ShowModal()
1194 Wammu.Utils.ParseCalendar(v)
1195 v['Synced'] = True
1196 # append new value to list
1197 self.values['calendar'][' '].append(v)
1199 except gammu.GSMError, val:
1200 del busy
1201 v = backup
1202 self.ShowError(val[0])
1204 self.ActivateView('calendar', ' ')
1205 try:
1206 self.browser.ShowLocation(v['Location'])
1207 except KeyError:
1208 pass
1210 def EditTodo(self, v):
1211 backup = copy.deepcopy(v)
1212 shoulddelete = (v == {} or v['Location'] == 0)
1213 if Wammu.Editor.TodoEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1214 try:
1215 busy = wx.BusyInfo(_('Writing todo...'))
1216 time.sleep(0.1)
1217 wx.Yield()
1218 # was entry moved => delete it from internal list
1219 if not shoulddelete:
1220 for idx in range(len(self.values['todo'][' '])):
1221 if self.values['todo'][' '][idx] == v:
1222 del self.values['todo'][' '][idx]
1223 break
1225 # have we specified location? => add or set
1226 if v['Location'] == 0:
1227 v['Location'] = self.sm.AddToDo(v)
1228 else:
1229 try:
1230 v['Location'] = self.sm.SetToDo(v)
1231 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1232 v['Location'] = self.sm.AddToDo(v)
1234 # was entry moved => delete it from phone
1235 if not shoulddelete:
1236 if v['Location'] != backup['Location']:
1237 # delete from phone
1238 self.sm.DeleteToDo(backup['Location'])
1240 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1241 try:
1242 v = self.sm.GetToDo(v['Location'])
1243 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1244 wx.MessageDialog(self, _('It was not possible to read saved entry! It might be different than one saved in phone untill you reread all entries.'), _('Could not read saved entry!'), wx.OK | wx.ICON_WARNING).ShowModal()
1245 Wammu.Utils.ParseTodo(v)
1246 v['Synced'] = True
1247 # append new value to list
1248 self.values['todo'][' '].append(v)
1249 except gammu.GSMError, val:
1250 del busy
1251 v = backup
1252 self.ShowError(val[0])
1254 self.ActivateView('todo', ' ')
1255 try:
1256 self.browser.ShowLocation(v['Location'])
1257 except KeyError:
1258 pass
1261 def OnEdit(self, evt):
1262 if evt.data != {} and not evt.data['Synced']:
1263 wx.MessageDialog(self, _('You can not work on this data, please retrieve it first from phone'), _('Data not up to date'), wx.OK | wx.ICON_ERROR).ShowModal()
1264 return
1265 if self.type[0] == 'contact':
1266 self.EditContact(evt.data)
1267 elif self.type[0] == 'call' and evt.data['Location'] == 0:
1268 self.EditContact(evt.data)
1269 elif self.type[0] == 'calendar':
1270 self.EditCalendar(evt.data)
1271 elif self.type[0] == 'todo':
1272 self.EditTodo(evt.data)
1273 else:
1274 print 'Edit not yet implemented (type = %s)!' % self.type[0]
1276 def OnReply(self, evt):
1277 if self.type[0] == 'message':
1278 self.ComposeMessage({'Number': evt.data['Number']}, action = 'send')
1279 else:
1280 print 'Reply not yet implemented!'
1281 print evt.index
1283 def OnCall(self, evt):
1284 if self.type[0] in ['call', 'contact']:
1285 num = Wammu.Select.SelectContactNumber(self, evt.data)
1286 if num == None:
1287 return
1289 try:
1290 self.sm.DialVoice(num)
1291 except gammu.GSMError, val:
1292 self.ShowError(val[0])
1293 elif self.type[0] == 'message':
1294 try:
1295 self.sm.DialVoice(evt.data['Number'])
1296 except gammu.GSMError, val:
1297 self.ShowError(val[0])
1298 else:
1299 print 'Call not yet implemented (type = %s)!' % self.type[0]
1301 def OnMessage(self, evt):
1302 if self.type[0] in ['call', 'contact']:
1304 num = Wammu.Select.SelectContactNumber(self, evt.data)
1305 if num == None:
1306 return
1307 self.ComposeMessage({'Number': num}, action = 'send')
1308 elif self.type[0] == 'message':
1309 self.ComposeMessage({'Number': evt.data['Number']}, action = 'send')
1310 else:
1311 print 'Message send not yet implemented (type = %s)!' % self.type[0]
1313 def OnDuplicate(self, evt):
1314 if evt.data != {} and not evt.data['Synced']:
1315 wx.MessageDialog(self, _('You can not work on this data, please retrieve it first from phone'), _('Data not up to date'), wx.OK | wx.ICON_ERROR).ShowModal()
1316 return
1317 v = copy.deepcopy(evt.data)
1318 if self.type[0] == 'contact':
1319 v['Location'] = 0
1320 self.EditContact(v)
1321 elif self.type[0] == 'calendar':
1322 v['Location'] = 0
1323 self.EditCalendar(v)
1324 elif self.type[0] == 'todo':
1325 v['Location'] = 0
1326 self.EditTodo(v)
1327 elif self.type[0] == 'message':
1328 self.ComposeMessage(v)
1329 else:
1330 print 'Duplicate not yet implemented (type = %s)!' % self.type[0]
1333 def OnSend(self, evt):
1334 if evt.data != {} and not evt.data['Synced']:
1335 wx.MessageDialog(self, _('You can not work on this data, please retrieve it first from phone'), _('Data not up to date'), wx.OK | wx.ICON_ERROR).ShowModal()
1336 return
1337 if self.type[0] == 'message':
1338 v = evt.data
1339 try:
1340 try:
1341 for loc in v['Location'].split(', '):
1342 self.sm.SendSavedSMS(0, int(loc))
1343 except gammu.ERR_NOTSUPPORTED:
1344 for msg in v['SMS']:
1345 self.sm.SendSMS(msg)
1346 except gammu.GSMError, val:
1347 self.ShowError(val[0])
1349 def SMSToMails(self, evt):
1350 messages = self.values['message']['Read'] + \
1351 self.values['message']['UnRead'] + \
1352 self.values['message']['Sent'] + \
1353 self.values['message']['UnSent']
1354 contacts = self.values['contact']['ME'] + \
1355 self.values['contact']['SM']
1356 Wammu.SMSExport.SMSExport(self, messages, contacts)
1358 def SMSToXML(self, evt):
1359 messages = self.values['message']['Read'] + \
1360 self.values['message']['UnRead'] + \
1361 self.values['message']['Sent'] + \
1362 self.values['message']['UnSent']
1363 contacts = self.values['contact']['ME'] + \
1364 self.values['contact']['SM']
1365 Wammu.SMSXML.SMSExportXML(self, messages, contacts)
1367 def SelectBackupFile(self, type, save = True, data = False):
1368 wildcard = ''
1369 if type == 'message':
1370 wildcard += _('Gammu messages backup') + ' (*.smsbackup)|*.smsbackup|'
1371 exts = ['smsbackup']
1372 else:
1373 if not save:
1374 wildcard += _('All backup formats') + '|*.backup;*.lmb;*.vcf;*.ldif;*.vcs;*.ics|'
1376 wildcard += _('Gammu backup [all data]') + ' (*.backup)|*.backup|'
1377 exts = ['backup']
1379 if type in ['contact', 'all']:
1380 wildcard += _('Nokia backup [contacts]') + ' (*.lmb)|*.lmb|'
1381 exts.append('lmb')
1382 if type in ['contact', 'all']:
1383 wildcard += _('vCard [contacts]') + ' (*.vcf)|*.vcf|'
1384 exts.append('vcf')
1385 if type in ['contact', 'all']:
1386 wildcard += _('LDIF [contacts]') + ' (*.ldif)|*.ldif|'
1387 exts.append('ldif')
1388 if type in ['todo', 'calendar', 'all']:
1389 wildcard += _('vCalendar [todo,calendar]') + ' (*.vcs)|*.vcs|'
1390 exts.append('vcs')
1391 if type in ['todo', 'calendar', 'all']:
1392 wildcard += _('iCalendar [todo,calendar]') + ' (*.ics)|*.ics|'
1393 exts.append('ics')
1395 wildcard += _('All files') + ' (*.*)|*.*'
1396 exts.append(None)
1398 if data:
1399 if save:
1400 dlg = wx.FileDialog(self, _('Save data as...'), os.getcwd(), "", wildcard, wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
1401 else:
1402 dlg = wx.FileDialog(self, _('Read data'), os.getcwd(), "", wildcard, wx.OPEN|wx.CHANGE_DIR)
1403 else:
1404 if save:
1405 dlg = wx.FileDialog(self, _('Save backup as...'), os.getcwd(), "", wildcard, wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
1406 else:
1407 dlg = wx.FileDialog(self, _('Import backup'), os.getcwd(), "", wildcard, wx.OPEN|wx.CHANGE_DIR)
1408 if dlg.ShowModal() == wx.ID_OK:
1409 path = dlg.GetPath()
1410 if save:
1411 ext = exts[dlg.GetFilterIndex()]
1412 # Add automatic extension if we know one and file does not
1413 # have any
1414 if (os.path.splitext(path)[1] == '' and
1415 ext is not None):
1416 path += '.' + ext
1417 return Wammu.Locales.ConsoleStrConv(path)
1418 return None
1420 def ReadBackup(self, type, data = False):
1421 filename = self.SelectBackupFile(type, save = False, data = data)
1422 if filename == None:
1423 return (None, None)
1424 try:
1425 if type == 'message':
1426 backup = gammu.ReadSMSBackup(filename)
1427 else:
1428 backup = gammu.ReadBackup(filename)
1429 except gammu.GSMError, val:
1430 info = val[0]
1431 evt = Wammu.Events.ShowMessageEvent(
1432 message = Wammu.Utils.FormatError(_('Error while reading backup'), info),
1433 title = _('Error Occured'),
1434 errortype = 'gammu',
1435 type = wx.ICON_ERROR)
1436 wx.PostEvent(self, evt)
1437 return (None, None)
1438 return (filename, backup)
1440 def ReadData(self, evt):
1441 (filename, backup) = self.ReadBackup('all', True)
1442 if backup == None:
1443 return
1445 if len(backup['PhonePhonebook']) > 0:
1446 self.values['contact']['ME'] = map(Wammu.Utils.ParseMemoryEntry, backup['PhonePhonebook'], [self.cfg] * len(backup['PhonePhonebook']))
1447 if len(backup['SIMPhonebook']) > 0:
1448 self.values['contact']['SM'] = map(Wammu.Utils.ParseMemoryEntry, backup['SIMPhonebook'], [self.cfg] * len(backup['SIMPhonebook']))
1449 if len(backup['ToDo']) > 0:
1450 self.values['todo'][' '] = map(Wammu.Utils.ParseTodo, backup['ToDo'])
1451 if len(backup['Calendar']) > 0:
1452 self.values['calendar'][' '] = map(Wammu.Utils.ParseCalendar, backup['Calendar'])
1454 self.ActivateView('contact', ' ')
1456 self.SetStatusText(_('Data has been read from file "%s"') % StrConv(filename))
1458 def ReadSMSData(self, evt):
1459 (filename, backup) = self.ReadBackup('message', True)
1460 if backup == None:
1461 return
1463 res = Wammu.Utils.ProcessMessages(map(lambda x:[x], backup), False)
1465 self.values['message']['Sent'] = res['sent']
1466 self.values['message']['UnSent'] = res['unsent']
1467 self.values['message']['Read'] = res['read']
1468 self.values['message']['UnRead'] = res['unread']
1470 self.ActivateView('message', ' ')
1472 self.SetStatusText(_('Data has been read from file "%s"') % StrConv(filename))
1474 def ImportSMS(self, evt):
1475 (filename, backup) = self.ReadBackup('message')
1476 if backup == None:
1477 return
1478 choices = []
1479 values = []
1480 if len(backup) > 0:
1481 values.append('message')
1482 choices.append(_('%d messages') % len(backup))
1484 if len(values) == 0:
1485 wx.MessageDialog(self,
1486 _('No importable data were found in file "%s"') % strconv(filename),
1487 _('No data to import'),
1488 wx.OK | wx.ICON_INFORMATION).ShowModal()
1489 return
1491 dlg = wx.lib.dialogs.MultipleChoiceDialog(self, _('Following data was found in backup, select which of these do you want to be added into phone.'), _('Select what to import'),
1492 choices,style = wx.CHOICEDLG_STYLE | wx.RESIZE_BORDER,
1493 size = (600, 200))
1494 if dlg.ShowModal() != wx.ID_OK:
1495 return
1497 lst = dlg.GetValue()
1498 if len(lst) == 0:
1499 return
1501 try:
1502 busy = wx.BusyInfo(_('Importing data...'))
1503 time.sleep(0.1)
1504 wx.Yield()
1505 for i in lst:
1506 datatype = values[i]
1507 if datatype == 'message':
1508 smsl = []
1509 for v in backup:
1510 v['SMSC']['Location'] = 1
1511 (v['Location'], v['Folder']) = self.sm.AddSMS(v)
1512 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1513 v = self.sm.GetSMS(0, v['Location'])
1514 smsl.append(v)
1516 res = Wammu.Utils.ProcessMessages(smsl, True)
1518 self.values['message']['Sent'] += res['sent']
1519 self.values['message']['UnSent'] += res['unsent']
1520 self.values['message']['Read'] += res['read']
1521 self.values['message']['UnRead'] += res['unread']
1523 self.ActivateView('message', ' ')
1525 del busy
1526 wx.Yield()
1528 wx.MessageDialog(self,
1529 _('Backup has been imported from file "%s"') % StrConv(filename),
1530 _('Backup imported'),
1531 wx.OK | wx.ICON_INFORMATION).ShowModal()
1533 except gammu.GSMError, val:
1534 self.ShowError(val[0])
1536 wx.MessageDialog(self,
1537 _('Restoring from file "%s" has failed, some parts of backup might have been stored to phone and some were not.') % StrConv(filename),
1538 _('Backup import failed'),
1539 wx.OK | wx.ICON_INFORMATION).ShowModal()
1541 def Import(self, evt):
1542 (filename, backup) = self.ReadBackup('all')
1543 if backup == None:
1544 return
1545 choices = []
1546 values = []
1547 if len(backup['PhonePhonebook']) > 0:
1548 values.append('PhonePhonebook')
1549 choices.append(_('%d phone contact entries') % len(backup['PhonePhonebook']))
1550 if len(backup['SIMPhonebook']) > 0:
1551 values.append('SIMPhonebook')
1552 choices.append(_('%d SIM contact entries') % len(backup['SIMPhonebook']))
1553 if len(backup['ToDo']) > 0:
1554 values.append('ToDo')
1555 choices.append(_('%d to do entries') % len(backup['ToDo']))
1556 if len(backup['Calendar']) > 0:
1557 values.append('Calendar')
1558 choices.append(_('%d calendar entries') % len(backup['Calendar']))
1560 if len(values) == 0:
1561 wx.MessageDialog(self,
1562 _('No importable data were found in file "%s"') % StrConv(filename),
1563 _('No data to import'),
1564 wx.OK | wx.ICON_INFORMATION).ShowModal()
1565 return
1567 msg = ''
1568 if backup['Model'] != '':
1569 msg = '\n \n' + _('Backup saved from phone %s') % backup['Model']
1570 if backup['IMEI'] != '':
1571 msg += _(', serial number %s') % backup['IMEI']
1572 if backup['Creator'] != '':
1573 msg += '\n \n' + _('Backup was created by %s') % backup['Creator']
1574 if backup['DateTime'] != None:
1575 msg += '\n \n' + _('Backup saved on %s') % str(backup['DateTime'])
1577 dlg = wx.lib.dialogs.MultipleChoiceDialog(self, _('Following data was found in backup, select which of these do you want to be added into phone.') + msg, _('Select what to import'),
1578 choices,style = wx.CHOICEDLG_STYLE | wx.RESIZE_BORDER,
1579 size = (600, 200))
1580 if dlg.ShowModal() != wx.ID_OK:
1581 return
1583 lst = dlg.GetValue()
1584 if len(lst) == 0:
1585 return
1587 try:
1588 busy = wx.BusyInfo(_('Importing data...'))
1589 time.sleep(0.1)
1590 wx.Yield()
1591 for i in lst:
1592 datatype = values[i]
1593 if datatype == 'PhonePhonebook':
1594 for v in backup['PhonePhonebook']:
1595 v['Location'] = self.sm.AddMemory(v)
1596 time.sleep(0.5)
1597 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1598 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1599 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1600 v['Synced'] = True
1601 # append new value to list
1602 self.values['contact'][v['MemoryType']].append(v)
1603 self.ActivateView('contact', 'ME')
1604 elif datatype == 'SIMPhonebook':
1605 for v in backup['SIMPhonebook']:
1606 v['Location'] = self.sm.AddMemory(v)
1607 time.sleep(0.5)
1608 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1609 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1610 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1611 v['Synced'] = True
1612 # append new value to list
1613 self.values['contact'][v['MemoryType']].append(v)
1614 self.ActivateView('contact', 'SM')
1615 elif datatype == 'ToDo':
1616 for v in backup['ToDo']:
1617 v['Location'] = self.sm.AddToDo(v)
1618 time.sleep(0.5)
1619 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1620 v = self.sm.GetToDo(v['Location'])
1621 Wammu.Utils.ParseTodo(v)
1622 v['Synced'] = True
1623 # append new value to list
1624 self.values['todo'][' '].append(v)
1625 self.ActivateView('todo', ' ')
1626 elif datatype == 'Calendar':
1627 for v in backup['Calendar']:
1628 v['Location'] = self.sm.AddCalendar(v)
1629 time.sleep(0.5)
1630 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1631 v = self.sm.GetCalendar(v['Location'])
1632 Wammu.Utils.ParseCalendar(v)
1633 v['Synced'] = True
1634 # append new value to list
1635 self.values['calendar'][' '].append(v)
1636 self.ActivateView('calendar', ' ')
1638 del busy
1639 wx.Yield()
1641 wx.MessageDialog(self,
1642 _('Backup has been imported from file "%s"') % StrConv(filename),
1643 _('Backup imported'),
1644 wx.OK | wx.ICON_INFORMATION).ShowModal()
1646 except gammu.GSMError, val:
1647 self.ShowError(val[0])
1649 wx.MessageDialog(self,
1650 _('Restoring from file "%s" has failed, some parts of backup might have been stored to phone and some were not.') % StrConv(filename),
1651 _('Backup import failed'),
1652 wx.OK | wx.ICON_INFORMATION).ShowModal()
1654 def WriteData(self, evt):
1655 self.DoBackup(True, 'all')
1657 def WriteSMSData(self, evt):
1658 self.DoBackup(True, 'message')
1660 def Backup(self, evt):
1661 self.DoBackup(False, 'all')
1663 def BackupSMS(self, evt):
1664 self.DoBackup(False, 'message')
1666 def PrepareBackup(self):
1667 backup = {}
1668 backup['Creator'] = 'Wammu ' + Wammu.__version__
1669 backup['IMEI'] = self.IMEI
1670 backup['Model'] = '%s %s %s' % ( self.Manufacturer, self.Model, self.Version)
1671 return backup
1673 def WriteBackup(self, filename, type, backup, data = False):
1674 try:
1675 if type == 'message':
1676 # Backup is here our internal SMS list: [{'SMS':[{sms1}, {sms2}]}, ...]
1677 data = map(lambda x:x['SMS'], backup)
1678 backup = []
1679 for x in data:
1680 backup += x
1681 gammu.SaveSMSBackup(filename, backup)
1682 else:
1683 gammu.SaveBackup(filename, backup)
1684 if data:
1685 self.SetStatusText(_('Backup has been saved to file "%s"') % StrConv(filename))
1686 else:
1687 self.SetStatusText(_('Data has been saved to file "%s"') % StrConv(filename))
1688 except gammu.GSMError, val:
1689 info = val[0]
1690 evt = Wammu.Events.ShowMessageEvent(
1691 message = Wammu.Utils.FormatError(_('Error while saving backup'), info),
1692 title = _('Error Occured'),
1693 errortype = 'gammu',
1694 type = wx.ICON_ERROR)
1695 wx.PostEvent(self, evt)
1696 except MemoryError, val:
1697 info = val[0]
1698 evt = Wammu.Events.ShowMessageEvent(
1699 message = _('Error while saving backup, probably some limit inside of Gammu exceeded.\n%s') % str(info),
1700 title = _('Error Occured'),
1701 type = wx.ICON_ERROR)
1702 wx.PostEvent(self, evt)
1704 def DoBackup(self, data, type):
1705 filename = self.SelectBackupFile(type, data = data)
1706 if filename == None:
1707 return
1708 ext = os.path.splitext(filename)[1].lower()
1710 if type == 'message':
1711 backup = self.values['message']['Read'] + self.values['message']['UnRead'] + self.values['message']['Sent'] + self.values['message']['UnSent']
1712 else:
1713 backup = self.PrepareBackup()
1714 if ext in ['.vcf', '.ldif']:
1715 # these support only one phonebook, so merged it
1716 backup['PhonePhonebook'] = self.values['contact']['ME'] + self.values['contact']['SM']
1717 else:
1718 backup['PhonePhonebook'] = self.values['contact']['ME']
1719 backup['SIMPhonebook'] = self.values['contact']['SM']
1721 backup['ToDo'] = self.values['todo'][' ']
1722 backup['Calendar'] = self.values['calendar'][' ']
1723 self.WriteBackup(filename, type, backup, data)
1726 def OnBackup(self, evt):
1727 filename = self.SelectBackupFile(self.type[0])
1728 if filename == None:
1729 return
1730 ext = os.path.splitext(filename)[1].lower()
1731 lst = evt.lst
1732 if self.type[0] == 'message':
1733 backup = lst
1734 else:
1735 backup = self.PrepareBackup()
1736 if self.type[0] == 'contact':
1737 if ext in ['.vcf', '.ldif']:
1738 # these support only one phonebook, so keep it merged
1739 backup['PhonePhonebook'] = lst
1740 else:
1741 sim = []
1742 phone = []
1743 for item in lst:
1744 if item['MemoryType'] == 'SM':
1745 sim.append(item)
1746 elif item['MemoryType'] == 'ME':
1747 phone.append(item)
1748 backup['PhonePhonebook'] = phone
1749 backup['SIMPhonebook'] = sim
1750 elif self.type[0] == 'todo':
1751 backup['ToDo'] = lst
1752 elif self.type[0] == 'calendar':
1753 backup['Calendar'] = lst
1755 self.WriteBackup(filename, self.type[0], backup)
1757 def OnDelete(self, evt):
1758 # first check on supported types
1759 if not self.type[0] in ['contact', 'call', 'message', 'todo', 'calendar']:
1760 print 'Delete not yet implemented! (items to delete = %s, type = %s)' % (str(evt.lst), self.type[0])
1761 return
1763 lst = evt.lst
1765 if len(lst) == 0:
1766 # nothing to delete
1767 return
1769 if not lst[0]['Synced']:
1770 wx.MessageDialog(self, _('You can not work on this data, please retrieve it first from phone'), _('Data not up to date'), wx.OK | wx.ICON_ERROR).ShowModal()
1771 return
1773 # check for confirmation
1774 if self.cfg.Read('/Wammu/ConfirmDelete') == 'yes':
1775 count = len(lst)
1776 if count == 1:
1777 v = lst[0]
1778 if self.type[0] == 'contact':
1779 txt = _('Are you sure you want to delete contact "%s"?') % v['Name']
1780 elif self.type[0] == 'call':
1781 txt = _('Are you sure you want to delete call from "%s"?') % v['Name']
1782 elif self.type[0] == 'message':
1783 txt = _('Are you sure you want to delete message from "%s"?') % v['Number']
1784 elif self.type[0] == 'todo':
1785 txt = _('Are you sure you want to delete todo entry "%s"?') % v['Text']
1786 elif self.type[0] == 'calendar':
1787 txt = _('Are you sure you want to delete calendar entry "%s"?') % v['Text']
1788 else:
1789 if self.type[0] == 'contact':
1790 txt = Wammu.Locales.ngettext(
1791 'Are you sure you want to delete %d contact?',
1792 'Are you sure you want to delete %d contacts?',
1793 count) % count
1794 elif self.type[0] == 'call':
1795 txt = Wammu.Locales.ngettext(
1796 'Are you sure you want to delete %d call?',
1797 'Are you sure you want to delete %d calls?',
1798 count) % count
1799 elif self.type[0] == 'message':
1800 txt = Wammu.Locales.ngettext(
1801 'Are you sure you want to delete %d message?',
1802 'Are you sure you want to delete %d messages?',
1803 count) % count
1804 elif self.type[0] == 'todo':
1805 txt = Wammu.Locales.ngettext(
1806 'Are you sure you want to delete %d todo entry?',
1807 'Are you sure you want to delete %d todo entries?',
1808 count) % count
1809 elif self.type[0] == 'calendar':
1810 txt = Wammu.Locales.ngettext(
1811 'Are you sure you want to delete %d calendar entry?',
1812 'Are you sure you want to delete %d calendar entries?',
1813 count) % count
1814 dlg = wx.MessageDialog(self,
1815 txt,
1816 _('Confirm deleting'),
1817 wx.OK | wx.CANCEL | wx.ICON_WARNING)
1818 if dlg.ShowModal() != wx.ID_OK:
1819 return
1821 # do real delete
1822 try:
1823 if self.type[0] == 'contact' or self.type[0] == 'call':
1824 busy = wx.BusyInfo(_('Deleting contact(s)...'))
1825 time.sleep(0.1)
1826 wx.Yield()
1827 for v in lst:
1828 self.sm.DeleteMemory(v['MemoryType'], v['Location'])
1829 for idx in range(len(self.values[self.type[0]][v['MemoryType']])):
1830 if self.values[self.type[0]][v['MemoryType']][idx] == v:
1831 del self.values[self.type[0]][v['MemoryType']][idx]
1832 break
1833 elif self.type[0] == 'message':
1834 busy = wx.BusyInfo(_('Deleting message(s)...'))
1835 time.sleep(0.1)
1836 wx.Yield()
1837 for v in lst:
1838 for loc in v['Location'].split(', '):
1839 self.sm.DeleteSMS(0, int(loc))
1840 for idx in range(len(self.values[self.type[0]][v['State']])):
1841 if self.values[self.type[0]][v['State']][idx] == v:
1842 del self.values[self.type[0]][v['State']][idx]
1843 break
1844 elif self.type[0] == 'todo':
1845 busy = wx.BusyInfo(_('Deleting todo(s)...'))
1846 time.sleep(0.1)
1847 wx.Yield()
1848 for v in lst:
1849 self.sm.DeleteToDo(v['Location'])
1850 for idx in range(len(self.values[self.type[0]][' '])):
1851 if self.values[self.type[0]][' '][idx] == v:
1852 del self.values[self.type[0]][' '][idx]
1853 break
1854 elif self.type[0] == 'calendar':
1855 busy = wx.BusyInfo(_('Deleting calendar event(s)...'))
1856 time.sleep(0.1)
1857 wx.Yield()
1858 for v in lst:
1859 self.sm.DeleteCalendar(v['Location'])
1860 for idx in range(len(self.values[self.type[0]][' '])):
1861 if self.values[self.type[0]][' '][idx] == v:
1862 del self.values[self.type[0]][' '][idx]
1863 break
1864 except gammu.GSMError, val:
1865 try:
1866 del busy
1867 finally:
1868 self.ShowError(val[0])
1870 self.ActivateView(self.type[0], self.type[1])
1872 def OnLink(self, evt):
1873 v = evt.link.split('://')
1874 if len(v) != 2:
1875 print 'Bad URL!'
1876 return
1877 if v[0] == 'memory':
1878 t = v[1].split('/')
1879 if len(t) != 2:
1880 print 'Bad URL!'
1881 return
1883 if t[0] in ['ME', 'SM']:
1884 self.ActivateView('contact', t[0])
1885 try:
1886 self.browser.ShowLocation(int(t[1]))
1887 except KeyError:
1888 pass
1890 elif t[0] in ['MC', 'RC', 'DC']:
1891 self.ActivateView('call', t[0])
1892 try:
1893 self.browser.ShowLocation(int(t[1]))
1894 except KeyError:
1895 pass
1897 else:
1898 print 'Not supported memory type "%s"' % t[0]
1899 return
1900 else:
1901 print 'This link not yet implemented: "%s"' % evt.link
1903 def OnShowMessage(self, evt):
1904 try:
1905 if self.progress.IsShown():
1906 parent = self.progress
1907 else:
1908 parent = self
1909 except:
1910 parent = self
1912 # Is is Gammu error?
1913 if hasattr(evt, 'errortype') and evt.errortype == 'gammu':
1914 Wammu.ErrorMessage.ErrorMessage(parent,
1915 StrConv(evt.message),
1916 StrConv(evt.title)).ShowModal()
1917 else:
1918 wx.MessageDialog(parent,
1919 StrConv(evt.message),
1920 StrConv(evt.title),
1921 wx.OK | evt.type).ShowModal()
1923 if hasattr(evt, 'lock'):
1924 evt.lock.release()
1926 def ShowInfo(self, event):
1927 self.ShowProgress(_('Reading phone information'))
1928 Wammu.Info.GetInfo(self, self.sm).start()
1929 self.nextfun = self.ActivateView
1930 self.nextarg = ('info', ' ')
1933 # Calls
1936 def ShowCalls(self, event):
1937 self.GetCallsType('MC')
1938 self.nextfun = self.ShowCalls2
1939 self.nextarg = ()
1941 def ShowCalls2(self):
1942 self.GetCallsType('DC')
1943 self.nextfun = self.ShowCalls3
1944 self.nextarg = ()
1946 def ShowCalls3(self):
1947 self.GetCallsType('RC')
1948 self.nextfun = self.ActivateView
1949 self.nextarg = ('call', ' ')
1951 def GetCallsType(self, type):
1952 self.ShowProgress(_('Reading calls of type %s') % type)
1953 Wammu.Memory.GetMemory(self, self.sm, 'call', type).start()
1956 # Contacts
1959 def ShowContacts(self, event):
1960 self.GetContactsType('SM')
1961 self.nextfun = self.ShowContacts2
1962 self.nextarg = ()
1964 def ShowContacts2(self):
1965 self.GetContactsType('ME')
1966 self.nextfun = self.ActivateView
1967 self.nextarg = ('contact', ' ')
1969 def ShowContactsME(self, event):
1970 self.GetContactsType('ME')
1971 self.nextfun = self.ActivateView
1972 self.nextarg = ('contact', 'ME')
1974 def ShowContactsSM(self, event):
1975 self.GetContactsType('SM')
1976 self.nextfun = self.ActivateView
1977 self.nextarg = ('contact', 'SM')
1979 def GetContactsType(self, type):
1980 self.ShowProgress(_('Reading contacts from %s') % type)
1981 Wammu.Memory.GetMemory(self, self.sm, 'contact', type).start()
1984 # Messages
1987 def ShowMessages(self, event):
1988 self.ShowProgress(_('Reading messages'))
1989 Wammu.Message.GetMessage(self, self.sm).start()
1990 self.nextfun = self.ActivateView
1991 self.nextarg = ('message', ' ')
1994 # Todos
1997 def ShowTodos(self, event):
1998 self.ShowProgress(_('Reading todos'))
1999 Wammu.Todo.GetTodo(self, self.sm).start()
2000 self.nextfun = self.ActivateView
2001 self.nextarg = ('todo', ' ')
2004 # Calendars
2007 def ShowCalendar(self, event):
2008 self.ShowProgress(_('Reading calendar'))
2009 Wammu.Calendar.GetCalendar(self, self.sm).start()
2010 self.nextfun = self.ActivateView
2011 self.nextarg = ('calendar', ' ')
2014 # Time
2017 def SyncTime(self, event):
2018 busy = wx.BusyInfo(_('Setting time in phone...'))
2019 time.sleep(0.1)
2020 wx.Yield()
2021 try:
2022 self.sm.SetDateTime(datetime.datetime.now())
2023 except gammu.GSMError, val:
2024 del busy
2025 self.ShowError(val[0])
2028 # Files
2031 def SendFile(self, event):
2033 Sends file to phone.
2035 @todo: Maybe we could add some wildcards for commonly used file types.
2037 dlg = wx.FileDialog(self, _('Send file to phone'), os.getcwd(), '', _('All files') + ' (*.*)|*.*', wx.OPEN|wx.CHANGE_DIR)
2038 if dlg.ShowModal() == wx.ID_OK:
2039 path = dlg.GetPath()
2040 try:
2041 file_data = open(path, 'r').read()
2042 file_f = {
2043 'ID_FullName': '',
2044 'Name': os.path.basename(path),
2045 'Folder': 0,
2046 'Level': 1,
2047 'Used': len(file_data),
2048 'Buffer': file_data,
2049 'Type': 'Other',
2050 'Protected': 0,
2051 'ReadOnly': 0,
2052 'Hidden': 0,
2053 'System': 0,
2054 'Handle': 0,
2055 'Pos': 0,
2056 'Finished': 0
2058 busy = wx.BusyInfo(_('Sending file to phone...'))
2059 time.sleep(0.1)
2060 wx.Yield()
2061 try:
2062 while (not file_f['Finished']):
2063 file_f = self.sm.SendFilePart(file_f)
2064 except gammu.ERR_PERMISSION:
2065 wx.MessageDialog(self,
2066 _('Transfer has been rejected by phone.'),
2067 _('Transfer rejected!'),
2068 wx.OK | wx.ICON_ERROR).ShowModal()
2069 except gammu.GSMError, val:
2070 del busy
2071 self.ShowError(val[0])
2072 except IOError:
2073 wx.MessageDialog(self,
2074 _('Selected file "%s" was not found, no data read.') % path,
2075 _('File not found!'),
2076 wx.OK | wx.ICON_ERROR).ShowModal()
2079 # Connecting / Disconnecting
2082 def PhoneConnect(self, event = None):
2083 busy = wx.BusyInfo(_('One moment please, connecting to phone...'))
2084 time.sleep(0.1)
2085 wx.Yield()
2086 section = self.cfg.ReadInt('/Gammu/Section')
2087 config = self.cfg.gammu.GetConfig(section)
2088 if config['Connection'] == '' or config['Device'] == '':
2089 wx.MessageDialog(self,
2090 _('Phone connection is not properly configured, can not connect to phone.'),
2091 _('Connection not configured!'),
2092 wx.OK | wx.ICON_ERROR).ShowModal()
2093 return
2094 cfg = {
2095 'StartInfo': self.cfg.ReadBool('/Gammu/StartInfo'),
2096 'UseGlobalDebugFile': True,
2097 'DebugFile': None, # Set on other place
2098 'SyncTime': self.cfg.ReadBool('/Gammu/SyncTime'),
2099 'Connection': config['Connection'],
2100 'LockDevice': self.cfg.ReadBool('/Gammu/LockDevice'),
2101 'DebugLevel': 'textalldate', # Set on other place
2102 'Device': config['Device'],
2103 'Model': config['Model'],
2106 # Store configuration for error handling
2107 self.gammu_config = cfg
2109 # Compatibility with old Gammu versions
2110 cfg = Wammu.Utils.CompatConfig(cfg)
2112 self.sm.SetConfig(0, cfg)
2113 try:
2114 self.sm.Init()
2115 self.sm.SetIncomingCallback(self.IncomingEvent)
2116 try:
2117 self.sm.SetIncomingCall(True)
2118 except gammu.GSMError:
2119 pass
2120 self.TogglePhoneMenus(True)
2121 self.SetupNumberPrefix()
2122 try:
2123 self.IMEI = self.sm.GetIMEI()
2124 except gammu.GSMError:
2125 pass
2126 try:
2127 self.Manufacturer = self.sm.GetManufacturer()
2128 self.cfg.Write('/Phone-0/Manufacturer', self.Manufacturer)
2129 except gammu.GSMError:
2130 pass
2131 try:
2132 m = self.sm.GetModel()
2133 if m[0] == '' or m[0] == 'unknown':
2134 self.Model = m[1]
2135 else:
2136 self.Model = m[0]
2137 self.cfg.Write('/Phone-0/Model', self.Model)
2138 except gammu.GSMError:
2139 pass
2140 try:
2141 self.Version = self.sm.GetFirmware()[0]
2142 except:
2143 pass
2145 except gammu.GSMError, val:
2146 del busy
2147 self.ShowError(val[0])
2148 try:
2149 self.sm.Terminate()
2150 except gammu.GSMError, val:
2151 pass
2153 # Check for PIN
2154 if self.connected:
2155 try:
2156 code = self.sm.GetSecurityStatus()
2157 except gammu.GSMError:
2158 code = None
2159 if code is not None:
2160 dlg = wx.PasswordEntryDialog(self,
2161 _('Please enter %s code:') % code,
2162 _('Phone asks for security code'))
2163 if dlg.ShowModal() == wx.ID_OK:
2164 self.sm.EnterSecurityCode(code, dlg.GetValue())
2167 def DBUSActionCallback(self, id, action):
2169 Called when user does something on notification.
2171 self.dbus_notify.CloseNotification(self.last_dbus_id)
2172 if action == 'accept-call':
2173 self.sm.AnswerCall(0, True)
2174 elif action == 'reject-call':
2175 self.sm.CancelCall(0, True)
2176 else:
2177 print 'Unknown DBUS event: %s' % action
2179 def DBUSNotify(self, title, message, actions):
2181 Performs D-Bus notification if available.
2183 if self.dbus_notify is not None:
2184 self.last_dbus_id = self.dbus_notify.Notify(
2185 'Wammu', self.last_dbus_id, 'wammu',
2186 title, message, actions, {}, -1)
2189 def IncomingEvent(self, sm, type, data):
2191 Called on incoming event from phone.
2194 if type == 'Call':
2195 if data['Status'] != 'IncomingCall':
2196 # We care only about incoming calls
2197 return
2198 if data['Number'] == '':
2199 msg = _('Your phone has just received incoming call')
2200 else:
2201 msg = _('Your phone has just received incoming call from %s') % data['Number']
2202 self.DBUSNotify(_('Incoming call'), msg,
2203 ['reject-call', _('Reject'), 'accept-call', _('Accept')])
2205 def PhoneDisconnect(self, event = None):
2206 busy = wx.BusyInfo(_('One moment please, disconnecting from phone...'))
2207 time.sleep(0.1)
2208 wx.Yield()
2209 try:
2210 self.sm.Terminate()
2211 except gammu.ERR_NOTCONNECTED:
2212 pass
2213 except gammu.GSMError, val:
2214 del busy
2215 self.ShowError(val[0])
2216 self.TogglePhoneMenus(False)
2218 def SearchMessage(self, text):
2220 This has to send message as it is called from different thread.
2222 evt = Wammu.Events.TextEvent(text = text + '\n')
2223 wx.PostEvent(self.searchlog, evt)
2225 def SearchDone(self, lst):
2227 This has to send message as it is called from different thread.
2229 self.founddevices = lst
2230 evt = Wammu.Events.DoneEvent()
2231 wx.PostEvent(self.searchlog, evt)
2233 def SearchPhone(self, evt = None):
2234 if self.connected:
2235 wx.MessageDialog(self,
2236 _('Searching for phone can not be performed while you are connected to phone, please disconnect first.'),
2237 _('You are connected to phone!'),
2238 wx.OK | wx.ICON_ERROR).ShowModal()
2239 return
2241 index = self.cfg.gammu.FirstFree()
2242 result = Wammu.PhoneWizard.RunConfigureWizard(self, index)
2243 if result is not None:
2244 self.cfg.gammu.SetConfig(result['Position'], result['Device'], result['Connection'], result['Name'])
2245 self.cfg.WriteInt('/Gammu/Section', index)
2247 def About(self, evt = None):
2248 Wammu.About.AboutBox(self).ShowModal()
2250 def Website(self, evt = None):
2251 Wammu.Webbrowser.Open("http://%swammu.eu/?version=%s" % (Wammu.Utils.GetWebsiteLang(), Wammu.__version__))
2253 def Support(self, evt = None):
2254 Wammu.Webbrowser.Open("http://%swammu.eu/support/?version=%s" % (Wammu.Utils.GetWebsiteLang(), Wammu.__version__))
2256 def ReportBug(self, evt = None):
2257 Wammu.Webbrowser.Open("http://bugs.cihar.com/set_project.php?ref=bug_report_page.php&project_id=1")
2259 def PhoneDB(self, evt = None):
2260 Wammu.Webbrowser.Open("http://%swammu.eu/phones/" % Wammu.Utils.GetWebsiteLang())
2262 def Talkback(self, evt = None):
2263 Wammu.TalkbackDialog.DoTalkback(self, self.cfg, 0)
2265 def Donate(self, evt = None):
2266 Wammu.Webbrowser.Open("http://%swammu.eu/donate/?src=wammu" % Wammu.Utils.GetWebsiteLang())
2268 def SaveLog(self, evt = None):
2270 Saves debug log to file.
2272 dlg = wx.FileDialog(self,
2273 _('Save debug log as...'),
2274 os.getcwd(),
2275 'wammu.log',
2277 wx.SAVE | wx.OVERWRITE_PROMPT | wx.CHANGE_DIR)
2278 if dlg.ShowModal() == wx.ID_OK:
2279 Wammu.ErrorLog.SaveLog(filename = dlg.GetPath())