iIgnore folders which can not be properly converted to unicode (bug #602).
[wammu.git] / Wammu / Main.py
blob318e49f22c76503372a72c2a9fdeefce1b7543b9
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 - 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.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 from Wammu.Locales import StrConv, ConsoleStrConv
82 TALKBACK_COUNT = 30
83 TALKBACK_DAYS = 30
85 def SortDataKeys(a, b):
86 if a == 'info':
87 return -1
88 elif b == 'info':
89 return 1
90 else:
91 return cmp(a,b)
93 def SortDataSubKeys(a, b):
94 if a == ' ':
95 return -1
96 elif b == ' ':
97 return 1
98 else:
99 return cmp(a,b)
101 displaydata = {}
102 displaydata['info'] = {}
103 displaydata['call'] = {}
104 displaydata['contact'] = {}
105 displaydata['message'] = {}
106 displaydata['todo'] = {}
107 displaydata['calendar'] = {}
109 #information
110 displaydata['info'][' '] = ('', _('Phone'), _('Phone Information'), 'phone', [
111 {'Name':_('Wammu version'), 'Value':Wammu.__version__, 'Synced': True},
113 if Wammu.gammu_error == None:
114 displaydata['info'][' '][4].append({'Name':_('Gammu version'), 'Value':gammu.Version()[0], 'Synced': True})
115 displaydata['info'][' '][4].append({'Name':_('python-gammu version'), 'Value':gammu.Version()[1], 'Synced': True})
117 # calls
118 displaydata['call'][' '] = ('info', _('Calls'), _('All Calls'), 'call', [])
119 displaydata['call']['RC'] = ('call', _('Received'), _('Received Calls'), 'call-received', [])
120 displaydata['call']['MC'] = ('call', _('Missed'), _('Missed Calls'), 'call-missed', [])
121 displaydata['call']['DC'] = ('call', _('Outgoing'), _('Outgoing Calls'), 'call-outgoing', [])
123 # contacts
124 displaydata['contact'][' '] = ('info', _('Contacts'), _('All Contacts'), 'contact', [])
125 displaydata['contact']['SM'] = ('contact', _('SIM'), _('SIM Contacts'), 'contact-sim', [])
126 displaydata['contact']['ME'] = ('contact', _('Phone'), _('Phone Contacts'), 'contact-phone', [])
128 # contacts
129 displaydata['message'][' '] = ('info', _('Messages'), _('All Messages'), 'message', [])
130 displaydata['message']['Read'] = ('message', _('Read'), _('Read Messages'), 'message-read', [])
131 displaydata['message']['UnRead'] = ('message', _('Unread'), _('Unread Messages'), 'message-unread', [])
132 displaydata['message']['Sent'] = ('message', _('Sent'), _('Sent Messages'), 'message-sent', [])
133 displaydata['message']['UnSent'] = ('message', _('Unsent'), _('Unsent Messages'), 'message-unsent', [])
135 #todos
136 displaydata['todo'][' '] = ('info', _('Todos'), _('All Todo Items'), 'todo', [])
138 #calendar
139 displaydata['calendar'][' '] = ('info', _('Calendar'), _('All Calendar Events'), 'calendar', [])
142 ## Create a new frame class, derived from the wxPython Frame.
143 class WammuFrame(wx.Frame):
145 def __init__(self, parent, id):
146 self.cfg = Wammu.WammuSettings.WammuConfig()
147 Wammu.configuration = self.cfg
148 if self.cfg.HasEntry('/Main/X') and self.cfg.HasEntry('/Main/Y'):
149 pos = wx.Point(self.cfg.ReadInt('/Main/X'), self.cfg.ReadInt('/Main/Y'))
150 else:
151 pos =wx.DefaultPosition
152 size = wx.Size(self.cfg.ReadInt('/Main/Width'), self.cfg.ReadInt('/Main/Height'))
154 wx.Frame.__init__(self, parent, id, 'Wammu', pos, size, wx.DEFAULT_FRAME_STYLE)
156 if sys.platform == 'win32':
157 img = wx.Image(AppIconPath('wammu'), wx.BITMAP_TYPE_ICO)
158 else:
159 img = wx.Image(AppIconPath('wammu'), wx.BITMAP_TYPE_PNG)
161 self.icon = wx.EmptyIcon()
162 self.icon.CopyFromBitmap(wx.BitmapFromImage(img))
164 if self.icon.GetWidth() == 16 and self.icon.GetHeight() == 16:
165 self.icon16 = self.icon
166 else:
167 img.Rescale(16, 16)
168 self.icon16 = wx.EmptyIcon()
169 self.icon16.CopyFromBitmap(wx.BitmapFromImage(img))
171 self.SetIcon(self.icon)
173 self.CreateStatusBar(2)
174 self.SetStatusWidths([-1,400])
176 # Associate some events with methods of this class
177 wx.EVT_CLOSE(self, self.CloseWindow)
178 Wammu.Events.EVT_PROGRESS(self, self.OnProgress)
179 Wammu.Events.EVT_SHOW_MESSAGE(self, self.OnShowMessage)
180 Wammu.Events.EVT_LINK(self, self.OnLink)
181 Wammu.Events.EVT_DATA(self, self.OnData)
182 Wammu.Events.EVT_SHOW(self, self.OnShow)
183 Wammu.Events.EVT_EDIT(self, self.OnEdit)
184 Wammu.Events.EVT_SEND(self, self.OnSend)
185 Wammu.Events.EVT_CALL(self, self.OnCall)
186 Wammu.Events.EVT_MESSAGE(self, self.OnMessage)
187 Wammu.Events.EVT_DUPLICATE(self, self.OnDuplicate)
188 Wammu.Events.EVT_REPLY(self, self.OnReply)
189 Wammu.Events.EVT_DELETE(self, self.OnDelete)
190 Wammu.Events.EVT_BACKUP(self, self.OnBackup)
191 Wammu.Events.EVT_EXCEPTION(self, self.OnException)
193 self.splitter = wx.SplitterWindow(self, -1)
194 il = wx.ImageList(16, 16)
196 self.tree = wx.TreeCtrl(self.splitter)
197 self.tree.AssignImageList(il)
199 self.treei = {}
200 self.values = {}
202 keys = displaydata.keys()
203 keys.sort(SortDataKeys)
204 for type in keys:
205 self.treei[type] = {}
206 self.values[type] = {}
207 subkeys = displaydata[type].keys()
208 subkeys.sort(SortDataSubKeys)
209 for subtype in subkeys:
210 self.values[type][subtype] = displaydata[type][subtype][4]
211 if displaydata[type][subtype][0] == '':
212 self.treei[type][subtype] = self.tree.AddRoot(
213 displaydata[type][subtype][1],
214 il.Add(wx.Bitmap(IconPath(displaydata[type][subtype][3]))))
215 else:
216 self.treei[type][subtype] = self.tree.AppendItem(
217 self.treei[displaydata[type][subtype][0]][' '],
218 displaydata[type][subtype][1],
219 il.Add(wx.Bitmap(IconPath(displaydata[type][subtype][3]))))
221 for type in keys:
222 self.tree.Expand(self.treei[type][' '])
224 wx.EVT_TREE_SEL_CHANGED(self, self.tree.GetId(), self.OnTreeSel)
226 # common border sizes (Gnome HIG)
227 self.separatorHalf = 3
228 self.separatorNormal = 6
229 self.separatorTwice = 12
231 # right frame
232 self.rightsplitter = wx.SplitterWindow(self.splitter, -1)
233 self.rightwin = wx.Panel(self.rightsplitter, -1)
234 self.rightwin.sizer = wx.BoxSizer(wx.VERTICAL)
236 # title text
237 self.righttitle = wx.StaticText(self.rightwin, -1, 'Wammu')
238 self.rightwin.sizer.Add(self.righttitle, 0, wx.LEFT|wx.ALL|wx.EXPAND, self.separatorNormal)
240 # line
241 self.rightwin.sizer.Add(wx.StaticLine(self.rightwin, -1), 0 , wx.EXPAND)
243 # search input
244 self.searchpanel = wx.Panel(self.rightwin, -1)
245 self.searchpanel.sizer = wx.BoxSizer(wx.HORIZONTAL)
246 self.searchpanel.sizer.Add(wx.StaticText(self.searchpanel, -1, _('Search: ')), 0, wx.LEFT | wx.CENTER)
247 self.searchinput = wx.TextCtrl(self.searchpanel, -1)
248 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.'))
249 self.searchpanel.sizer.Add(self.searchinput, 1, wx.CENTER | wx.ALIGN_CENTER_VERTICAL)
250 self.searchchoice = wx.Choice(self.searchpanel, choices = [_('Text'), _('Regexp'), _('Wildcard')])
251 self.searchchoice.SetToolTipString(_('Select search type'))
252 self.searchchoice.SetSelection(self.cfg.ReadInt('/Defaults/SearchType'))
253 self.searchpanel.sizer.Add(self.searchchoice, 0, wx.LEFT | wx.CENTER | wx.EXPAND, self.separatorNormal)
254 self.searchclear = wx.Button(self.searchpanel, wx.ID_CLEAR)
255 self.searchpanel.sizer.Add(self.searchclear, 0, wx.LEFT | wx.CENTER | wx.EXPAND, self.separatorNormal)
256 self.searchpanel.SetSizer(self.searchpanel.sizer)
257 self.rightwin.sizer.Add(self.searchpanel, 0, wx.LEFT | wx.ALL | wx.EXPAND, self.separatorNormal)
259 self.Bind(wx.EVT_CHOICE, self.OnSearch, self.searchchoice)
260 self.Bind(wx.EVT_TEXT, self.OnSearch, self.searchinput)
261 self.Bind(wx.EVT_BUTTON, self.ClearSearch, self.searchclear)
263 # item browser
264 self.browser = Wammu.Browser.Browser(self.rightwin, self, self.cfg)
265 self.rightwin.sizer.Add(self.browser, 1, wx.EXPAND)
266 self.rightwin.SetSizer(self.rightwin.sizer)
268 # values displayer
269 self.content = Wammu.Displayer.Displayer(self.rightsplitter, self)
271 self.splitter.SplitVertically(self.tree, self.rightsplitter, self.cfg.ReadInt('/Main/Split'))
272 self.rightsplitter.SplitHorizontally(self.rightwin, self.content, self.cfg.ReadInt('/Main/SplitRight'))
274 # initial content
275 self.content.SetContent('<font size=+1><b>%s</b></font>' % (_('Welcome to Wammu %s') % Wammu.__version__))
277 # Prepare the menu bar
278 self.menuBar = wx.MenuBar()
280 menu1 = wx.Menu()
281 menu1.Append(100, _('&Write data'), _('Write data (except messages) to file.'))
282 menu1.Append(101, _('W&rite message'), _('Write messages to file.'))
283 menu1.Append(102, _('&Read data'), _('Read data (except messages) from file (does not import to the phone).'))
284 menu1.Append(103, _('R&ead messages'), _('Read messages from file (does not import to the phone).'))
285 menu1.AppendSeparator()
286 menu1.Append(150, _('&Phone wizard'), _('Search for phone or configure it using guided wizard.'))
287 menu1.Append(151, _('Se&ttings'), _('Change Wammu settings.'))
288 menu1.AppendSeparator()
289 menu1.Append(199, _('E&xit'), _('Terminate Wammu.'))
290 # Add menu to the menu bar
291 self.menuBar.Append(menu1, _('&Wammu'))
293 menu2 = wx.Menu()
294 menu2.Append(201, _('&Connect'), _('Connect the device.'))
295 menu2.Append(202, _('&Disconnect'), _('Disconnect the device.'))
296 menu2.AppendSeparator()
297 menu2.Append(210, _('&Synchronise time'), _('Synchronise time in mobile with PC.'))
298 menu2.AppendSeparator()
299 menu2.Append(250, _('Send &file'), _('Send file to phone.'))
300 # Add menu to the menu bar
301 self.menuBar.Append(menu2, _('&Phone'))
303 menu3 = wx.Menu()
304 menu3.Append(301, _('&Info'), _('Retrieve phone information.'))
305 menu3.AppendSeparator()
306 menu3.Append(310, _('Contacts (&SIM)'), _('Retrieve contacts from SIM.'))
307 menu3.Append(311, _('Contacts (&phone)'), _('Retrieve contacts from phone memory.'))
308 menu3.Append(312, _('&Contacts (All)'), _('Retrieve contacts from phone and SIM memory.'))
309 menu3.AppendSeparator()
310 menu3.Append(320, _('C&alls'), _('Retrieve call history.'))
311 menu3.AppendSeparator()
312 menu3.Append(330, _('&Messages'), _('Retrieve messages.'))
313 menu3.AppendSeparator()
314 menu3.Append(340, _('&Todos'), _('Retrieve todos.'))
315 menu3.AppendSeparator()
316 menu3.Append(350, _('Calenda&r'), _('Retrieve calendar events.'))
317 # Add menu to the menu bar
318 self.menuBar.Append(menu3, _('&Retrieve'))
320 menu4 = wx.Menu()
321 menu4.Append(401, _('&Contact'), _('Create new contact.'))
322 menu4.Append(402, _('Calendar &event'), _('Create new calendar event.'))
323 menu4.Append(403, _('&Todo'), _('Create new todo.'))
324 menu4.Append(404, _('&Message'), _('Create new message.'))
325 # Add menu to the menu bar
326 self.menuBar.Append(menu4, _('&Create'))
328 menu5 = wx.Menu()
329 menu5.Append(501, _('&Save'), _('Save currently retrieved data (except messages) to backup.'))
330 menu5.Append(502, _('S&ave messages'), _('Save currently retrieved messages to backup.'))
331 menu5.Append(503, _('&Import'), _('Import data from backup to phone.'))
332 menu5.Append(504, _('I&mport messages'), _('Import messages from backup to phone.'))
333 menu5.AppendSeparator()
334 menu5.Append(510, _('Export messages to &emails'), _('Export messages to emails in storage you choose.'))
335 # Add menu to the menu bar
336 self.menuBar.Append(menu5, _('&Backups'))
338 menuhelp = wx.Menu()
339 menuhelp.Append(1001, _('&Website'), _('Visit Wammu website.'))
340 menuhelp.Append(1002, _('&Support'), _('Visit Wammu support website.'))
341 menuhelp.Append(1003, _('&Report bug'), _('Report bug in Wammu, please include saved debug log if possible.'))
342 menuhelp.Append(1004, _('&Save debug log'), _('Save a copy of debug log, please include this in bug report.'))
343 menuhelp.AppendSeparator()
344 menuhelp.Append(1010, _('&Gammu Phone Database'), _('Visit database of user experiences with phones.'))
345 menuhelp.Append(1011, _('&Talkback'), _('Report your experiences into Gammu Phone Database.'))
346 menuhelp.AppendSeparator()
347 menuhelp.Append(1020, _('&Donate'), _('Donate to Wammu project.'))
348 menuhelp.AppendSeparator()
349 menuhelp.Append(1100, _('&About'), _('Information about program.'))
350 # Add menu to the menu bar
351 self.menuBar.Append(menuhelp, _('&Help'))
353 # Set menu bar
354 self.SetMenuBar(self.menuBar)
356 # menu events
357 wx.EVT_MENU(self, 100, self.WriteData)
358 wx.EVT_MENU(self, 101, self.WriteSMSData)
359 wx.EVT_MENU(self, 102, self.ReadData)
360 wx.EVT_MENU(self, 103, self.ReadSMSData)
361 wx.EVT_MENU(self, 150, self.SearchPhone)
362 wx.EVT_MENU(self, 151, self.Settings)
363 wx.EVT_MENU(self, 199, self.CloseWindow)
365 wx.EVT_MENU(self, 201, self.PhoneConnect)
366 wx.EVT_MENU(self, 202, self.PhoneDisconnect)
367 wx.EVT_MENU(self, 210, self.SyncTime)
368 wx.EVT_MENU(self, 250, self.SendFile)
370 wx.EVT_MENU(self, 301, self.ShowInfo)
371 wx.EVT_MENU(self, 310, self.ShowContactsSM)
372 wx.EVT_MENU(self, 311, self.ShowContactsME)
373 wx.EVT_MENU(self, 312, self.ShowContacts)
374 wx.EVT_MENU(self, 320, self.ShowCalls)
375 wx.EVT_MENU(self, 330, self.ShowMessages)
376 wx.EVT_MENU(self, 340, self.ShowTodos)
377 wx.EVT_MENU(self, 350, self.ShowCalendar)
379 wx.EVT_MENU(self, 401, self.NewContact)
380 wx.EVT_MENU(self, 402, self.NewCalendar)
381 wx.EVT_MENU(self, 403, self.NewTodo)
382 wx.EVT_MENU(self, 404, self.NewMessage)
384 wx.EVT_MENU(self, 501, self.Backup)
385 wx.EVT_MENU(self, 502, self.BackupSMS)
386 wx.EVT_MENU(self, 503, self.Import)
387 wx.EVT_MENU(self, 504, self.ImportSMS)
388 wx.EVT_MENU(self, 510, self.SMSToMails)
390 wx.EVT_MENU(self, 1001, self.Website)
391 wx.EVT_MENU(self, 1002, self.Support)
392 wx.EVT_MENU(self, 1003, self.ReportBug)
393 wx.EVT_MENU(self, 1004, self.SaveLog)
394 wx.EVT_MENU(self, 1010, self.PhoneDB)
395 wx.EVT_MENU(self, 1011, self.Talkback)
396 wx.EVT_MENU(self, 1020, self.Donate)
397 wx.EVT_MENU(self, 1100, self.About)
399 self.timer = None
400 self.TogglePhoneMenus(False)
402 self.type = ['info',' ']
404 self.TimerId = wx.NewId()
406 if Wammu.gammu_error == None:
407 # create state machine
408 self.sm = gammu.StateMachine()
410 # create temporary file for logs
411 fd, self.logfilename = tempfile.mkstemp('.log', 'wammu')
413 # set filename to be used for error reports
414 Wammu.ErrorLog.DEBUG_LOG_FILENAME = self.logfilename
416 if sys.platform != 'win32':
417 print ConsoleStrConv(
418 _('Debug log created in temporary file <%s>. In case of crash please include it in bugreport!') % self.logfilename
421 self.logfilefd = os.fdopen(fd, 'w+')
422 # use temporary file for logs
423 gammu.SetDebugFile(self.logfilefd)
424 gammu.SetDebugLevel('textalldate')
426 # initialize variables
427 self.showdebug = ''
428 self.IMEI = ''
429 self.Manufacturer = ''
430 self.Model = ''
431 self.Version = ''
432 self.tbicon = None
434 def HandleGammuError(self):
436 Show error about gammu import failure. Try to help user with various
437 situation which could happened, so that he can solve this problem.
439 error = str(Wammu.gammu_error)
440 if error.find('Runtime libGammu version does not match compile time version') != -1:
441 result = re.match('Runtime libGammu version does not match compile time version \(runtime: (\S+), compiletime: (\S+)\)', error)
443 wx.MessageDialog(self,
444 _('Wammu could not import gammu module, program will be terminated.') + '\n\n' +
445 _('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' +
446 _('You can fix it by recompiling python-gammu against gammu library you are currently using.'),
447 _('Gammu module not working!'),
448 wx.OK | wx.ICON_ERROR).ShowModal()
449 elif error.find('No module named gammu') != -1:
450 wx.MessageDialog(self,
451 _('Wammu could not import gammu module, program will be terminated.') + '\n\n' +
452 _('Gammu module was not found, you probably don\'t have properly installed python-gammu for current python version.'),
453 _('Gammu module not working!'),
454 wx.OK | wx.ICON_ERROR).ShowModal()
455 else:
456 wx.MessageDialog(self,
457 _('Wammu could not import gammu module, program will be terminated.') + '\n\n' +
458 _('The import failed with following error:') + '\n\n%s' % error,
459 _('Gammu module not working!'),
460 wx.OK | wx.ICON_ERROR).ShowModal()
461 sys.exit()
463 def InitConfiguration(self):
465 Binds Wammu configuration to Gammu one. If at least one section
466 exists, use first one (same as Gammu), otherwise we suggest search to
467 user.
469 gammucfg = self.cfg.gammu.GetConfigs()
470 if len(gammucfg) == 0:
471 dlg = wx.MessageDialog(self,
472 _('Wammu configuration was not found and Gammu settings couldn\'t be read.') + '\n\n' +
473 _('Do you want to configure phone connection now?') + '\n',
474 _('Configuration not found'),
475 wx.YES_NO | wx.YES_DEFAULT | wx.ICON_WARNING)
476 if dlg.ShowModal() == wx.ID_YES:
477 self.SearchPhone()
478 elif not self.cfg.HasEntry('/Gammu/Section'):
479 # behave as Gammu
480 self.cfg.WriteInt('/Gammu/Section', 0)
482 def TalkbackCheck(self):
484 Do ask for talkback after month of usage and at least 30 executions.
486 firstrun = self.cfg.ReadFloat('/Wammu/FirstRun')
487 if firstrun == -1:
488 firstrun = time.time()
489 self.cfg.WriteFloat('/Wammu/FirstRun', firstrun)
490 runs = self.cfg.ReadInt('/Wammu/RunCounter')
491 self.cfg.WriteInt('/Wammu/RunCounter', runs + 1)
492 if self.cfg.Read('/Wammu/TalkbackDone') == 'no':
493 if (firstrun + (3600 * 24 * TALKBACK_DAYS) < time.time()
494 and runs > TALKBACK_COUNT):
495 dlg = wx.MessageDialog(self,
496 _('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?') +
497 '\n\n' + _('Press Cancel to never show this question again.'),
498 _('Thanks for using Wammu'),
499 wx.YES_NO | wx.CANCEL | wx.ICON_INFORMATION)
500 ret = dlg.ShowModal()
501 if ret == wx.ID_YES:
502 self.Talkback()
503 elif ret == wx.ID_CANCEL:
504 self.cfg.Write('/Wammu/TalkbackDone', 'skipped')
506 def MigrateConfiguration(self):
508 Migrate configuration from pre-0.18 style one (Gammu was configured
509 inside Wammu configuration) to using .gammurc.
511 connection = self.cfg.Read('/Gammu/Connection')
512 device = self.cfg.Read('/Gammu/Device')
513 model = self.cfg.Read('/Gammu/Model')
514 gammucfg = self.cfg.gammu.GetConfigs()
515 if len(gammucfg) > 0:
516 for i in gammucfg:
517 cfg = self.cfg.gammu.GetConfig(i['Id'])
518 if cfg['Model'] == model and cfg['Connection'] == connection and cfg['Device'] == device:
519 self.cfg.WriteInt('/Gammu/Section', i['Id'])
520 break
521 if not self.cfg.HasEntry('/Gammu/Section'):
522 index = self.cfg.gammu.FirstFree()
523 self.cfg.gammu.SetConfig(index, device, connection, _('Migrated from older Wammu'), model)
524 self.cfg.WriteInt('/Gammu/Section', index)
526 def PostInit(self, appparent):
528 Do things which need window opened to behave correctly.
530 - Activate initial view.
531 - Show if something wrong has happened on gammu import.
532 - Initialize or migrate Gammu configuration.
533 - Connect to phone if required.
534 - Ask for talkback.
535 - Setup internal information.
537 self.ActivateView('info', ' ')
538 self.appparent = appparent
540 if Wammu.gammu_error != None:
541 self.HandleGammuError()
543 if not self.cfg.HasEntry('/Gammu/Section') and self.cfg.HasEntry('/Gammu/Connection'):
544 self.MigrateConfiguration()
546 self.InitConfiguration()
548 self.DoDebug(self.cfg.Read('/Debug/Show'))
550 if (self.cfg.Read('/Wammu/AutoConnect') == 'yes'):
551 self.PhoneConnect()
553 self.TalkbackCheck()
555 self.SetupNumberPrefix()
557 self.SetupStatusRefresh()
559 self.SetupTrayIcon()
561 self.InitDBUS()
563 def InitDBUS(self):
565 Initializes DBUS handlers if available.
567 self.dbus_notify = None
568 self.last_dbus_id = 0
569 if HAVE_DBUS:
570 try:
571 bus = dbus.SessionBus() #mainloop = self.appparent.MainLoop)
572 interface = 'org.freedesktop.Notifications'
573 path = '/org/freedesktop/Notifications'
574 if Wammu.Utils.DBUSServiceAvailable(bus, interface, True):
575 obj = bus.get_object(interface, path)
576 self.dbus_notify = dbus.Interface(obj, interface)
577 self.dbus_notify.connect_to_signal('ActionInvoked', self.DBUSActionCallback)
578 except dbus.DBusException:
579 self.dbus_notify = None
580 self.last_dbus_id = 0
582 def SetupTrayIcon(self):
583 if self.cfg.Read('/Wammu/TaskBarIcon') != 'yes':
584 if self.tbicon is not None:
585 self.tbicon.Destroy()
586 return
587 if self.tbicon is not None:
588 # Nothing to do
589 return
590 self.tbicon = wx.TaskBarIcon()
591 self.tbicon.SetIcon(self.icon16, 'Wammu')
592 self.tbicon.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.OnTaskBarRightClick)
593 self.tbicon.Bind(wx.EVT_TASKBAR_LEFT_UP, self.OnTaskBarLeftClick)
594 self.tbicon.Bind(wx.EVT_MENU, self.Settings, id=151)
595 self.tbicon.Bind(wx.EVT_MENU, self.PhoneConnect, id=201)
596 self.tbicon.Bind(wx.EVT_MENU, self.PhoneDisconnect, id=202)
597 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarRestore, id=100000)
598 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarMinimize, id=100001)
599 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=100002)
601 def OnTaskBarRightClick(self, evt):
602 menutaskbar = wx.Menu()
603 menutaskbar.Append(201, _('Connect'))
604 menutaskbar.Append(202, _('Disconnect'))
605 menutaskbar.AppendSeparator()
606 menutaskbar.Append(151, _('Settings'))
607 menutaskbar.AppendSeparator()
608 menutaskbar.Append(100000, _('Restore'))
609 menutaskbar.Append(100001, _('Minimize'))
610 menutaskbar.AppendSeparator()
611 menutaskbar.Append(100002, _('Close'))
612 self.tbicon.PopupMenu(menutaskbar)
613 menutaskbar.Destroy()
615 def OnTaskBarLeftClick(self, evt):
616 if self.IsShown():
617 self.Show(False)
618 else:
619 self.Show(True)
621 def OnTaskBarRestore(self, evt):
622 self.Show(True)
624 def OnTaskBarMinimize(self, evt):
625 self.Show(False)
627 def OnTaskBarClose(self, evt):
628 self.CloseWindow(evt)
630 def OnTimer(self, evt = None):
631 if self.connected:
632 try:
633 s = self.sm.GetSignalQuality()
634 b = self.sm.GetBatteryCharge()
635 d = self.sm.GetDateTime()
637 # Parse power source
638 power = _('Unknown')
639 if b['ChargeState'] == 'BatteryPowered':
640 power = _('battery')
641 elif b['ChargeState'] == 'BatteryConnected':
642 power = _('AC')
643 elif b['ChargeState'] == 'BatteryNotConnected':
644 power = _('no battery')
645 elif b['ChargeState'] == 'PowerFault':
646 power = _('fault')
647 elif b['ChargeState'] == 'BatteryCharging':
648 power = _('charging')
649 elif b['ChargeState'] == 'BatteryFull':
650 power = _('charged')
652 # Time might be None if it is invalid (eg. 0.0.0000 date)
653 if d is None:
654 time = _('Unknown')
655 else:
656 time = StrConv(d.strftime('%c'))
658 # Detect unknown signal quality
659 if s['SignalPercent'] == -1:
660 signal = _('Unknown')
661 else:
662 # l10n: Formatting of signal percentage, usually you can keep this as it is.
663 signal = _('%d %%') % s['SignalPercent']
665 self.SetStatusText(_('Bat: %(battery_percent)d %% (%(power_source)s), Sig: %(signal_level)s, Time: %(time)s') %
667 'battery_percent':b['BatteryPercent'],
668 'power_source':power,
669 'signal_level':signal,
670 'time': time
671 }, 1)
672 except gammu.GSMError:
673 pass
675 def SetupNumberPrefix(self):
676 self.prefix = self.cfg.Read('/Wammu/PhonePrefix')
677 if self.prefix == 'Auto':
678 if self.connected:
679 self.prefix = None
680 try:
681 on = Wammu.Utils.ParseMemoryEntry(self.sm.GetMemory(Location = 1, Type = 'ON'), self.cfg)['Number']
682 self.prefix = Wammu.Utils.GrabNumberPrefix(on, Wammu.Data.InternationalPrefixes)
683 except gammu.GSMError:
684 pass
685 if self.prefix is None:
686 try:
687 smsc = self.sm.GetSMSC()['Number']
688 self.prefix = Wammu.Utils.GrabNumberPrefix(smsc, Wammu.Data.InternationalPrefixes)
689 except gammu.GSMError:
690 pass
691 if self.prefix is None:
692 self.prefix = self.cfg.Read('/Wammu/LastPhonePrefix')
693 else:
694 self.cfg.Write('/Wammu/LastPhonePrefix', self.prefix)
695 else:
696 self.prefix = self.cfg.Read('/Wammu/LastPhonePrefix')
697 Wammu.Utils.NumberPrefix = self.prefix
699 def SetupStatusRefresh(self):
700 repeat = self.cfg.ReadInt('/Wammu/RefreshState')
701 if repeat == 0:
702 self.timer = None
703 else:
704 self.OnTimer()
705 self.timer = wx.Timer(self, self.TimerId)
706 wx.EVT_TIMER(self, self.TimerId, self.OnTimer)
707 self.timer.Start(repeat)
709 def DoDebug(self, newdebug):
710 if newdebug != self.showdebug:
711 self.showdebug = newdebug
712 if self.showdebug == 'yes':
713 self.logwin = Wammu.Logger.LogFrame(self, self.cfg)
714 self.logwin.Show(True)
715 wx.EVT_CLOSE(self.logwin, self.LogClose)
716 self.logger = Wammu.Logger.Logger(self.logwin, self.logfilename)
717 self.logger.start()
718 else:
719 self.CloseLogWindow()
721 def SaveWinSize(self, win, key):
722 x,y = win.GetPositionTuple()
723 w,h = win.GetSizeTuple()
725 self.cfg.WriteInt('/%s/X' % key, x)
726 self.cfg.WriteInt('/%s/Y' % key, y)
727 self.cfg.WriteInt('/%s/Width' % key, w)
728 self.cfg.WriteInt('/%s/Height' % key, h)
730 def CloseLogWindow(self):
731 if hasattr(self, 'logwin'):
732 self.SaveWinSize(self.logwin, 'Debug')
733 if hasattr(self, 'logger'):
734 self.logger.canceled = True
735 del self.logger
736 if hasattr(self, 'logwin'):
737 self.logwin.Destroy()
738 del self.logwin
741 def LogClose(self, evt = None):
742 self.cfg.Write('/Debug/Show', 'no')
743 self.CloseLogWindow()
745 def TogglePhoneMenus(self, enable):
746 self.connected = enable
747 if enable:
748 self.SetStatusText(_('Connected'), 1)
749 if self.timer != None:
750 self.OnTimer()
751 else:
752 self.SetStatusText(_('Disconnected'), 1)
753 mb = self.menuBar
755 mb.Enable(201, not enable);
756 mb.Enable(202, enable);
758 mb.Enable(210, enable);
760 mb.Enable(250, enable);
762 mb.Enable(301, enable);
764 mb.Enable(310, enable);
765 mb.Enable(311, enable);
766 mb.Enable(312, enable);
768 mb.Enable(320, enable);
770 mb.Enable(330, enable);
772 mb.Enable(340, enable);
774 mb.Enable(350, enable);
776 mb.Enable(401, enable);
777 mb.Enable(402, enable);
778 mb.Enable(403, enable);
779 mb.Enable(404, enable);
781 mb.Enable(501, enable);
782 mb.Enable(502, enable);
783 mb.Enable(503, enable);
784 mb.Enable(504, enable);
786 mb.Enable(510, enable);
788 def ActivateView(self, k1, k2):
789 self.tree.SelectItem(self.treei[k1][k2])
790 self.ChangeView(k1, k2)
792 def ChangeView(self, k1, k2):
793 self.ChangeBrowser(k1, k2)
794 self.righttitle.SetLabel(displaydata[k1][k2][2])
796 def ChangeBrowser(self, k1, k2):
797 self.type = [k1, k2]
798 if k2 == ' ':
799 data = []
800 for k3, v3 in self.values[k1].iteritems():
801 if k3 != '__':
802 data = data + v3
803 self.values[k1]['__'] = data
804 self.browser.Change(k1, data)
805 else:
806 self.browser.Change(k1, self.values[k1][k2])
807 self.browser.ShowRow(0)
809 def OnTreeSel(self, event):
810 item = event.GetItem()
811 for k1, v1 in self.treei.iteritems():
812 for k2, v2 in v1.iteritems():
813 if v2 == item:
814 self.ChangeView(k1, k2)
815 self.ClearSearch()
817 def OnSearch(self, event):
818 text = self.searchinput.GetValue()
819 type = self.searchchoice.GetSelection()
820 try:
821 self.browser.Filter(text, type)
822 self.searchinput.SetBackgroundColour(wx.NullColour)
823 except Wammu.Browser.FilterException:
824 self.searchinput.SetBackgroundColour(wx.RED)
826 def ClearSearch(self, event = None):
827 self.searchinput.SetValue('')
829 def Settings(self, event = None):
830 if self.connected:
831 connection_settings = {
832 'Connection': self.cfg.Read('/Gammu/Connection'),
833 'LockDevice': self.cfg.Read('/Gammu/LockDevice'),
834 'Device': self.cfg.Read('/Gammu/Device'),
835 'Model': self.cfg.Read('/Gammu/Model')
838 result = Wammu.Settings.Settings(self, self.cfg).ShowModal()
839 if result == wx.ID_OK:
840 if self.connected:
841 connection_settings_new = {
842 'Connection': self.cfg.Read('/Gammu/Connection'),
843 'LockDevice': self.cfg.Read('/Gammu/LockDevice'),
844 'Device': self.cfg.Read('/Gammu/Device'),
845 'Model': self.cfg.Read('/Gammu/Model')
848 if connection_settings != connection_settings_new:
849 wx.MessageDialog(self,
850 _('You changed parameters affecting phone connection, they will be used next time you connect to phone.'),
851 _('Notice'),
852 wx.OK | wx.ICON_INFORMATION).ShowModal()
853 self.DoDebug(self.cfg.Read('/Debug/Show'))
854 self.SetupNumberPrefix()
855 self.SetupStatusRefresh()
856 self.SetupTrayIcon()
858 def CloseWindow(self, event):
859 self.SaveWinSize(self, 'Main')
860 if hasattr(self, 'logwin'):
861 self.CloseLogWindow()
862 self.cfg.WriteInt('/Main/Split', self.splitter.GetSashPosition())
863 self.cfg.WriteInt('/Main/SplitRight', self.rightsplitter.GetSashPosition())
864 self.cfg.WriteInt('/Defaults/SearchType', self.searchchoice.GetCurrentSelection())
866 gammu.SetDebugFile(None)
867 gammu.SetDebugLevel('nothing')
869 self.logfilefd.close()
871 if hasattr(self, 'logger'):
872 self.logger.canceled = True
873 self.logger.join()
875 if self.tbicon is not None:
876 self.tbicon.Destroy()
878 if sys.platform != 'win32':
879 print ConsoleStrConv(
880 _('Looks like normal program termination, deleting log file.')
882 try:
883 os.unlink(self.logfilename)
884 except:
885 print ConsoleStrConv(
886 _('Failed to unlink temporary log file, please delete it yourself.')
888 print ConsoleStrConv(
889 _('Filename: %s') % self.logfilename
892 # Forcibily save configuration
893 self.cfg.Flush()
895 # tell the window to kill itself
896 self.Destroy()
898 def ShowError(self, info):
899 evt = Wammu.Events.ShowMessageEvent(
900 message = Wammu.Utils.FormatError(_('Error while communicating with phone'), info),
901 title = _('Error Occured'),
902 errortype = 'gammu',
903 type = wx.ICON_ERROR)
904 wx.PostEvent(self, evt)
906 def ShowProgress(self, text):
907 self.progress = wx.ProgressDialog(
908 _('Operation in progress'),
909 text,
910 100,
911 self,
912 wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_ESTIMATED_TIME)
914 def OnProgress(self, evt):
915 if hasattr(self, 'progress'):
916 if not self.progress.Update(evt.progress):
917 try:
918 evt.cancel()
919 except:
920 pass
921 if (evt.progress == 100):
922 del self.progress
923 if hasattr(evt, 'lock'):
924 evt.lock.release()
926 def OnException(self, evt):
927 Wammu.Error.Handler(*evt.data)
929 def OnData(self, evt):
930 self.values[evt.type[0]][evt.type[1]] = evt.data
931 if evt.last:
932 if hasattr(self, 'progress'):
933 self.progress.Update(100)
934 del self.progress
936 if hasattr(self, 'nextfun'):
937 f = self.nextfun
938 a = self.nextarg
939 del self.nextfun
940 del self.nextarg
941 f (*a)
943 def ShowData(self, data):
944 text = u''
945 if data is not None:
946 for d in data:
947 if len(d) == 2:
948 text += u'<b>%s</b>: %s<br>' % (d[0], d[1])
949 else:
950 text += u'<br>%s' % d[0]
951 self.content.SetContent(text)
953 def OnShow(self, evt):
954 data = v = evt.data
955 if data is None:
956 pass
957 elif self.type == ['info',' ']:
958 data = [(evt.data['Name'], evt.data['Value'])]
959 elif self.type[0] == 'contact' or self.type[0] == 'call':
960 data = [
961 (_('Location'), str(v['Location'])),
962 (_('Memory type'), v['MemoryType'])]
963 for i in v['Entries']:
964 s = Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values, linkphone = False)
965 try:
966 if i['VoiceTag']:
967 s += ', ' + (_('voice tag %x') % i['VoiceTag'])
968 except:
969 pass
970 data.append((i['Type'], s))
971 elif self.type[0] == 'message':
972 data = [
973 (_('Number'), Wammu.Utils.GetNumberLink([] + self.values['contact']['ME'] + self.values['contact']['SM'], v['Number'])),
974 (_('Date'), StrConv(v['DateTime'])),
975 (_('Location'), StrConv(v['Location'])),
976 (_('Folder'), StrConv(v['SMS'][0]['Folder'])),
977 (_('Memory'), StrConv(v['SMS'][0]['Memory'])),
978 (_('SMSC'), Wammu.Utils.GetNumberLink([] + self.values['contact']['ME'] + self.values['contact']['SM'], v['SMS'][0]['SMSC']['Number'])),
979 (_('State'), StrConv(v['State']))]
980 if v['Name'] != '':
981 data.append((_('Name'), StrConv(v['Name'])))
982 data.append((Wammu.MessageDisplay.SmsToHtml(self.cfg, v),))
983 elif self.type[0] == 'todo':
984 data = [
985 (_('Location'), str(v['Location'])),
986 (_('Priority'), v['Priority']),
987 (_('Type'), v['Type']),
989 for i in v['Entries']:
990 data.append((i['Type'], Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values)))
991 elif self.type[0] == 'calendar':
992 data = [
993 (_('Location'), str(v['Location'])),
994 (_('Type'), v['Type']),
996 for i in v['Entries']:
997 data.append((i['Type'], Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values)))
998 else:
999 data = [('Show not yet implemented! (type = %s)' % self.type[0])]
1000 self.ShowData(data)
1002 def NewContact(self, evt):
1003 self.EditContact({})
1005 def NewCalendar(self, evt):
1006 self.EditCalendar({})
1008 def NewTodo(self, evt):
1009 self.EditTodo({})
1011 def NewMessage(self, evt):
1012 self.ComposeMessage({})
1014 def ComposeMessage(self, v, action = 'save'):
1015 if Wammu.Composer.SMSComposer(self, self.cfg, v, self.values, action).ShowModal() == wx.ID_OK:
1017 if len(v['Numbers']) == 0:
1018 v['Numbers'] = ['Wammu']
1020 for number in v['Numbers']:
1021 busy = wx.BusyInfo(_('Writing message(s)...'))
1022 time.sleep(0.1)
1023 wx.Yield()
1024 v['Number'] = number
1025 v['SMS'] = gammu.EncodeSMS(v['SMSInfo'])
1027 if v['Save']:
1028 result = {}
1029 result['SMS'] = []
1031 try:
1032 for msg in v['SMS']:
1033 msg['SMSC']['Location'] = 1
1035 msg['Folder'] = v['Folder']
1036 msg['Number'] = v['Number']
1037 msg['Type'] = v['Type']
1038 msg['State'] = v['State']
1040 if v['Save']:
1041 (msg['Location'], msg['Folder']) = self.sm.AddSMS(msg)
1042 if v['Send']:
1043 # When sending of saved message fails, send it directly:
1044 try:
1045 msg['MessageReference'] = self.sm.SendSavedSMS(0, msg['Location'])
1046 except gammu.GSMError:
1047 msg['MessageReference'] = self.sm.SendSMS(msg)
1048 try:
1049 result['SMS'].append(self.sm.GetSMS(0, msg['Location'])[0])
1050 except gammu.ERR_EMPTY:
1051 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()
1052 elif v['Send']:
1053 msg['MessageReference'] = self.sm.SendSMS(msg)
1055 if v['Save']:
1056 info = gammu.DecodeSMS(result['SMS'])
1057 if info != None:
1058 result['SMSInfo'] = info
1059 Wammu.Utils.ParseMessage(result, (info != None))
1060 result['Synced'] = True
1061 self.values['message'][result['State']].append(result)
1063 except gammu.GSMError, val:
1064 del busy
1065 self.ShowError(val[0])
1067 if v['Save']:
1068 try:
1069 self.ActivateView('message', result['State'])
1070 self.browser.ShowLocation(result['Location'])
1071 except KeyError:
1072 pass
1074 def EditContact(self, v):
1075 backup = copy.deepcopy(v)
1076 shoulddelete = (v == {} or v['Location'] == 0)
1077 if Wammu.Editor.ContactEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1078 try:
1079 busy = wx.BusyInfo(_('Writing contact...'))
1080 time.sleep(0.1)
1081 wx.Yield()
1082 # was entry moved => delete it from internal list
1083 if not shoulddelete:
1084 for idx in range(len(self.values['contact'][backup['MemoryType']])):
1085 if self.values['contact'][backup['MemoryType']][idx] == v:
1086 del self.values['contact'][backup['MemoryType']][idx]
1087 break
1089 # have we specified location? => add or set
1090 if v['Location'] == 0:
1091 v['Location'] = self.sm.AddMemory(v)
1092 else:
1093 try:
1094 v['Location'] = self.sm.SetMemory(v)
1095 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1096 v['Location'] = self.sm.AddMemory(v)
1098 # was entry moved => delete it from phone
1099 if not shoulddelete:
1100 if v['MemoryType'] != backup['MemoryType'] or v['Location'] != backup['Location']:
1101 # delete from phone
1102 self.sm.DeleteMemory(backup['MemoryType'], backup['Location'])
1104 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1105 try:
1106 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1107 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1108 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()
1109 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1110 v['Synced'] = True
1111 # append new value to list
1112 self.values['contact'][v['MemoryType']].append(v)
1114 except gammu.GSMError, val:
1115 del busy
1116 v = backup
1117 self.ShowError(val[0])
1119 if (self.type[0] == 'contact' and self.type[1] == ' ') or not v.has_key('MemoryType'):
1120 self.ActivateView('contact', ' ')
1121 try:
1122 self.browser.ShowLocation(v['Location'], ('MemoryType', v['MemoryType']))
1123 except KeyError:
1124 pass
1125 else:
1126 self.ActivateView('contact', v['MemoryType'])
1127 try:
1128 self.browser.ShowLocation(v['Location'])
1129 except KeyError:
1130 pass
1132 def EditCalendar(self, v):
1133 backup = copy.deepcopy(v)
1134 shoulddelete = (v == {} or v['Location'] == 0)
1135 if Wammu.Editor.CalendarEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1136 try:
1137 busy = wx.BusyInfo(_('Writing calendar...'))
1138 time.sleep(0.1)
1139 wx.Yield()
1140 # was entry moved => delete it from internal list
1141 if not shoulddelete:
1142 # delete from internal list
1143 for idx in range(len(self.values['calendar'][' '])):
1144 if self.values['calendar'][' '][idx] == v:
1145 del self.values['calendar'][' '][idx]
1146 break
1148 # have we specified location? => add or set
1149 if v['Location'] == 0:
1150 v['Location'] = self.sm.AddCalendar(v)
1151 else:
1152 try:
1153 v['Location'] = self.sm.SetCalendar(v)
1154 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1155 v['Location'] = self.sm.AddCalendar(v)
1157 # was entry moved => delete it from phone
1158 if not shoulddelete:
1159 if v['Location'] != backup['Location']:
1160 # delete from phone
1161 self.sm.DeleteCalendar(backup['Location'])
1163 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1164 try:
1165 v = self.sm.GetCalendar(v['Location'])
1166 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1167 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()
1168 Wammu.Utils.ParseCalendar(v)
1169 v['Synced'] = True
1170 # append new value to list
1171 self.values['calendar'][' '].append(v)
1173 except gammu.GSMError, val:
1174 del busy
1175 v = backup
1176 self.ShowError(val[0])
1178 self.ActivateView('calendar', ' ')
1179 try:
1180 self.browser.ShowLocation(v['Location'])
1181 except KeyError:
1182 pass
1184 def EditTodo(self, v):
1185 backup = copy.deepcopy(v)
1186 shoulddelete = (v == {} or v['Location'] == 0)
1187 if Wammu.Editor.TodoEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1188 try:
1189 busy = wx.BusyInfo(_('Writing todo...'))
1190 time.sleep(0.1)
1191 wx.Yield()
1192 # was entry moved => delete it from internal list
1193 if not shoulddelete:
1194 for idx in range(len(self.values['todo'][' '])):
1195 if self.values['todo'][' '][idx] == v:
1196 del self.values['todo'][' '][idx]
1197 break
1199 # have we specified location? => add or set
1200 if v['Location'] == 0:
1201 v['Location'] = self.sm.AddToDo(v)
1202 else:
1203 try:
1204 v['Location'] = self.sm.SetToDo(v)
1205 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1206 v['Location'] = self.sm.AddToDo(v)
1208 # was entry moved => delete it from phone
1209 if not shoulddelete:
1210 if v['Location'] != backup['Location']:
1211 # delete from phone
1212 self.sm.DeleteToDo(backup['Location'])
1214 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1215 try:
1216 v = self.sm.GetToDo(v['Location'])
1217 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1218 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()
1219 Wammu.Utils.ParseTodo(v)
1220 v['Synced'] = True
1221 # append new value to list
1222 self.values['todo'][' '].append(v)
1223 except gammu.GSMError, val:
1224 del busy
1225 v = backup
1226 self.ShowError(val[0])
1228 self.ActivateView('todo', ' ')
1229 try:
1230 self.browser.ShowLocation(v['Location'])
1231 except KeyError:
1232 pass
1235 def OnEdit(self, evt):
1236 if evt.data != {} and not evt.data['Synced']:
1237 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()
1238 return
1239 if self.type[0] == 'contact':
1240 self.EditContact(evt.data)
1241 elif self.type[0] == 'calendar':
1242 self.EditCalendar(evt.data)
1243 elif self.type[0] == 'todo':
1244 self.EditTodo(evt.data)
1245 else:
1246 print 'Edit not yet implemented (type = %s)!' % self.type[0]
1248 def OnReply(self, evt):
1249 if self.type[0] == 'message':
1250 self.ComposeMessage({'Number': evt.data['Number']}, action = 'send')
1251 else:
1252 print 'Reply not yet implemented!'
1253 print evt.index
1255 def OnCall(self, evt):
1256 if self.type[0] in ['call', 'contact']:
1257 num = Wammu.Select.SelectContactNumber(self, evt.data)
1258 if num == None:
1259 return
1261 try:
1262 self.sm.DialVoice(num)
1263 except gammu.GSMError, val:
1264 self.ShowError(val[0])
1265 elif self.type[0] == 'message':
1266 try:
1267 self.sm.DialVoice(evt.data['Number'])
1268 except gammu.GSMError, val:
1269 self.ShowError(val[0])
1270 else:
1271 print 'Call not yet implemented (type = %s)!' % self.type[0]
1273 def OnMessage(self, evt):
1274 if self.type[0] in ['call', 'contact']:
1276 num = Wammu.Select.SelectContactNumber(self, evt.data)
1277 if num == None:
1278 return
1279 self.ComposeMessage({'Number': num}, action = 'send')
1280 elif self.type[0] == 'message':
1281 self.ComposeMessage({'Number': evt.data['Number']}, action = 'send')
1282 else:
1283 print 'Message send not yet implemented (type = %s)!' % self.type[0]
1285 def OnDuplicate(self, evt):
1286 if evt.data != {} and not evt.data['Synced']:
1287 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()
1288 return
1289 v = copy.deepcopy(evt.data)
1290 if self.type[0] == 'contact':
1291 v['Location'] = 0
1292 self.EditContact(v)
1293 elif self.type[0] == 'calendar':
1294 v['Location'] = 0
1295 self.EditCalendar(v)
1296 elif self.type[0] == 'todo':
1297 v['Location'] = 0
1298 self.EditTodo(v)
1299 elif self.type[0] == 'message':
1300 self.ComposeMessage(v)
1301 else:
1302 print 'Duplicate not yet implemented (type = %s)!' % self.type[0]
1305 def OnSend(self, evt):
1306 if evt.data != {} and not evt.data['Synced']:
1307 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()
1308 return
1309 if self.type[0] == 'message':
1310 v = evt.data
1311 try:
1312 try:
1313 for loc in v['Location'].split(', '):
1314 self.sm.SendSavedSMS(0, int(loc))
1315 except gammu.ERR_NOTSUPPORTED:
1316 for msg in v['SMS']:
1317 self.sm.SendSMS(msg)
1318 except gammu.GSMError, val:
1319 self.ShowError(val[0])
1321 def SMSToMails(self, evt):
1322 messages = self.values['message']['Read'] + \
1323 self.values['message']['UnRead'] + \
1324 self.values['message']['Sent'] + \
1325 self.values['message']['UnSent']
1326 contacts = self.values['contact']['ME'] + \
1327 self.values['contact']['SM']
1328 Wammu.SMSExport.SMSExport(self, messages, contacts)
1330 def SelectBackupFile(self, type, save = True, data = False):
1331 wildcard = ''
1332 if type == 'message':
1333 wildcard += _('Gammu messages backup') + ' (*.smsbackup)|*.smsbackup|'
1334 exts = ['smsbackup']
1335 else:
1336 if not save:
1337 wildcard += _('All backup formats') + '|*.backup;*.lmb;*.vcf;*.ldif;*.vcs;*.ics|'
1339 wildcard += _('Gammu backup [all data]') + ' (*.backup)|*.backup|'
1340 exts = ['backup']
1342 if type in ['contact', 'all']:
1343 wildcard += _('Nokia backup [contacts]') + ' (*.lmb)|*.lmb|'
1344 exts.append('lmb')
1345 if type in ['contact', 'all']:
1346 wildcard += _('vCard [contacts]') + ' (*.vcf)|*.vcf|'
1347 exts.append('vcf')
1348 if type in ['contact', 'all']:
1349 wildcard += _('LDIF [contacts]') + ' (*.ldif)|*.ldif|'
1350 exts.append('ldif')
1351 if type in ['todo', 'calendar', 'all']:
1352 wildcard += _('vCalendar [todo,calendar]') + ' (*.vcs)|*.vcs|'
1353 exts.append('vcs')
1354 if type in ['todo', 'calendar', 'all']:
1355 wildcard += _('iCalendar [todo,calendar]') + ' (*.ics)|*.ics|'
1356 exts.append('ics')
1358 wildcard += _('All files') + ' (*.*)|*.*'
1359 exts.append(None)
1361 if data:
1362 if save:
1363 dlg = wx.FileDialog(self, _('Save data as...'), os.getcwd(), "", wildcard, wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
1364 else:
1365 dlg = wx.FileDialog(self, _('Read data'), os.getcwd(), "", wildcard, wx.OPEN|wx.CHANGE_DIR)
1366 else:
1367 if save:
1368 dlg = wx.FileDialog(self, _('Save backup as...'), os.getcwd(), "", wildcard, wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
1369 else:
1370 dlg = wx.FileDialog(self, _('Import backup'), os.getcwd(), "", wildcard, wx.OPEN|wx.CHANGE_DIR)
1371 if dlg.ShowModal() == wx.ID_OK:
1372 path = dlg.GetPath()
1373 if save:
1374 ext = exts[dlg.GetFilterIndex()]
1375 # Add automatic extension if we know one and file does not
1376 # have any
1377 if (os.path.splitext(path)[1] == '' and
1378 ext is not None):
1379 path += '.' + ext
1380 return path
1381 return None
1383 def ReadBackup(self, type, data = False):
1384 filename = self.SelectBackupFile(type, save = False, data = data)
1385 if filename == None:
1386 return (None, None)
1387 try:
1388 if type == 'message':
1389 backup = gammu.ReadSMSBackup(filename)
1390 else:
1391 backup = gammu.ReadBackup(filename)
1392 except gammu.GSMError, val:
1393 info = val[0]
1394 evt = Wammu.Events.ShowMessageEvent(
1395 message = Wammu.Utils.FormatError(_('Error while reading backup'), info),
1396 title = _('Error Occured'),
1397 errortype = 'gammu',
1398 type = wx.ICON_ERROR)
1399 wx.PostEvent(self, evt)
1400 return (None, None)
1401 return (filename, backup)
1403 def ReadData(self, evt):
1404 (filename, backup) = self.ReadBackup('all', True)
1405 if backup == None:
1406 return
1408 if len(backup['PhonePhonebook']) > 0:
1409 self.values['contact']['ME'] = map(Wammu.Utils.ParseMemoryEntry, backup['PhonePhonebook'], [self.cfg] * len(backup['PhonePhonebook']))
1410 if len(backup['SIMPhonebook']) > 0:
1411 self.values['contact']['SM'] = map(Wammu.Utils.ParseMemoryEntry, backup['SIMPhonebook'], [self.cfg] * len(backup['SIMPhonebook']))
1412 if len(backup['ToDo']) > 0:
1413 self.values['todo'][' '] = map(Wammu.Utils.ParseTodo, backup['ToDo'])
1414 if len(backup['Calendar']) > 0:
1415 self.values['calendar'][' '] = map(Wammu.Utils.ParseCalendar, backup['Calendar'])
1417 self.ActivateView('contact', ' ')
1419 self.SetStatusText(_('Data has been read from file "%s"') % filename)
1421 def ReadSMSData(self, evt):
1422 (filename, backup) = self.ReadBackup('message', True)
1423 if backup == None:
1424 return
1426 res = Wammu.Utils.ProcessMessages(map(lambda x:[x], backup), False)
1428 self.values['message']['Sent'] = res['sent']
1429 self.values['message']['UnSent'] = res['unsent']
1430 self.values['message']['Read'] = res['read']
1431 self.values['message']['UnRead'] = res['unread']
1433 self.ActivateView('message', ' ')
1435 self.SetStatusText(_('Data has been read from file "%s"') % filename)
1437 def ImportSMS(self, evt):
1438 (filename, backup) = self.ReadBackup('message')
1439 if backup == None:
1440 return
1441 choices = []
1442 values = []
1443 if len(backup) > 0:
1444 values.append('message')
1445 choices.append(_('%d messages') % len(backup))
1447 if len(values) == 0:
1448 wx.MessageDialog(self,
1449 _('No importable data were found in file "%s"') % filename,
1450 _('No data to import'),
1451 wx.OK | wx.ICON_INFORMATION).ShowModal()
1452 return
1454 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'),
1455 choices,style = wx.CHOICEDLG_STYLE | wx.RESIZE_BORDER,
1456 size = (600, 200))
1457 if dlg.ShowModal() != wx.ID_OK:
1458 return
1460 lst = dlg.GetValue()
1461 if len(lst) == 0:
1462 return
1464 try:
1465 busy = wx.BusyInfo(_('Importing data...'))
1466 time.sleep(0.1)
1467 wx.Yield()
1468 for i in lst:
1469 datatype = values[i]
1470 if datatype == 'message':
1471 smsl = []
1472 for v in backup:
1473 v['Folder'] = 2 # FIXME: this should be configurable
1474 v['SMSC']['Location'] = 1
1475 (v['Location'], v['Folder']) = self.sm.AddSMS(v)
1476 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1477 v = self.sm.GetSMS(0, v['Location'])
1478 smsl.append(v)
1480 res = Wammu.Utils.ProcessMessages(smsl, True)
1482 self.values['message']['Sent'] += res['sent']
1483 self.values['message']['UnSent'] += res['unsent']
1484 self.values['message']['Read'] += res['read']
1485 self.values['message']['UnRead'] += res['unread']
1487 self.ActivateView('message', ' ')
1489 except gammu.GSMError, val:
1490 self.ShowError(val[0])
1492 wx.MessageDialog(self,
1493 _('Backup has been imported from file "%s"') % filename,
1494 _('Backup imported'),
1495 wx.OK | wx.ICON_INFORMATION).ShowModal()
1497 def Import(self, evt):
1498 (filename, backup) = self.ReadBackup('all')
1499 if backup == None:
1500 return
1501 choices = []
1502 values = []
1503 if len(backup['PhonePhonebook']) > 0:
1504 values.append('PhonePhonebook')
1505 choices.append(_('%d phone contact entries') % len(backup['PhonePhonebook']))
1506 if len(backup['SIMPhonebook']) > 0:
1507 values.append('SIMPhonebook')
1508 choices.append(_('%d SIM contact entries') % len(backup['SIMPhonebook']))
1509 if len(backup['ToDo']) > 0:
1510 values.append('ToDo')
1511 choices.append(_('%d to do entries') % len(backup['ToDo']))
1512 if len(backup['Calendar']) > 0:
1513 values.append('Calendar')
1514 choices.append(_('%d calendar entries') % len(backup['Calendar']))
1516 if len(values) == 0:
1517 wx.MessageDialog(self,
1518 _('No importable data were found in file "%s"') % filename,
1519 _('No data to import'),
1520 wx.OK | wx.ICON_INFORMATION).ShowModal()
1521 return
1523 msg = ''
1524 if backup['Model'] != '':
1525 msg = '\n \n' + _('Backup saved from phone %s') % backup['Model']
1526 if backup['IMEI'] != '':
1527 msg += _(', serial number %s') % backup['IMEI']
1528 if backup['Creator'] != '':
1529 msg += '\n \n' + _('Backup was created by %s') % backup['Creator']
1530 if backup['DateTime'] != None:
1531 msg += '\n \n' + _('Backup saved on %s') % str(backup['DateTime'])
1533 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'),
1534 choices,style = wx.CHOICEDLG_STYLE | wx.RESIZE_BORDER,
1535 size = (600, 200))
1536 if dlg.ShowModal() != wx.ID_OK:
1537 return
1539 lst = dlg.GetValue()
1540 if len(lst) == 0:
1541 return
1543 try:
1544 busy = wx.BusyInfo(_('Importing data...'))
1545 time.sleep(0.1)
1546 wx.Yield()
1547 for i in lst:
1548 datatype = values[i]
1549 if datatype == 'PhonePhonebook':
1550 for v in backup['PhonePhonebook']:
1551 v['Location'] = self.sm.AddMemory(v)
1552 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1553 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1554 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1555 v['Synced'] = True
1556 # append new value to list
1557 self.values['contact'][v['MemoryType']].append(v)
1558 self.ActivateView('contact', 'ME')
1559 elif datatype == 'SIMPhonebook':
1560 for v in backup['SIMPhonebook']:
1561 v['Location'] = self.sm.AddMemory(v)
1562 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1563 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1564 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1565 v['Synced'] = True
1566 # append new value to list
1567 self.values['contact'][v['MemoryType']].append(v)
1568 self.ActivateView('contact', 'SM')
1569 elif datatype == 'ToDo':
1570 for v in backup['ToDo']:
1571 v['Location'] = self.sm.AddToDo(v)
1572 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1573 v = self.sm.GetToDo(v['Location'])
1574 Wammu.Utils.ParseTodo(v)
1575 v['Synced'] = True
1576 # append new value to list
1577 self.values['todo'][' '].append(v)
1578 self.ActivateView('todo', ' ')
1579 elif datatype == 'Calendar':
1580 for v in backup['Calendar']:
1581 v['Location'] = self.sm.AddCalendar(v)
1582 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1583 v = self.sm.GetCalendar(v['Location'])
1584 Wammu.Utils.ParseCalendar(v)
1585 v['Synced'] = True
1586 # append new value to list
1587 self.values['calendar'][' '].append(v)
1588 self.ActivateView('calendar', ' ')
1589 except gammu.GSMError, val:
1590 self.ShowError(val[0])
1592 wx.MessageDialog(self,
1593 _('Backup has been imported from file "%s"') % filename,
1594 _('Backup imported'),
1595 wx.OK | wx.ICON_INFORMATION).ShowModal()
1597 def WriteData(self, evt):
1598 self.DoBackup(True, 'all')
1600 def WriteSMSData(self, evt):
1601 self.DoBackup(True, 'message')
1603 def Backup(self, evt):
1604 self.DoBackup(False, 'all')
1606 def BackupSMS(self, evt):
1607 self.DoBackup(False, 'message')
1609 def PrepareBackup(self):
1610 backup = {}
1611 backup['Creator'] = 'Wammu ' + Wammu.__version__
1612 backup['IMEI'] = self.IMEI
1613 backup['Model'] = '%s %s %s' % ( self.Manufacturer, self.Model, self.Version)
1614 return backup
1616 def WriteBackup(self, filename, type, backup, data = False):
1617 try:
1618 if type == 'message':
1619 # Backup is here our internal SMS list: [{'SMS':[{sms1}, {sms2}]}, ...]
1620 data = map(lambda x:x['SMS'], backup)
1621 backup = []
1622 for x in data:
1623 backup += x
1624 gammu.SaveSMSBackup(filename, backup)
1625 else:
1626 gammu.SaveBackup(filename, backup)
1627 if data:
1628 self.SetStatusText(_('Backup has been saved to file "%s"') % filename)
1629 else:
1630 self.SetStatusText(_('Data has been saved to file "%s"') % filename)
1631 except gammu.GSMError, val:
1632 info = val[0]
1633 evt = Wammu.Events.ShowMessageEvent(
1634 message = Wammu.Utils.FormatError(_('Error while saving backup'), info),
1635 title = _('Error Occured'),
1636 errortype = 'gammu',
1637 type = wx.ICON_ERROR)
1638 wx.PostEvent(self, evt)
1639 except MemoryError, val:
1640 info = val[0]
1641 evt = Wammu.Events.ShowMessageEvent(
1642 message = _('Error while saving backup, probably some limit inside of Gammu exceeded.\n%s') % str(info),
1643 title = _('Error Occured'),
1644 type = wx.ICON_ERROR)
1645 wx.PostEvent(self, evt)
1647 def DoBackup(self, data, type):
1648 filename = self.SelectBackupFile(type, data = data)
1649 if filename == None:
1650 return
1651 ext = os.path.splitext(filename)[1].lower()
1653 if type == 'message':
1654 backup = self.values['message']['Read'] + self.values['message']['UnRead'] + self.values['message']['Sent'] + self.values['message']['UnSent']
1655 else:
1656 backup = self.PrepareBackup()
1657 if ext in ['.vcf', '.ldif']:
1658 # these support only one phonebook, so merged it
1659 backup['PhonePhonebook'] = self.values['contact']['ME'] + self.values['contact']['SM']
1660 else:
1661 backup['PhonePhonebook'] = self.values['contact']['ME']
1662 backup['SIMPhonebook'] = self.values['contact']['SM']
1664 backup['ToDo'] = self.values['todo'][' ']
1665 backup['Calendar'] = self.values['calendar'][' ']
1666 self.WriteBackup(filename, type, backup, data)
1669 def OnBackup(self, evt):
1670 filename = self.SelectBackupFile(self.type[0])
1671 if filename == None:
1672 return
1673 ext = os.path.splitext(filename)[1].lower()
1674 lst = evt.lst
1675 if self.type[0] == 'message':
1676 backup = lst
1677 else:
1678 backup = self.PrepareBackup()
1679 if self.type[0] == 'contact':
1680 if ext in ['.vcf', '.ldif']:
1681 # these support only one phonebook, so keep it merged
1682 backup['PhonePhonebook'] = lst
1683 else:
1684 sim = []
1685 phone = []
1686 for item in lst:
1687 if item['MemoryType'] == 'SM':
1688 sim.append(item)
1689 elif item['MemoryType'] == 'ME':
1690 phone.append(item)
1691 backup['PhonePhonebook'] = phone
1692 backup['SIMPhonebook'] = sim
1693 elif self.type[0] == 'todo':
1694 backup['ToDo'] = lst
1695 elif self.type[0] == 'calendar':
1696 backup['Calendar'] = lst
1698 self.WriteBackup(filename, self.type[0], backup)
1700 def OnDelete(self, evt):
1701 # first check on supported types
1702 if not self.type[0] in ['contact', 'call', 'message', 'todo', 'calendar']:
1703 print 'Delete not yet implemented! (items to delete = %s, type = %s)' % (str(evt.lst), self.type[0])
1704 return
1706 lst = evt.lst
1708 if len(lst) == 0:
1709 # nothing to delete
1710 return
1712 if not lst[0]['Synced']:
1713 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()
1714 return
1716 # check for confirmation
1717 if self.cfg.Read('/Wammu/ConfirmDelete') == 'yes':
1718 count = len(lst)
1719 if count == 1:
1720 v = lst[0]
1721 if self.type[0] == 'contact':
1722 txt = _('Are you sure you want to delete contact "%s"?') % v['Name']
1723 elif self.type[0] == 'call':
1724 txt = _('Are you sure you want to delete call from "%s"?') % v['Name']
1725 elif self.type[0] == 'message':
1726 txt = _('Are you sure you want to delete message from "%s"?') % v['Number']
1727 elif self.type[0] == 'todo':
1728 txt = _('Are you sure you want to delete todo entry "%s"?') % v['Text']
1729 elif self.type[0] == 'calendar':
1730 txt = _('Are you sure you want to delete calendar entry "%s"?') % v['Text']
1731 else:
1732 if self.type[0] == 'contact':
1733 txt = Wammu.Locales.ngettext(
1734 'Are you sure you want to delete %d contact?',
1735 'Are you sure you want to delete %d contacts?',
1736 count) % count
1737 elif self.type[0] == 'call':
1738 txt = Wammu.Locales.ngettext(
1739 'Are you sure you want to delete %d call?',
1740 'Are you sure you want to delete %d calls?',
1741 count) % count
1742 elif self.type[0] == 'message':
1743 txt = Wammu.Locales.ngettext(
1744 'Are you sure you want to delete %d message?',
1745 'Are you sure you want to delete %d messages?',
1746 count) % count
1747 elif self.type[0] == 'todo':
1748 txt = Wammu.Locales.ngettext(
1749 'Are you sure you want to delete %d todo entry?',
1750 'Are you sure you want to delete %d todo entries?',
1751 count) % count
1752 elif self.type[0] == 'calendar':
1753 txt = Wammu.Locales.ngettext(
1754 'Are you sure you want to delete %d calendar entry?',
1755 'Are you sure you want to delete %d calendar entries?',
1756 count) % count
1757 dlg = wx.MessageDialog(self,
1758 txt,
1759 _('Confirm deleting'),
1760 wx.OK | wx.CANCEL | wx.ICON_WARNING)
1761 if dlg.ShowModal() != wx.ID_OK:
1762 return
1764 # do real delete
1765 try:
1766 if self.type[0] == 'contact' or self.type[0] == 'call':
1767 busy = wx.BusyInfo(_('Deleting contact(s)...'))
1768 time.sleep(0.1)
1769 wx.Yield()
1770 for v in lst:
1771 self.sm.DeleteMemory(v['MemoryType'], v['Location'])
1772 for idx in range(len(self.values[self.type[0]][v['MemoryType']])):
1773 if self.values[self.type[0]][v['MemoryType']][idx] == v:
1774 del self.values[self.type[0]][v['MemoryType']][idx]
1775 break
1776 elif self.type[0] == 'message':
1777 busy = wx.BusyInfo(_('Deleting message(s)...'))
1778 time.sleep(0.1)
1779 wx.Yield()
1780 for v in lst:
1781 for loc in v['Location'].split(', '):
1782 self.sm.DeleteSMS(0, int(loc))
1783 for idx in range(len(self.values[self.type[0]][v['State']])):
1784 if self.values[self.type[0]][v['State']][idx] == v:
1785 del self.values[self.type[0]][v['State']][idx]
1786 break
1787 elif self.type[0] == 'todo':
1788 busy = wx.BusyInfo(_('Deleting todo(s)...'))
1789 time.sleep(0.1)
1790 wx.Yield()
1791 for v in lst:
1792 self.sm.DeleteToDo(v['Location'])
1793 for idx in range(len(self.values[self.type[0]][' '])):
1794 if self.values[self.type[0]][' '][idx] == v:
1795 del self.values[self.type[0]][' '][idx]
1796 break
1797 elif self.type[0] == 'calendar':
1798 busy = wx.BusyInfo(_('Deleting calendar event(s)...'))
1799 time.sleep(0.1)
1800 wx.Yield()
1801 for v in lst:
1802 self.sm.DeleteCalendar(v['Location'])
1803 for idx in range(len(self.values[self.type[0]][' '])):
1804 if self.values[self.type[0]][' '][idx] == v:
1805 del self.values[self.type[0]][' '][idx]
1806 break
1807 except gammu.GSMError, val:
1808 try:
1809 del busy
1810 finally:
1811 self.ShowError(val[0])
1813 self.ActivateView(self.type[0], self.type[1])
1815 def OnLink(self, evt):
1816 v = evt.link.split('://')
1817 if len(v) != 2:
1818 print 'Bad URL!'
1819 return
1820 if v[0] == 'memory':
1821 t = v[1].split('/')
1822 if len(t) != 2:
1823 print 'Bad URL!'
1824 return
1826 if t[0] in ['ME', 'SM']:
1827 self.ActivateView('contact', t[0])
1828 try:
1829 self.browser.ShowLocation(int(t[1]))
1830 except KeyError:
1831 pass
1833 elif t[0] in ['MC', 'RC', 'DC']:
1834 self.ActivateView('call', t[0])
1835 try:
1836 self.browser.ShowLocation(int(t[1]))
1837 except KeyError:
1838 pass
1840 else:
1841 print 'Not supported memory type "%s"' % t[0]
1842 return
1843 else:
1844 print 'This link not yet implemented: "%s"' % evt.link
1846 def OnShowMessage(self, evt):
1847 try:
1848 if self.progress.IsShown():
1849 parent = self.progress
1850 else:
1851 parent = self
1852 except:
1853 parent = self
1855 # Is is Gammu error?
1856 if hasattr(evt, 'errortype') and evt.errortype == 'gammu':
1857 Wammu.ErrorMessage.ErrorMessage(parent,
1858 StrConv(evt.message),
1859 StrConv(evt.title)).ShowModal()
1860 else:
1861 wx.MessageDialog(parent,
1862 StrConv(evt.message),
1863 StrConv(evt.title),
1864 wx.OK | evt.type).ShowModal()
1866 if hasattr(evt, 'lock'):
1867 evt.lock.release()
1869 def ShowInfo(self, event):
1870 self.ShowProgress(_('Reading phone information'))
1871 Wammu.Info.GetInfo(self, self.sm).start()
1872 self.nextfun = self.ActivateView
1873 self.nextarg = ('info', ' ')
1876 # Calls
1879 def ShowCalls(self, event):
1880 self.GetCallsType('MC')
1881 self.nextfun = self.ShowCalls2
1882 self.nextarg = ()
1884 def ShowCalls2(self):
1885 self.GetCallsType('DC')
1886 self.nextfun = self.ShowCalls3
1887 self.nextarg = ()
1889 def ShowCalls3(self):
1890 self.GetCallsType('RC')
1891 self.nextfun = self.ActivateView
1892 self.nextarg = ('call', ' ')
1894 def GetCallsType(self, type):
1895 self.ShowProgress(_('Reading calls of type %s') % type)
1896 Wammu.Memory.GetMemory(self, self.sm, 'call', type).start()
1899 # Contacts
1902 def ShowContacts(self, event):
1903 self.GetContactsType('SM')
1904 self.nextfun = self.ShowContacts2
1905 self.nextarg = ()
1907 def ShowContacts2(self):
1908 self.GetContactsType('ME')
1909 self.nextfun = self.ActivateView
1910 self.nextarg = ('contact', ' ')
1912 def ShowContactsME(self, event):
1913 self.GetContactsType('ME')
1914 self.nextfun = self.ActivateView
1915 self.nextarg = ('contact', 'ME')
1917 def ShowContactsSM(self, event):
1918 self.GetContactsType('SM')
1919 self.nextfun = self.ActivateView
1920 self.nextarg = ('contact', 'SM')
1922 def GetContactsType(self, type):
1923 self.ShowProgress(_('Reading contacts from %s') % type)
1924 Wammu.Memory.GetMemory(self, self.sm, 'contact', type).start()
1927 # Messages
1930 def ShowMessages(self, event):
1931 self.ShowProgress(_('Reading messages'))
1932 Wammu.Message.GetMessage(self, self.sm).start()
1933 self.nextfun = self.ActivateView
1934 self.nextarg = ('message', ' ')
1937 # Todos
1940 def ShowTodos(self, event):
1941 self.ShowProgress(_('Reading todos'))
1942 Wammu.Todo.GetTodo(self, self.sm).start()
1943 self.nextfun = self.ActivateView
1944 self.nextarg = ('todo', ' ')
1947 # Calendars
1950 def ShowCalendar(self, event):
1951 self.ShowProgress(_('Reading calendar'))
1952 Wammu.Calendar.GetCalendar(self, self.sm).start()
1953 self.nextfun = self.ActivateView
1954 self.nextarg = ('calendar', ' ')
1957 # Time
1960 def SyncTime(self, event):
1961 busy = wx.BusyInfo(_('Setting time in phone...'))
1962 time.sleep(0.1)
1963 wx.Yield()
1964 try:
1965 self.sm.SetDateTime(datetime.datetime.now())
1966 except gammu.GSMError, val:
1967 del busy
1968 self.ShowError(val[0])
1971 # Files
1974 def SendFile(self, event):
1976 Sends file to phone.
1978 @todo: Maybe we could add some wildcards for commonly used file types.
1980 dlg = wx.FileDialog(self, _('Send file to phone'), os.getcwd(), '', _('All files') + ' (*.*)|*.*', wx.OPEN|wx.CHANGE_DIR)
1981 if dlg.ShowModal() == wx.ID_OK:
1982 path = dlg.GetPath()
1983 try:
1984 file_data = open(path, 'r').read()
1985 file_f = {
1986 'ID_FullName': '',
1987 'Name': os.path.basename(path),
1988 'Folder': 0,
1989 'Level': 1,
1990 'Used': len(file_data),
1991 'Buffer': file_data,
1992 'Type': 'Other',
1993 'Protected': 0,
1994 'ReadOnly': 0,
1995 'Hidden': 0,
1996 'System': 0,
1997 'Handle': 0,
1998 'Pos': 0,
1999 'Finished': 0
2001 busy = wx.BusyInfo(_('Sending file to phone...'))
2002 time.sleep(0.1)
2003 wx.Yield()
2004 try:
2005 while (not file_f['Finished']):
2006 file_f = self.sm.SendFilePart(file_f)
2007 except gammu.ERR_PERMISSION:
2008 wx.MessageDialog(self,
2009 _('Transfer has been rejected by phone.'),
2010 _('Transfer rejected!'),
2011 wx.OK | wx.ICON_ERROR).ShowModal()
2012 except gammu.GSMError, val:
2013 del busy
2014 self.ShowError(val[0])
2015 except IOError:
2016 wx.MessageDialog(self,
2017 _('Selected file "%s" was not found, no data read.') % path,
2018 _('File not found!'),
2019 wx.OK | wx.ICON_ERROR).ShowModal()
2022 # Connecting / Disconnecting
2025 def PhoneConnect(self, event = None):
2026 busy = wx.BusyInfo(_('One moment please, connecting to phone...'))
2027 time.sleep(0.1)
2028 wx.Yield()
2029 section = self.cfg.ReadInt('/Gammu/Section')
2030 config = self.cfg.gammu.GetConfig(section)
2031 if config['Connection'] == '' or config['Device'] == '':
2032 wx.MessageDialog(self,
2033 _('Phone connection is not properly configured, can not connect to phone.'),
2034 _('Connection not configured!'),
2035 wx.OK | wx.ICON_ERROR).ShowModal()
2036 return
2037 cfg = {
2038 'StartInfo': self.cfg.Read('/Gammu/StartInfo'),
2039 'UseGlobalDebugFile': 1,
2040 'DebugFile': None, # Set on other place
2041 'SyncTime': self.cfg.Read('/Gammu/SyncTime'),
2042 'Connection': config['Connection'],
2043 'LockDevice': self.cfg.Read('/Gammu/LockDevice'),
2044 'DebugLevel': 'textalldate', # Set on other place
2045 'Device': config['Device'],
2046 'Localize': None, # Set automatically by python-gammu
2047 'Model': config['Model'],
2049 if cfg['Model'] == 'auto':
2050 cfg['Model'] = ''
2051 self.sm.SetConfig(0, cfg)
2052 try:
2053 self.sm.Init()
2054 self.sm.SetIncomingCallback(self.IncomingEvent)
2055 try:
2056 self.sm.SetIncomingCall(True)
2057 except gammu.GSMError:
2058 pass
2059 self.TogglePhoneMenus(True)
2060 self.SetupNumberPrefix()
2061 try:
2062 self.IMEI = self.sm.GetIMEI()
2063 except gammu.GSMError:
2064 pass
2065 try:
2066 self.Manufacturer = self.sm.GetManufacturer()
2067 self.cfg.Write('/Phone-0/Manufacturer', self.Manufacturer)
2068 except gammu.GSMError:
2069 pass
2070 try:
2071 m = self.sm.GetModel()
2072 if m[0] == '' or m[0] == 'unknown':
2073 self.Model = m[1]
2074 else:
2075 self.Model = m[0]
2076 self.cfg.Write('/Phone-0/Model', self.Model)
2077 except gammu.GSMError:
2078 pass
2079 try:
2080 self.Version = self.sm.GetFirmware()[0]
2081 except:
2082 pass
2084 except gammu.GSMError, val:
2085 del busy
2086 self.ShowError(val[0])
2087 try:
2088 self.sm.Terminate()
2089 except gammu.GSMError, val:
2090 pass
2092 def DBUSActionCallback(self, id, action):
2094 Called when user does something on notification.
2096 self.dbus_notify.CloseNotification(self.last_dbus_id)
2097 if action == 'accept-call':
2098 self.sm.AnswerCall(0, True)
2099 elif action == 'reject-call':
2100 self.sm.CancelCall(0, True)
2101 else:
2102 print 'Unknown DBUS event: %s' % action
2104 def DBUSNotify(self, title, message, actions):
2106 Performs D-Bus notification if available.
2108 if self.dbus_notify is not None:
2109 self.last_dbus_id = self.dbus_notify.Notify(
2110 'Wammu', self.last_dbus_id, 'wammu',
2111 title, message, actions, {}, -1)
2114 def IncomingEvent(self, sm, type, data):
2116 Called on incoming event from phone.
2119 if type == 'Call':
2120 if data['Status'] != 'IncomingCall':
2121 # We care only about incoming calls
2122 return
2123 if data['Number'] == '':
2124 msg = _('Your phone has just received incoming call')
2125 else:
2126 msg = _('Your phone has just received incoming call from %s') % data['Number']
2127 self.DBUSNotify(_('Incoming call'), msg,
2128 ['reject-call', _('Reject'), 'accept-call', _('Accept')])
2130 def PhoneDisconnect(self, event = None):
2131 busy = wx.BusyInfo(_('One moment please, disconnecting from phone...'))
2132 time.sleep(0.1)
2133 wx.Yield()
2134 try:
2135 self.sm.Terminate()
2136 except gammu.ERR_NOTCONNECTED:
2137 pass
2138 except gammu.GSMError, val:
2139 del busy
2140 self.ShowError(val[0])
2141 self.TogglePhoneMenus(False)
2143 def SearchMessage(self, text):
2145 This has to send message as it is called from different thread.
2147 evt = Wammu.Events.TextEvent(text = text + '\n')
2148 wx.PostEvent(self.searchlog, evt)
2150 def SearchDone(self, lst):
2152 This has to send message as it is called from different thread.
2154 self.founddevices = lst
2155 evt = Wammu.Events.DoneEvent()
2156 wx.PostEvent(self.searchlog, evt)
2158 def SearchPhone(self, evt = None):
2159 index = self.cfg.gammu.FirstFree()
2160 result = Wammu.PhoneWizard.RunConfigureWizard(self, index)
2161 if result is not None:
2162 self.cfg.gammu.SetConfig(result['Position'], result['Device'], result['Connection'], result['Name'])
2163 self.cfg.WriteInt('/Gammu/Section', index)
2165 def About(self, evt = None):
2166 Wammu.About.AboutBox(self).ShowModal()
2168 def Website(self, evt = None):
2169 Wammu.Webbrowser.Open("http://%swammu.eu/?version=%s" % (Wammu.Utils.GetWebsiteLang(), Wammu.__version__))
2171 def Support(self, evt = None):
2172 Wammu.Webbrowser.Open("http://%swammu.eu/support?version=%s" % (Wammu.Utils.GetWebsiteLang(), Wammu.__version__))
2174 def ReportBug(self, evt = None):
2175 Wammu.Webbrowser.Open("http://bugs.cihar.com/set_project.php?ref=bug_report_page.php&project_id=1")
2177 def PhoneDB(self, evt = None):
2178 Wammu.Webbrowser.Open("http://%scihar.com/gammu/phonedb" % Wammu.Utils.GetWebsiteLang())
2180 def Talkback(self, evt = None):
2181 Wammu.TalkbackDialog.DoTalkback(self, self.cfg, 0)
2183 def Donate(self, evt = None):
2184 Wammu.Webbrowser.Open("http://%swammu.eu/donate?src=wammu" % Wammu.Utils.GetWebsiteLang())
2186 def SaveLog(self, evt = None):
2188 Saves debug log to file.
2190 dlg = wx.FileDialog(self,
2191 _('Save debug log as...'),
2192 os.getcwd(),
2193 'wammu.log',
2195 wx.SAVE | wx.OVERWRITE_PROMPT | wx.CHANGE_DIR)
2196 if dlg.ShowModal() == wx.ID_OK:
2197 Wammu.ErrorLog.SaveLog(filename = dlg.GetPath())