Translation update done using Pootle.
[wammu.git] / Wammu / Main.py
blobbf7fbe3e8fd0b4dca2df7ea54928e6ffaafb1f20
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'), _('Import data from backup to phone.'))
333 menu5.Append(504, _('I&mport messages'), _('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 return
595 if self.tbicon is not None:
596 # Nothing to do
597 return
598 self.tbicon = wx.TaskBarIcon()
599 self.tbicon.SetIcon(self.icon16, 'Wammu')
600 self.tbicon.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.OnTaskBarRightClick)
601 self.tbicon.Bind(wx.EVT_TASKBAR_LEFT_UP, self.OnTaskBarLeftClick)
602 self.tbicon.Bind(wx.EVT_MENU, self.Settings, id=151)
603 self.tbicon.Bind(wx.EVT_MENU, self.PhoneConnect, id=201)
604 self.tbicon.Bind(wx.EVT_MENU, self.PhoneDisconnect, id=202)
605 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarRestore, id=100000)
606 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarMinimize, id=100001)
607 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=100002)
609 def OnTaskBarRightClick(self, evt):
610 menutaskbar = wx.Menu()
611 menutaskbar.Append(201, _('Connect'))
612 menutaskbar.Append(202, _('Disconnect'))
613 menutaskbar.AppendSeparator()
614 menutaskbar.Append(151, _('Settings'))
615 menutaskbar.AppendSeparator()
616 menutaskbar.Append(100000, _('Restore'))
617 menutaskbar.Append(100001, _('Minimize'))
618 menutaskbar.AppendSeparator()
619 menutaskbar.Append(100002, _('Close'))
620 self.tbicon.PopupMenu(menutaskbar)
621 menutaskbar.Destroy()
623 def OnTaskBarLeftClick(self, evt):
624 if self.IsShown():
625 self.Show(False)
626 else:
627 self.Show(True)
629 def OnTaskBarRestore(self, evt):
630 self.Show(True)
632 def OnTaskBarMinimize(self, evt):
633 self.Show(False)
635 def OnTaskBarClose(self, evt):
636 self.CloseWindow(evt)
638 def OnTimer(self, evt = None):
639 if self.connected:
640 try:
641 s = self.sm.GetSignalQuality()
642 b = self.sm.GetBatteryCharge()
643 d = self.sm.GetDateTime()
645 # Parse power source
646 power = _('Unknown')
647 if b['ChargeState'] == 'BatteryPowered':
648 power = _('battery')
649 elif b['ChargeState'] == 'BatteryConnected':
650 power = _('AC')
651 elif b['ChargeState'] == 'BatteryNotConnected':
652 power = _('no battery')
653 elif b['ChargeState'] == 'PowerFault':
654 power = _('fault')
655 elif b['ChargeState'] == 'BatteryCharging':
656 power = _('charging')
657 elif b['ChargeState'] == 'BatteryFull':
658 power = _('charged')
660 # Time might be None if it is invalid (eg. 0.0.0000 date)
661 if d is None:
662 time = _('Unknown')
663 else:
664 time = StrConv(d.strftime('%c'))
666 # Detect unknown signal quality
667 if s['SignalPercent'] == -1:
668 signal = _('Unknown')
669 else:
670 # l10n: Formatting of signal percentage, usually you can keep this as it is.
671 signal = _('%d %%') % s['SignalPercent']
673 self.SetStatusText(_('Bat: %(battery_percent)d %% (%(power_source)s), Sig: %(signal_level)s, Time: %(time)s') %
675 'battery_percent':b['BatteryPercent'],
676 'power_source':power,
677 'signal_level':signal,
678 'time': time
679 }, 1)
680 except gammu.GSMError:
681 pass
683 def SetupNumberPrefix(self):
684 self.prefix = self.cfg.Read('/Wammu/PhonePrefix')
685 if self.prefix == 'Auto':
686 if self.connected:
687 self.prefix = None
688 try:
689 on = Wammu.Utils.ParseMemoryEntry(self.sm.GetMemory(Location = 1, Type = 'ON'), self.cfg)['Number']
690 self.prefix = Wammu.Utils.GrabNumberPrefix(on, Wammu.Data.InternationalPrefixes)
691 except gammu.GSMError:
692 pass
693 if self.prefix is None:
694 try:
695 smsc = self.sm.GetSMSC()['Number']
696 self.prefix = Wammu.Utils.GrabNumberPrefix(smsc, Wammu.Data.InternationalPrefixes)
697 except gammu.GSMError:
698 pass
699 if self.prefix is None:
700 self.prefix = self.cfg.Read('/Wammu/LastPhonePrefix')
701 else:
702 self.cfg.Write('/Wammu/LastPhonePrefix', self.prefix)
703 else:
704 self.prefix = self.cfg.Read('/Wammu/LastPhonePrefix')
705 Wammu.Utils.NumberPrefix = self.prefix
707 def SetupStatusRefresh(self):
708 repeat = self.cfg.ReadInt('/Wammu/RefreshState')
709 if repeat == 0:
710 self.timer = None
711 else:
712 self.OnTimer()
713 self.timer = wx.Timer(self, self.TimerId)
714 wx.EVT_TIMER(self, self.TimerId, self.OnTimer)
715 self.timer.Start(repeat)
717 def DoDebug(self, newdebug):
718 if newdebug != self.showdebug:
719 self.showdebug = newdebug
720 if self.showdebug == 'yes':
721 self.logwin = Wammu.Logger.LogFrame(self, self.cfg)
722 self.logwin.Show(True)
723 wx.EVT_CLOSE(self.logwin, self.LogClose)
724 self.logger = Wammu.Logger.Logger(self.logwin, self.logfilename)
725 self.logger.start()
726 else:
727 self.CloseLogWindow()
729 def SaveWinSize(self, win, key):
730 x,y = win.GetPositionTuple()
731 w,h = win.GetSizeTuple()
733 self.cfg.WriteInt('/%s/X' % key, x)
734 self.cfg.WriteInt('/%s/Y' % key, y)
735 self.cfg.WriteInt('/%s/Width' % key, w)
736 self.cfg.WriteInt('/%s/Height' % key, h)
738 def CloseLogWindow(self):
739 if hasattr(self, 'logwin'):
740 self.SaveWinSize(self.logwin, 'Debug')
741 if hasattr(self, 'logger'):
742 self.logger.canceled = True
743 del self.logger
744 if hasattr(self, 'logwin'):
745 self.logwin.Destroy()
746 del self.logwin
749 def LogClose(self, evt = None):
750 self.cfg.Write('/Debug/Show', 'no')
751 self.CloseLogWindow()
753 def TogglePhoneMenus(self, enable):
754 self.connected = enable
755 if enable:
756 self.SetStatusText(_('Connected'), 1)
757 if self.timer != None:
758 self.OnTimer()
759 else:
760 self.SetStatusText(_('Disconnected'), 1)
761 mb = self.menuBar
763 mb.Enable(201, not enable);
764 mb.Enable(202, enable);
766 mb.Enable(210, enable);
768 mb.Enable(250, enable);
770 mb.Enable(301, enable);
772 mb.Enable(310, enable);
773 mb.Enable(311, enable);
774 mb.Enable(312, enable);
776 mb.Enable(320, enable);
778 mb.Enable(330, enable);
780 mb.Enable(340, enable);
782 mb.Enable(350, enable);
784 mb.Enable(401, enable);
785 mb.Enable(402, enable);
786 mb.Enable(403, enable);
787 mb.Enable(404, enable);
789 mb.Enable(501, enable);
790 mb.Enable(502, enable);
791 mb.Enable(503, enable);
792 mb.Enable(504, enable);
794 mb.Enable(510, enable);
795 mb.Enable(511, enable);
797 def ActivateView(self, k1, k2):
798 self.tree.SelectItem(self.treei[k1][k2])
799 self.ChangeView(k1, k2)
801 def ChangeView(self, k1, k2):
802 self.ChangeBrowser(k1, k2)
803 self.righttitle.SetLabel(displaydata[k1][k2][2])
805 def ChangeBrowser(self, k1, k2):
806 self.type = [k1, k2]
807 if k2 == ' ':
808 data = []
809 for k3, v3 in self.values[k1].iteritems():
810 if k3 != '__':
811 data = data + v3
812 self.values[k1]['__'] = data
813 self.browser.Change(k1, data)
814 else:
815 self.browser.Change(k1, self.values[k1][k2])
816 self.browser.ShowRow(0)
818 def OnTreeSel(self, event):
819 item = event.GetItem()
820 for k1, v1 in self.treei.iteritems():
821 for k2, v2 in v1.iteritems():
822 if v2 == item:
823 self.ChangeView(k1, k2)
824 self.ClearSearch()
826 def OnSearch(self, event):
827 text = self.searchinput.GetValue()
828 type = self.searchchoice.GetSelection()
829 try:
830 self.browser.Filter(text, type)
831 self.searchinput.SetBackgroundColour(wx.NullColour)
832 except Wammu.Browser.FilterException:
833 self.searchinput.SetBackgroundColour(wx.RED)
835 def ClearSearch(self, event = None):
836 self.searchinput.SetValue('')
838 def Settings(self, event = None):
839 if self.connected:
840 connection_settings = {
841 'Connection': self.cfg.Read('/Gammu/Connection'),
842 'LockDevice': self.cfg.Read('/Gammu/LockDevice'),
843 'Device': self.cfg.Read('/Gammu/Device'),
844 'Model': self.cfg.Read('/Gammu/Model')
847 result = Wammu.Settings.Settings(self, self.cfg).ShowModal()
848 if result == wx.ID_OK:
849 if self.connected:
850 connection_settings_new = {
851 'Connection': self.cfg.Read('/Gammu/Connection'),
852 'LockDevice': self.cfg.Read('/Gammu/LockDevice'),
853 'Device': self.cfg.Read('/Gammu/Device'),
854 'Model': self.cfg.Read('/Gammu/Model')
857 if connection_settings != connection_settings_new:
858 wx.MessageDialog(self,
859 _('You changed parameters affecting phone connection, they will be used next time you connect to phone.'),
860 _('Notice'),
861 wx.OK | wx.ICON_INFORMATION).ShowModal()
862 self.DoDebug(self.cfg.Read('/Debug/Show'))
863 self.SetupNumberPrefix()
864 self.SetupStatusRefresh()
865 self.SetupTrayIcon()
867 def CloseWindow(self, event):
868 self.SaveWinSize(self, 'Main')
869 if hasattr(self, 'logwin'):
870 self.CloseLogWindow()
871 self.cfg.WriteInt('/Main/Split', self.splitter.GetSashPosition())
872 self.cfg.WriteInt('/Main/SplitRight', self.rightsplitter.GetSashPosition())
873 self.cfg.WriteInt('/Defaults/SearchType', self.searchchoice.GetCurrentSelection())
875 gammu.SetDebugFile(None)
876 gammu.SetDebugLevel('nothing')
878 self.logfilefd.close()
880 if hasattr(self, 'logger'):
881 self.logger.canceled = True
882 self.logger.join()
884 if hasattr(self, 'loggerdebug'):
885 self.loggerdebug.canceled = True
886 self.loggerdebug.join()
888 if self.tbicon is not None:
889 self.tbicon.Destroy()
891 if sys.platform != 'win32':
892 print ConsoleStrConv(
893 _('Looks like normal program termination, deleting log file.')
895 try:
896 os.unlink(self.logfilename)
897 except:
898 print ConsoleStrConv(
899 _('Failed to unlink temporary log file, please delete it yourself.')
901 print ConsoleStrConv(
902 _('Filename: %s') % self.logfilename
905 # Forcibily save configuration
906 self.cfg.Flush()
908 # tell the window to kill itself
909 self.Destroy()
911 def ShowError(self, info):
912 evt = Wammu.Events.ShowMessageEvent(
913 message = Wammu.Utils.FormatError(_('Error while communicating with phone'), info),
914 title = _('Error Occured'),
915 errortype = 'gammu',
916 type = wx.ICON_ERROR)
917 wx.PostEvent(self, evt)
919 def ShowProgress(self, text):
920 self.progress = wx.ProgressDialog(
921 _('Operation in progress'),
922 text,
923 100,
924 self,
925 wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_ESTIMATED_TIME)
927 def OnProgress(self, evt):
928 if hasattr(self, 'progress'):
929 if not self.progress.Update(evt.progress):
930 try:
931 evt.cancel()
932 except:
933 pass
934 if (evt.progress == 100):
935 del self.progress
936 if hasattr(evt, 'lock'):
937 evt.lock.release()
939 def OnException(self, evt):
940 Wammu.Error.Handler(*evt.data)
942 def OnData(self, evt):
943 self.values[evt.type[0]][evt.type[1]] = evt.data
944 if evt.last:
945 if hasattr(self, 'progress'):
946 self.progress.Update(100)
947 del self.progress
949 if hasattr(self, 'nextfun'):
950 f = self.nextfun
951 a = self.nextarg
952 del self.nextfun
953 del self.nextarg
954 f (*a)
956 def ShowData(self, data):
957 text = u''
958 if data is not None:
959 for d in data:
960 if len(d) == 2:
961 text += u'<b>%s</b>: %s<br>' % (d[0], d[1])
962 else:
963 text += u'<p>%s</p>' % d[0]
964 self.content.SetContent(text)
966 def OnShow(self, evt):
967 data = v = evt.data
968 if data is None:
969 pass
970 elif self.type == ['info',' ']:
971 data = [(evt.data['Name'], evt.data['Value'])]
972 elif self.type[0] == 'contact' or self.type[0] == 'call':
973 data = [
974 (_('Location'), str(v['Location'])),
975 (_('Memory type'), v['MemoryType'])]
976 for i in v['Entries']:
977 s = Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values, linkphone = False)
978 try:
979 if i['VoiceTag']:
980 s += ', ' + (_('voice tag %x') % i['VoiceTag'])
981 except:
982 pass
983 data.append((i['Type'], s))
984 elif self.type[0] == 'message':
985 data = [
986 (_('Number'), Wammu.Utils.GetNumberLink([] + self.values['contact']['ME'] + self.values['contact']['SM'], v['Number'])),
987 (_('Date'), StrConv(v['DateTime'])),
988 (_('Location'), StrConv(v['Location'])),
989 (_('Folder'), StrConv(v['SMS'][0]['Folder'])),
990 (_('Memory'), StrConv(v['SMS'][0]['Memory'])),
991 (_('SMSC'), Wammu.Utils.GetNumberLink([] + self.values['contact']['ME'] + self.values['contact']['SM'], v['SMS'][0]['SMSC']['Number'])),
992 (_('State'), StrConv(v['State']))]
993 if v['Name'] != '':
994 data.append((_('Name'), StrConv(v['Name'])))
995 data.append((Wammu.MessageDisplay.SmsToHtml(self.cfg, v),))
996 elif self.type[0] == 'todo':
997 data = [
998 (_('Location'), str(v['Location'])),
999 (_('Priority'), v['Priority']),
1000 (_('Type'), v['Type']),
1002 for i in v['Entries']:
1003 data.append((i['Type'], Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values)))
1004 elif self.type[0] == 'calendar':
1005 data = [
1006 (_('Location'), str(v['Location'])),
1007 (_('Type'), v['Type']),
1009 for i in v['Entries']:
1010 data.append((i['Type'], Wammu.Utils.GetTypeString(i['Type'], i['Value'], self.values)))
1011 else:
1012 data = [('Show not yet implemented! (type = %s)' % self.type[0])]
1013 self.ShowData(data)
1015 def NewContact(self, evt):
1016 self.EditContact({})
1018 def NewCalendar(self, evt):
1019 self.EditCalendar({})
1021 def NewTodo(self, evt):
1022 self.EditTodo({})
1024 def NewMessage(self, evt):
1025 self.ComposeMessage({})
1027 def ComposeMessage(self, v, action = 'save'):
1028 if Wammu.Composer.SMSComposer(self, self.cfg, v, self.values, action).ShowModal() == wx.ID_OK:
1030 if len(v['Numbers']) == 0:
1031 v['Numbers'] = ['Wammu']
1033 for number in v['Numbers']:
1034 busy = wx.BusyInfo(_('Writing message(s)...'))
1035 time.sleep(0.1)
1036 wx.Yield()
1037 v['Number'] = number
1038 v['SMS'] = gammu.EncodeSMS(v['SMSInfo'])
1040 if v['Save']:
1041 result = {}
1042 result['SMS'] = []
1044 try:
1045 for msg in v['SMS']:
1046 msg['SMSC']['Location'] = 1
1048 msg['Folder'] = v['Folder']
1049 msg['Number'] = v['Number']
1050 msg['Type'] = v['Type']
1051 msg['State'] = v['State']
1053 if v['Save']:
1054 (msg['Location'], msg['Folder']) = self.sm.AddSMS(msg)
1055 if v['Send']:
1056 # When sending of saved message fails, send it directly:
1057 try:
1058 msg['MessageReference'] = self.sm.SendSavedSMS(0, msg['Location'])
1059 except gammu.GSMError:
1060 msg['MessageReference'] = self.sm.SendSMS(msg)
1061 try:
1062 result['SMS'].append(self.sm.GetSMS(0, msg['Location'])[0])
1063 except gammu.ERR_EMPTY:
1064 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()
1065 elif v['Send']:
1066 msg['MessageReference'] = self.sm.SendSMS(msg)
1068 if v['Save']:
1069 info = gammu.DecodeSMS(result['SMS'])
1070 if info != None:
1071 result['SMSInfo'] = info
1072 Wammu.Utils.ParseMessage(result, (info != None))
1073 result['Synced'] = True
1074 self.values['message'][result['State']].append(result)
1076 except gammu.GSMError, val:
1077 del busy
1078 self.ShowError(val[0])
1080 if v['Save']:
1081 try:
1082 self.ActivateView('message', result['State'])
1083 self.browser.ShowLocation(result['Location'])
1084 except KeyError:
1085 pass
1087 def EditContact(self, v):
1088 backup = copy.deepcopy(v)
1089 shoulddelete = (v == {} or v['Location'] == 0)
1090 if Wammu.Editor.ContactEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1091 try:
1092 busy = wx.BusyInfo(_('Writing contact...'))
1093 time.sleep(0.1)
1094 wx.Yield()
1095 # was entry moved => delete it from internal list
1096 if not shoulddelete:
1097 for idx in range(len(self.values['contact'][backup['MemoryType']])):
1098 if self.values['contact'][backup['MemoryType']][idx] == v:
1099 del self.values['contact'][backup['MemoryType']][idx]
1100 break
1102 # have we specified location? => add or set
1103 if v['Location'] == 0:
1104 v['Location'] = self.sm.AddMemory(v)
1105 else:
1106 try:
1107 v['Location'] = self.sm.SetMemory(v)
1108 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1109 v['Location'] = self.sm.AddMemory(v)
1111 # was entry moved => delete it from phone
1112 if not shoulddelete:
1113 if v['MemoryType'] != backup['MemoryType'] or v['Location'] != backup['Location']:
1114 # delete from phone
1115 self.sm.DeleteMemory(backup['MemoryType'], backup['Location'])
1117 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1118 try:
1119 attempts = 0
1120 while attempts < 10:
1121 try:
1122 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1123 break
1124 except gammu.ERR_EMPTY:
1125 # some phones need time till entry appears
1126 attempts = attempts + 1
1127 time.sleep(0.2)
1128 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1129 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()
1130 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1131 v['Synced'] = True
1132 # append new value to list
1133 self.values['contact'][v['MemoryType']].append(v)
1135 except gammu.GSMError, val:
1136 del busy
1137 v = backup
1138 self.ShowError(val[0])
1140 if (self.type[0] == 'contact' and self.type[1] == ' ') or not v.has_key('MemoryType'):
1141 self.ActivateView('contact', ' ')
1142 try:
1143 self.browser.ShowLocation(v['Location'], ('MemoryType', v['MemoryType']))
1144 except KeyError:
1145 pass
1146 else:
1147 self.ActivateView('contact', v['MemoryType'])
1148 try:
1149 self.browser.ShowLocation(v['Location'])
1150 except KeyError:
1151 pass
1153 def EditCalendar(self, v):
1154 backup = copy.deepcopy(v)
1155 shoulddelete = (v == {} or v['Location'] == 0)
1156 if Wammu.Editor.CalendarEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1157 try:
1158 busy = wx.BusyInfo(_('Writing calendar...'))
1159 time.sleep(0.1)
1160 wx.Yield()
1161 # was entry moved => delete it from internal list
1162 if not shoulddelete:
1163 # delete from internal list
1164 for idx in range(len(self.values['calendar'][' '])):
1165 if self.values['calendar'][' '][idx] == v:
1166 del self.values['calendar'][' '][idx]
1167 break
1169 # have we specified location? => add or set
1170 if v['Location'] == 0:
1171 v['Location'] = self.sm.AddCalendar(v)
1172 else:
1173 try:
1174 v['Location'] = self.sm.SetCalendar(v)
1175 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1176 v['Location'] = self.sm.AddCalendar(v)
1178 # was entry moved => delete it from phone
1179 if not shoulddelete:
1180 if v['Location'] != backup['Location']:
1181 # delete from phone
1182 self.sm.DeleteCalendar(backup['Location'])
1184 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1185 try:
1186 v = self.sm.GetCalendar(v['Location'])
1187 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1188 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()
1189 Wammu.Utils.ParseCalendar(v)
1190 v['Synced'] = True
1191 # append new value to list
1192 self.values['calendar'][' '].append(v)
1194 except gammu.GSMError, val:
1195 del busy
1196 v = backup
1197 self.ShowError(val[0])
1199 self.ActivateView('calendar', ' ')
1200 try:
1201 self.browser.ShowLocation(v['Location'])
1202 except KeyError:
1203 pass
1205 def EditTodo(self, v):
1206 backup = copy.deepcopy(v)
1207 shoulddelete = (v == {} or v['Location'] == 0)
1208 if Wammu.Editor.TodoEditor(self, self.cfg, self.values, v).ShowModal() == wx.ID_OK:
1209 try:
1210 busy = wx.BusyInfo(_('Writing todo...'))
1211 time.sleep(0.1)
1212 wx.Yield()
1213 # was entry moved => delete it from internal list
1214 if not shoulddelete:
1215 for idx in range(len(self.values['todo'][' '])):
1216 if self.values['todo'][' '][idx] == v:
1217 del self.values['todo'][' '][idx]
1218 break
1220 # have we specified location? => add or set
1221 if v['Location'] == 0:
1222 v['Location'] = self.sm.AddToDo(v)
1223 else:
1224 try:
1225 v['Location'] = self.sm.SetToDo(v)
1226 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1227 v['Location'] = self.sm.AddToDo(v)
1229 # was entry moved => delete it from phone
1230 if not shoulddelete:
1231 if v['Location'] != backup['Location']:
1232 # delete from phone
1233 self.sm.DeleteToDo(backup['Location'])
1235 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1236 try:
1237 v = self.sm.GetToDo(v['Location'])
1238 except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
1239 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()
1240 Wammu.Utils.ParseTodo(v)
1241 v['Synced'] = True
1242 # append new value to list
1243 self.values['todo'][' '].append(v)
1244 except gammu.GSMError, val:
1245 del busy
1246 v = backup
1247 self.ShowError(val[0])
1249 self.ActivateView('todo', ' ')
1250 try:
1251 self.browser.ShowLocation(v['Location'])
1252 except KeyError:
1253 pass
1256 def OnEdit(self, evt):
1257 if evt.data != {} and not evt.data['Synced']:
1258 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()
1259 return
1260 if self.type[0] == 'contact':
1261 self.EditContact(evt.data)
1262 elif self.type[0] == 'calendar':
1263 self.EditCalendar(evt.data)
1264 elif self.type[0] == 'todo':
1265 self.EditTodo(evt.data)
1266 else:
1267 print 'Edit not yet implemented (type = %s)!' % self.type[0]
1269 def OnReply(self, evt):
1270 if self.type[0] == 'message':
1271 self.ComposeMessage({'Number': evt.data['Number']}, action = 'send')
1272 else:
1273 print 'Reply not yet implemented!'
1274 print evt.index
1276 def OnCall(self, evt):
1277 if self.type[0] in ['call', 'contact']:
1278 num = Wammu.Select.SelectContactNumber(self, evt.data)
1279 if num == None:
1280 return
1282 try:
1283 self.sm.DialVoice(num)
1284 except gammu.GSMError, val:
1285 self.ShowError(val[0])
1286 elif self.type[0] == 'message':
1287 try:
1288 self.sm.DialVoice(evt.data['Number'])
1289 except gammu.GSMError, val:
1290 self.ShowError(val[0])
1291 else:
1292 print 'Call not yet implemented (type = %s)!' % self.type[0]
1294 def OnMessage(self, evt):
1295 if self.type[0] in ['call', 'contact']:
1297 num = Wammu.Select.SelectContactNumber(self, evt.data)
1298 if num == None:
1299 return
1300 self.ComposeMessage({'Number': num}, action = 'send')
1301 elif self.type[0] == 'message':
1302 self.ComposeMessage({'Number': evt.data['Number']}, action = 'send')
1303 else:
1304 print 'Message send not yet implemented (type = %s)!' % self.type[0]
1306 def OnDuplicate(self, evt):
1307 if evt.data != {} and not evt.data['Synced']:
1308 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()
1309 return
1310 v = copy.deepcopy(evt.data)
1311 if self.type[0] == 'contact':
1312 v['Location'] = 0
1313 self.EditContact(v)
1314 elif self.type[0] == 'calendar':
1315 v['Location'] = 0
1316 self.EditCalendar(v)
1317 elif self.type[0] == 'todo':
1318 v['Location'] = 0
1319 self.EditTodo(v)
1320 elif self.type[0] == 'message':
1321 self.ComposeMessage(v)
1322 else:
1323 print 'Duplicate not yet implemented (type = %s)!' % self.type[0]
1326 def OnSend(self, evt):
1327 if evt.data != {} and not evt.data['Synced']:
1328 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()
1329 return
1330 if self.type[0] == 'message':
1331 v = evt.data
1332 try:
1333 try:
1334 for loc in v['Location'].split(', '):
1335 self.sm.SendSavedSMS(0, int(loc))
1336 except gammu.ERR_NOTSUPPORTED:
1337 for msg in v['SMS']:
1338 self.sm.SendSMS(msg)
1339 except gammu.GSMError, val:
1340 self.ShowError(val[0])
1342 def SMSToMails(self, evt):
1343 messages = self.values['message']['Read'] + \
1344 self.values['message']['UnRead'] + \
1345 self.values['message']['Sent'] + \
1346 self.values['message']['UnSent']
1347 contacts = self.values['contact']['ME'] + \
1348 self.values['contact']['SM']
1349 Wammu.SMSExport.SMSExport(self, messages, contacts)
1351 def SMSToXML(self, evt):
1352 messages = self.values['message']['Read'] + \
1353 self.values['message']['UnRead'] + \
1354 self.values['message']['Sent'] + \
1355 self.values['message']['UnSent']
1356 contacts = self.values['contact']['ME'] + \
1357 self.values['contact']['SM']
1358 Wammu.SMSXML.SMSExportXML(self, messages, contacts)
1360 def SelectBackupFile(self, type, save = True, data = False):
1361 wildcard = ''
1362 if type == 'message':
1363 wildcard += _('Gammu messages backup') + ' (*.smsbackup)|*.smsbackup|'
1364 exts = ['smsbackup']
1365 else:
1366 if not save:
1367 wildcard += _('All backup formats') + '|*.backup;*.lmb;*.vcf;*.ldif;*.vcs;*.ics|'
1369 wildcard += _('Gammu backup [all data]') + ' (*.backup)|*.backup|'
1370 exts = ['backup']
1372 if type in ['contact', 'all']:
1373 wildcard += _('Nokia backup [contacts]') + ' (*.lmb)|*.lmb|'
1374 exts.append('lmb')
1375 if type in ['contact', 'all']:
1376 wildcard += _('vCard [contacts]') + ' (*.vcf)|*.vcf|'
1377 exts.append('vcf')
1378 if type in ['contact', 'all']:
1379 wildcard += _('LDIF [contacts]') + ' (*.ldif)|*.ldif|'
1380 exts.append('ldif')
1381 if type in ['todo', 'calendar', 'all']:
1382 wildcard += _('vCalendar [todo,calendar]') + ' (*.vcs)|*.vcs|'
1383 exts.append('vcs')
1384 if type in ['todo', 'calendar', 'all']:
1385 wildcard += _('iCalendar [todo,calendar]') + ' (*.ics)|*.ics|'
1386 exts.append('ics')
1388 wildcard += _('All files') + ' (*.*)|*.*'
1389 exts.append(None)
1391 if data:
1392 if save:
1393 dlg = wx.FileDialog(self, _('Save data as...'), os.getcwd(), "", wildcard, wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
1394 else:
1395 dlg = wx.FileDialog(self, _('Read data'), os.getcwd(), "", wildcard, wx.OPEN|wx.CHANGE_DIR)
1396 else:
1397 if save:
1398 dlg = wx.FileDialog(self, _('Save backup as...'), os.getcwd(), "", wildcard, wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
1399 else:
1400 dlg = wx.FileDialog(self, _('Import backup'), os.getcwd(), "", wildcard, wx.OPEN|wx.CHANGE_DIR)
1401 if dlg.ShowModal() == wx.ID_OK:
1402 path = dlg.GetPath()
1403 if save:
1404 ext = exts[dlg.GetFilterIndex()]
1405 # Add automatic extension if we know one and file does not
1406 # have any
1407 if (os.path.splitext(path)[1] == '' and
1408 ext is not None):
1409 path += '.' + ext
1410 return Wammu.Locales.ConsoleStrConv(path)
1411 return None
1413 def ReadBackup(self, type, data = False):
1414 filename = self.SelectBackupFile(type, save = False, data = data)
1415 if filename == None:
1416 return (None, None)
1417 try:
1418 if type == 'message':
1419 backup = gammu.ReadSMSBackup(filename)
1420 else:
1421 backup = gammu.ReadBackup(filename)
1422 except gammu.GSMError, val:
1423 info = val[0]
1424 evt = Wammu.Events.ShowMessageEvent(
1425 message = Wammu.Utils.FormatError(_('Error while reading backup'), info),
1426 title = _('Error Occured'),
1427 errortype = 'gammu',
1428 type = wx.ICON_ERROR)
1429 wx.PostEvent(self, evt)
1430 return (None, None)
1431 return (filename, backup)
1433 def ReadData(self, evt):
1434 (filename, backup) = self.ReadBackup('all', True)
1435 if backup == None:
1436 return
1438 if len(backup['PhonePhonebook']) > 0:
1439 self.values['contact']['ME'] = map(Wammu.Utils.ParseMemoryEntry, backup['PhonePhonebook'], [self.cfg] * len(backup['PhonePhonebook']))
1440 if len(backup['SIMPhonebook']) > 0:
1441 self.values['contact']['SM'] = map(Wammu.Utils.ParseMemoryEntry, backup['SIMPhonebook'], [self.cfg] * len(backup['SIMPhonebook']))
1442 if len(backup['ToDo']) > 0:
1443 self.values['todo'][' '] = map(Wammu.Utils.ParseTodo, backup['ToDo'])
1444 if len(backup['Calendar']) > 0:
1445 self.values['calendar'][' '] = map(Wammu.Utils.ParseCalendar, backup['Calendar'])
1447 self.ActivateView('contact', ' ')
1449 self.SetStatusText(_('Data has been read from file "%s"') % StrConv(filename))
1451 def ReadSMSData(self, evt):
1452 (filename, backup) = self.ReadBackup('message', True)
1453 if backup == None:
1454 return
1456 res = Wammu.Utils.ProcessMessages(map(lambda x:[x], backup), False)
1458 self.values['message']['Sent'] = res['sent']
1459 self.values['message']['UnSent'] = res['unsent']
1460 self.values['message']['Read'] = res['read']
1461 self.values['message']['UnRead'] = res['unread']
1463 self.ActivateView('message', ' ')
1465 self.SetStatusText(_('Data has been read from file "%s"') % StrConv(filename))
1467 def ImportSMS(self, evt):
1468 (filename, backup) = self.ReadBackup('message')
1469 if backup == None:
1470 return
1471 choices = []
1472 values = []
1473 if len(backup) > 0:
1474 values.append('message')
1475 choices.append(_('%d messages') % len(backup))
1477 if len(values) == 0:
1478 wx.MessageDialog(self,
1479 _('No importable data were found in file "%s"') % strconv(filename),
1480 _('No data to import'),
1481 wx.OK | wx.ICON_INFORMATION).ShowModal()
1482 return
1484 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'),
1485 choices,style = wx.CHOICEDLG_STYLE | wx.RESIZE_BORDER,
1486 size = (600, 200))
1487 if dlg.ShowModal() != wx.ID_OK:
1488 return
1490 lst = dlg.GetValue()
1491 if len(lst) == 0:
1492 return
1494 try:
1495 busy = wx.BusyInfo(_('Importing data...'))
1496 time.sleep(0.1)
1497 wx.Yield()
1498 for i in lst:
1499 datatype = values[i]
1500 if datatype == 'message':
1501 smsl = []
1502 for v in backup:
1503 v['SMSC']['Location'] = 1
1504 (v['Location'], v['Folder']) = self.sm.AddSMS(v)
1505 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1506 v = self.sm.GetSMS(0, v['Location'])
1507 smsl.append(v)
1509 res = Wammu.Utils.ProcessMessages(smsl, True)
1511 self.values['message']['Sent'] += res['sent']
1512 self.values['message']['UnSent'] += res['unsent']
1513 self.values['message']['Read'] += res['read']
1514 self.values['message']['UnRead'] += res['unread']
1516 self.ActivateView('message', ' ')
1518 del busy
1519 wx.Yield()
1521 wx.MessageDialog(self,
1522 _('Backup has been imported from file "%s"') % StrConv(filename),
1523 _('Backup imported'),
1524 wx.OK | wx.ICON_INFORMATION).ShowModal()
1526 except gammu.GSMError, val:
1527 self.ShowError(val[0])
1529 wx.MessageDialog(self,
1530 _('Restoring from file "%s" has failed, some parts of backup might have been stored to phone and some were not.') % StrConv(filename),
1531 _('Backup import failed'),
1532 wx.OK | wx.ICON_INFORMATION).ShowModal()
1534 def Import(self, evt):
1535 (filename, backup) = self.ReadBackup('all')
1536 if backup == None:
1537 return
1538 choices = []
1539 values = []
1540 if len(backup['PhonePhonebook']) > 0:
1541 values.append('PhonePhonebook')
1542 choices.append(_('%d phone contact entries') % len(backup['PhonePhonebook']))
1543 if len(backup['SIMPhonebook']) > 0:
1544 values.append('SIMPhonebook')
1545 choices.append(_('%d SIM contact entries') % len(backup['SIMPhonebook']))
1546 if len(backup['ToDo']) > 0:
1547 values.append('ToDo')
1548 choices.append(_('%d to do entries') % len(backup['ToDo']))
1549 if len(backup['Calendar']) > 0:
1550 values.append('Calendar')
1551 choices.append(_('%d calendar entries') % len(backup['Calendar']))
1553 if len(values) == 0:
1554 wx.MessageDialog(self,
1555 _('No importable data were found in file "%s"') % StrConv(filename),
1556 _('No data to import'),
1557 wx.OK | wx.ICON_INFORMATION).ShowModal()
1558 return
1560 msg = ''
1561 if backup['Model'] != '':
1562 msg = '\n \n' + _('Backup saved from phone %s') % backup['Model']
1563 if backup['IMEI'] != '':
1564 msg += _(', serial number %s') % backup['IMEI']
1565 if backup['Creator'] != '':
1566 msg += '\n \n' + _('Backup was created by %s') % backup['Creator']
1567 if backup['DateTime'] != None:
1568 msg += '\n \n' + _('Backup saved on %s') % str(backup['DateTime'])
1570 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'),
1571 choices,style = wx.CHOICEDLG_STYLE | wx.RESIZE_BORDER,
1572 size = (600, 200))
1573 if dlg.ShowModal() != wx.ID_OK:
1574 return
1576 lst = dlg.GetValue()
1577 if len(lst) == 0:
1578 return
1580 try:
1581 busy = wx.BusyInfo(_('Importing data...'))
1582 time.sleep(0.1)
1583 wx.Yield()
1584 for i in lst:
1585 datatype = values[i]
1586 if datatype == 'PhonePhonebook':
1587 for v in backup['PhonePhonebook']:
1588 v['Location'] = self.sm.AddMemory(v)
1589 time.sleep(0.5)
1590 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1591 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1592 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1593 v['Synced'] = True
1594 # append new value to list
1595 self.values['contact'][v['MemoryType']].append(v)
1596 self.ActivateView('contact', 'ME')
1597 elif datatype == 'SIMPhonebook':
1598 for v in backup['SIMPhonebook']:
1599 v['Location'] = self.sm.AddMemory(v)
1600 time.sleep(0.5)
1601 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1602 v = self.sm.GetMemory(v['MemoryType'], v['Location'])
1603 Wammu.Utils.ParseMemoryEntry(v, self.cfg)
1604 v['Synced'] = True
1605 # append new value to list
1606 self.values['contact'][v['MemoryType']].append(v)
1607 self.ActivateView('contact', 'SM')
1608 elif datatype == 'ToDo':
1609 for v in backup['ToDo']:
1610 v['Location'] = self.sm.AddToDo(v)
1611 time.sleep(0.5)
1612 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1613 v = self.sm.GetToDo(v['Location'])
1614 Wammu.Utils.ParseTodo(v)
1615 v['Synced'] = True
1616 # append new value to list
1617 self.values['todo'][' '].append(v)
1618 self.ActivateView('todo', ' ')
1619 elif datatype == 'Calendar':
1620 for v in backup['Calendar']:
1621 v['Location'] = self.sm.AddCalendar(v)
1622 time.sleep(0.5)
1623 # reread entry (it doesn't have to contain exactly same data as entered, it depends on phone features)
1624 v = self.sm.GetCalendar(v['Location'])
1625 Wammu.Utils.ParseCalendar(v)
1626 v['Synced'] = True
1627 # append new value to list
1628 self.values['calendar'][' '].append(v)
1629 self.ActivateView('calendar', ' ')
1631 del busy
1632 wx.Yield()
1634 wx.MessageDialog(self,
1635 _('Backup has been imported from file "%s"') % StrConv(filename),
1636 _('Backup imported'),
1637 wx.OK | wx.ICON_INFORMATION).ShowModal()
1639 except gammu.GSMError, val:
1640 self.ShowError(val[0])
1642 wx.MessageDialog(self,
1643 _('Restoring from file "%s" has failed, some parts of backup might have been stored to phone and some were not.') % StrConv(filename),
1644 _('Backup import failed'),
1645 wx.OK | wx.ICON_INFORMATION).ShowModal()
1647 def WriteData(self, evt):
1648 self.DoBackup(True, 'all')
1650 def WriteSMSData(self, evt):
1651 self.DoBackup(True, 'message')
1653 def Backup(self, evt):
1654 self.DoBackup(False, 'all')
1656 def BackupSMS(self, evt):
1657 self.DoBackup(False, 'message')
1659 def PrepareBackup(self):
1660 backup = {}
1661 backup['Creator'] = 'Wammu ' + Wammu.__version__
1662 backup['IMEI'] = self.IMEI
1663 backup['Model'] = '%s %s %s' % ( self.Manufacturer, self.Model, self.Version)
1664 return backup
1666 def WriteBackup(self, filename, type, backup, data = False):
1667 try:
1668 if type == 'message':
1669 # Backup is here our internal SMS list: [{'SMS':[{sms1}, {sms2}]}, ...]
1670 data = map(lambda x:x['SMS'], backup)
1671 backup = []
1672 for x in data:
1673 backup += x
1674 gammu.SaveSMSBackup(filename, backup)
1675 else:
1676 gammu.SaveBackup(filename, backup)
1677 if data:
1678 self.SetStatusText(_('Backup has been saved to file "%s"') % StrConv(filename))
1679 else:
1680 self.SetStatusText(_('Data has been saved to file "%s"') % StrConv(filename))
1681 except gammu.GSMError, val:
1682 info = val[0]
1683 evt = Wammu.Events.ShowMessageEvent(
1684 message = Wammu.Utils.FormatError(_('Error while saving backup'), info),
1685 title = _('Error Occured'),
1686 errortype = 'gammu',
1687 type = wx.ICON_ERROR)
1688 wx.PostEvent(self, evt)
1689 except MemoryError, val:
1690 info = val[0]
1691 evt = Wammu.Events.ShowMessageEvent(
1692 message = _('Error while saving backup, probably some limit inside of Gammu exceeded.\n%s') % str(info),
1693 title = _('Error Occured'),
1694 type = wx.ICON_ERROR)
1695 wx.PostEvent(self, evt)
1697 def DoBackup(self, data, type):
1698 filename = self.SelectBackupFile(type, data = data)
1699 if filename == None:
1700 return
1701 ext = os.path.splitext(filename)[1].lower()
1703 if type == 'message':
1704 backup = self.values['message']['Read'] + self.values['message']['UnRead'] + self.values['message']['Sent'] + self.values['message']['UnSent']
1705 else:
1706 backup = self.PrepareBackup()
1707 if ext in ['.vcf', '.ldif']:
1708 # these support only one phonebook, so merged it
1709 backup['PhonePhonebook'] = self.values['contact']['ME'] + self.values['contact']['SM']
1710 else:
1711 backup['PhonePhonebook'] = self.values['contact']['ME']
1712 backup['SIMPhonebook'] = self.values['contact']['SM']
1714 backup['ToDo'] = self.values['todo'][' ']
1715 backup['Calendar'] = self.values['calendar'][' ']
1716 self.WriteBackup(filename, type, backup, data)
1719 def OnBackup(self, evt):
1720 filename = self.SelectBackupFile(self.type[0])
1721 if filename == None:
1722 return
1723 ext = os.path.splitext(filename)[1].lower()
1724 lst = evt.lst
1725 if self.type[0] == 'message':
1726 backup = lst
1727 else:
1728 backup = self.PrepareBackup()
1729 if self.type[0] == 'contact':
1730 if ext in ['.vcf', '.ldif']:
1731 # these support only one phonebook, so keep it merged
1732 backup['PhonePhonebook'] = lst
1733 else:
1734 sim = []
1735 phone = []
1736 for item in lst:
1737 if item['MemoryType'] == 'SM':
1738 sim.append(item)
1739 elif item['MemoryType'] == 'ME':
1740 phone.append(item)
1741 backup['PhonePhonebook'] = phone
1742 backup['SIMPhonebook'] = sim
1743 elif self.type[0] == 'todo':
1744 backup['ToDo'] = lst
1745 elif self.type[0] == 'calendar':
1746 backup['Calendar'] = lst
1748 self.WriteBackup(filename, self.type[0], backup)
1750 def OnDelete(self, evt):
1751 # first check on supported types
1752 if not self.type[0] in ['contact', 'call', 'message', 'todo', 'calendar']:
1753 print 'Delete not yet implemented! (items to delete = %s, type = %s)' % (str(evt.lst), self.type[0])
1754 return
1756 lst = evt.lst
1758 if len(lst) == 0:
1759 # nothing to delete
1760 return
1762 if not lst[0]['Synced']:
1763 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()
1764 return
1766 # check for confirmation
1767 if self.cfg.Read('/Wammu/ConfirmDelete') == 'yes':
1768 count = len(lst)
1769 if count == 1:
1770 v = lst[0]
1771 if self.type[0] == 'contact':
1772 txt = _('Are you sure you want to delete contact "%s"?') % v['Name']
1773 elif self.type[0] == 'call':
1774 txt = _('Are you sure you want to delete call from "%s"?') % v['Name']
1775 elif self.type[0] == 'message':
1776 txt = _('Are you sure you want to delete message from "%s"?') % v['Number']
1777 elif self.type[0] == 'todo':
1778 txt = _('Are you sure you want to delete todo entry "%s"?') % v['Text']
1779 elif self.type[0] == 'calendar':
1780 txt = _('Are you sure you want to delete calendar entry "%s"?') % v['Text']
1781 else:
1782 if self.type[0] == 'contact':
1783 txt = Wammu.Locales.ngettext(
1784 'Are you sure you want to delete %d contact?',
1785 'Are you sure you want to delete %d contacts?',
1786 count) % count
1787 elif self.type[0] == 'call':
1788 txt = Wammu.Locales.ngettext(
1789 'Are you sure you want to delete %d call?',
1790 'Are you sure you want to delete %d calls?',
1791 count) % count
1792 elif self.type[0] == 'message':
1793 txt = Wammu.Locales.ngettext(
1794 'Are you sure you want to delete %d message?',
1795 'Are you sure you want to delete %d messages?',
1796 count) % count
1797 elif self.type[0] == 'todo':
1798 txt = Wammu.Locales.ngettext(
1799 'Are you sure you want to delete %d todo entry?',
1800 'Are you sure you want to delete %d todo entries?',
1801 count) % count
1802 elif self.type[0] == 'calendar':
1803 txt = Wammu.Locales.ngettext(
1804 'Are you sure you want to delete %d calendar entry?',
1805 'Are you sure you want to delete %d calendar entries?',
1806 count) % count
1807 dlg = wx.MessageDialog(self,
1808 txt,
1809 _('Confirm deleting'),
1810 wx.OK | wx.CANCEL | wx.ICON_WARNING)
1811 if dlg.ShowModal() != wx.ID_OK:
1812 return
1814 # do real delete
1815 try:
1816 if self.type[0] == 'contact' or self.type[0] == 'call':
1817 busy = wx.BusyInfo(_('Deleting contact(s)...'))
1818 time.sleep(0.1)
1819 wx.Yield()
1820 for v in lst:
1821 self.sm.DeleteMemory(v['MemoryType'], v['Location'])
1822 for idx in range(len(self.values[self.type[0]][v['MemoryType']])):
1823 if self.values[self.type[0]][v['MemoryType']][idx] == v:
1824 del self.values[self.type[0]][v['MemoryType']][idx]
1825 break
1826 elif self.type[0] == 'message':
1827 busy = wx.BusyInfo(_('Deleting message(s)...'))
1828 time.sleep(0.1)
1829 wx.Yield()
1830 for v in lst:
1831 for loc in v['Location'].split(', '):
1832 self.sm.DeleteSMS(0, int(loc))
1833 for idx in range(len(self.values[self.type[0]][v['State']])):
1834 if self.values[self.type[0]][v['State']][idx] == v:
1835 del self.values[self.type[0]][v['State']][idx]
1836 break
1837 elif self.type[0] == 'todo':
1838 busy = wx.BusyInfo(_('Deleting todo(s)...'))
1839 time.sleep(0.1)
1840 wx.Yield()
1841 for v in lst:
1842 self.sm.DeleteToDo(v['Location'])
1843 for idx in range(len(self.values[self.type[0]][' '])):
1844 if self.values[self.type[0]][' '][idx] == v:
1845 del self.values[self.type[0]][' '][idx]
1846 break
1847 elif self.type[0] == 'calendar':
1848 busy = wx.BusyInfo(_('Deleting calendar event(s)...'))
1849 time.sleep(0.1)
1850 wx.Yield()
1851 for v in lst:
1852 self.sm.DeleteCalendar(v['Location'])
1853 for idx in range(len(self.values[self.type[0]][' '])):
1854 if self.values[self.type[0]][' '][idx] == v:
1855 del self.values[self.type[0]][' '][idx]
1856 break
1857 except gammu.GSMError, val:
1858 try:
1859 del busy
1860 finally:
1861 self.ShowError(val[0])
1863 self.ActivateView(self.type[0], self.type[1])
1865 def OnLink(self, evt):
1866 v = evt.link.split('://')
1867 if len(v) != 2:
1868 print 'Bad URL!'
1869 return
1870 if v[0] == 'memory':
1871 t = v[1].split('/')
1872 if len(t) != 2:
1873 print 'Bad URL!'
1874 return
1876 if t[0] in ['ME', 'SM']:
1877 self.ActivateView('contact', t[0])
1878 try:
1879 self.browser.ShowLocation(int(t[1]))
1880 except KeyError:
1881 pass
1883 elif t[0] in ['MC', 'RC', 'DC']:
1884 self.ActivateView('call', t[0])
1885 try:
1886 self.browser.ShowLocation(int(t[1]))
1887 except KeyError:
1888 pass
1890 else:
1891 print 'Not supported memory type "%s"' % t[0]
1892 return
1893 else:
1894 print 'This link not yet implemented: "%s"' % evt.link
1896 def OnShowMessage(self, evt):
1897 try:
1898 if self.progress.IsShown():
1899 parent = self.progress
1900 else:
1901 parent = self
1902 except:
1903 parent = self
1905 # Is is Gammu error?
1906 if hasattr(evt, 'errortype') and evt.errortype == 'gammu':
1907 Wammu.ErrorMessage.ErrorMessage(parent,
1908 StrConv(evt.message),
1909 StrConv(evt.title)).ShowModal()
1910 else:
1911 wx.MessageDialog(parent,
1912 StrConv(evt.message),
1913 StrConv(evt.title),
1914 wx.OK | evt.type).ShowModal()
1916 if hasattr(evt, 'lock'):
1917 evt.lock.release()
1919 def ShowInfo(self, event):
1920 self.ShowProgress(_('Reading phone information'))
1921 Wammu.Info.GetInfo(self, self.sm).start()
1922 self.nextfun = self.ActivateView
1923 self.nextarg = ('info', ' ')
1926 # Calls
1929 def ShowCalls(self, event):
1930 self.GetCallsType('MC')
1931 self.nextfun = self.ShowCalls2
1932 self.nextarg = ()
1934 def ShowCalls2(self):
1935 self.GetCallsType('DC')
1936 self.nextfun = self.ShowCalls3
1937 self.nextarg = ()
1939 def ShowCalls3(self):
1940 self.GetCallsType('RC')
1941 self.nextfun = self.ActivateView
1942 self.nextarg = ('call', ' ')
1944 def GetCallsType(self, type):
1945 self.ShowProgress(_('Reading calls of type %s') % type)
1946 Wammu.Memory.GetMemory(self, self.sm, 'call', type).start()
1949 # Contacts
1952 def ShowContacts(self, event):
1953 self.GetContactsType('SM')
1954 self.nextfun = self.ShowContacts2
1955 self.nextarg = ()
1957 def ShowContacts2(self):
1958 self.GetContactsType('ME')
1959 self.nextfun = self.ActivateView
1960 self.nextarg = ('contact', ' ')
1962 def ShowContactsME(self, event):
1963 self.GetContactsType('ME')
1964 self.nextfun = self.ActivateView
1965 self.nextarg = ('contact', 'ME')
1967 def ShowContactsSM(self, event):
1968 self.GetContactsType('SM')
1969 self.nextfun = self.ActivateView
1970 self.nextarg = ('contact', 'SM')
1972 def GetContactsType(self, type):
1973 self.ShowProgress(_('Reading contacts from %s') % type)
1974 Wammu.Memory.GetMemory(self, self.sm, 'contact', type).start()
1977 # Messages
1980 def ShowMessages(self, event):
1981 self.ShowProgress(_('Reading messages'))
1982 Wammu.Message.GetMessage(self, self.sm).start()
1983 self.nextfun = self.ActivateView
1984 self.nextarg = ('message', ' ')
1987 # Todos
1990 def ShowTodos(self, event):
1991 self.ShowProgress(_('Reading todos'))
1992 Wammu.Todo.GetTodo(self, self.sm).start()
1993 self.nextfun = self.ActivateView
1994 self.nextarg = ('todo', ' ')
1997 # Calendars
2000 def ShowCalendar(self, event):
2001 self.ShowProgress(_('Reading calendar'))
2002 Wammu.Calendar.GetCalendar(self, self.sm).start()
2003 self.nextfun = self.ActivateView
2004 self.nextarg = ('calendar', ' ')
2007 # Time
2010 def SyncTime(self, event):
2011 busy = wx.BusyInfo(_('Setting time in phone...'))
2012 time.sleep(0.1)
2013 wx.Yield()
2014 try:
2015 self.sm.SetDateTime(datetime.datetime.now())
2016 except gammu.GSMError, val:
2017 del busy
2018 self.ShowError(val[0])
2021 # Files
2024 def SendFile(self, event):
2026 Sends file to phone.
2028 @todo: Maybe we could add some wildcards for commonly used file types.
2030 dlg = wx.FileDialog(self, _('Send file to phone'), os.getcwd(), '', _('All files') + ' (*.*)|*.*', wx.OPEN|wx.CHANGE_DIR)
2031 if dlg.ShowModal() == wx.ID_OK:
2032 path = dlg.GetPath()
2033 try:
2034 file_data = open(path, 'r').read()
2035 file_f = {
2036 'ID_FullName': '',
2037 'Name': os.path.basename(path),
2038 'Folder': 0,
2039 'Level': 1,
2040 'Used': len(file_data),
2041 'Buffer': file_data,
2042 'Type': 'Other',
2043 'Protected': 0,
2044 'ReadOnly': 0,
2045 'Hidden': 0,
2046 'System': 0,
2047 'Handle': 0,
2048 'Pos': 0,
2049 'Finished': 0
2051 busy = wx.BusyInfo(_('Sending file to phone...'))
2052 time.sleep(0.1)
2053 wx.Yield()
2054 try:
2055 while (not file_f['Finished']):
2056 file_f = self.sm.SendFilePart(file_f)
2057 except gammu.ERR_PERMISSION:
2058 wx.MessageDialog(self,
2059 _('Transfer has been rejected by phone.'),
2060 _('Transfer rejected!'),
2061 wx.OK | wx.ICON_ERROR).ShowModal()
2062 except gammu.GSMError, val:
2063 del busy
2064 self.ShowError(val[0])
2065 except IOError:
2066 wx.MessageDialog(self,
2067 _('Selected file "%s" was not found, no data read.') % path,
2068 _('File not found!'),
2069 wx.OK | wx.ICON_ERROR).ShowModal()
2072 # Connecting / Disconnecting
2075 def PhoneConnect(self, event = None):
2076 busy = wx.BusyInfo(_('One moment please, connecting to phone...'))
2077 time.sleep(0.1)
2078 wx.Yield()
2079 section = self.cfg.ReadInt('/Gammu/Section')
2080 config = self.cfg.gammu.GetConfig(section)
2081 if config['Connection'] == '' or config['Device'] == '':
2082 wx.MessageDialog(self,
2083 _('Phone connection is not properly configured, can not connect to phone.'),
2084 _('Connection not configured!'),
2085 wx.OK | wx.ICON_ERROR).ShowModal()
2086 return
2087 cfg = {
2088 'StartInfo': self.cfg.ReadBool('/Gammu/StartInfo'),
2089 'UseGlobalDebugFile': True,
2090 'DebugFile': None, # Set on other place
2091 'SyncTime': self.cfg.ReadBool('/Gammu/SyncTime'),
2092 'Connection': config['Connection'],
2093 'LockDevice': self.cfg.ReadBool('/Gammu/LockDevice'),
2094 'DebugLevel': 'textalldate', # Set on other place
2095 'Device': config['Device'],
2096 'Model': config['Model'],
2099 # Compatibility with old Gammu versions
2100 cfg = Wammu.Utils.CompatConfig(cfg)
2102 self.sm.SetConfig(0, cfg)
2103 try:
2104 self.sm.Init()
2105 self.sm.SetIncomingCallback(self.IncomingEvent)
2106 try:
2107 self.sm.SetIncomingCall(True)
2108 except gammu.GSMError:
2109 pass
2110 self.TogglePhoneMenus(True)
2111 self.SetupNumberPrefix()
2112 try:
2113 self.IMEI = self.sm.GetIMEI()
2114 except gammu.GSMError:
2115 pass
2116 try:
2117 self.Manufacturer = self.sm.GetManufacturer()
2118 self.cfg.Write('/Phone-0/Manufacturer', self.Manufacturer)
2119 except gammu.GSMError:
2120 pass
2121 try:
2122 m = self.sm.GetModel()
2123 if m[0] == '' or m[0] == 'unknown':
2124 self.Model = m[1]
2125 else:
2126 self.Model = m[0]
2127 self.cfg.Write('/Phone-0/Model', self.Model)
2128 except gammu.GSMError:
2129 pass
2130 try:
2131 self.Version = self.sm.GetFirmware()[0]
2132 except:
2133 pass
2135 except gammu.GSMError, val:
2136 del busy
2137 self.ShowError(val[0])
2138 try:
2139 self.sm.Terminate()
2140 except gammu.GSMError, val:
2141 pass
2143 def DBUSActionCallback(self, id, action):
2145 Called when user does something on notification.
2147 self.dbus_notify.CloseNotification(self.last_dbus_id)
2148 if action == 'accept-call':
2149 self.sm.AnswerCall(0, True)
2150 elif action == 'reject-call':
2151 self.sm.CancelCall(0, True)
2152 else:
2153 print 'Unknown DBUS event: %s' % action
2155 def DBUSNotify(self, title, message, actions):
2157 Performs D-Bus notification if available.
2159 if self.dbus_notify is not None:
2160 self.last_dbus_id = self.dbus_notify.Notify(
2161 'Wammu', self.last_dbus_id, 'wammu',
2162 title, message, actions, {}, -1)
2165 def IncomingEvent(self, sm, type, data):
2167 Called on incoming event from phone.
2170 if type == 'Call':
2171 if data['Status'] != 'IncomingCall':
2172 # We care only about incoming calls
2173 return
2174 if data['Number'] == '':
2175 msg = _('Your phone has just received incoming call')
2176 else:
2177 msg = _('Your phone has just received incoming call from %s') % data['Number']
2178 self.DBUSNotify(_('Incoming call'), msg,
2179 ['reject-call', _('Reject'), 'accept-call', _('Accept')])
2181 def PhoneDisconnect(self, event = None):
2182 busy = wx.BusyInfo(_('One moment please, disconnecting from phone...'))
2183 time.sleep(0.1)
2184 wx.Yield()
2185 try:
2186 self.sm.Terminate()
2187 except gammu.ERR_NOTCONNECTED:
2188 pass
2189 except gammu.GSMError, val:
2190 del busy
2191 self.ShowError(val[0])
2192 self.TogglePhoneMenus(False)
2194 def SearchMessage(self, text):
2196 This has to send message as it is called from different thread.
2198 evt = Wammu.Events.TextEvent(text = text + '\n')
2199 wx.PostEvent(self.searchlog, evt)
2201 def SearchDone(self, lst):
2203 This has to send message as it is called from different thread.
2205 self.founddevices = lst
2206 evt = Wammu.Events.DoneEvent()
2207 wx.PostEvent(self.searchlog, evt)
2209 def SearchPhone(self, evt = None):
2210 index = self.cfg.gammu.FirstFree()
2211 result = Wammu.PhoneWizard.RunConfigureWizard(self, index)
2212 if result is not None:
2213 self.cfg.gammu.SetConfig(result['Position'], result['Device'], result['Connection'], result['Name'])
2214 self.cfg.WriteInt('/Gammu/Section', index)
2216 def About(self, evt = None):
2217 Wammu.About.AboutBox(self).ShowModal()
2219 def Website(self, evt = None):
2220 Wammu.Webbrowser.Open("http://%swammu.eu/?version=%s" % (Wammu.Utils.GetWebsiteLang(), Wammu.__version__))
2222 def Support(self, evt = None):
2223 Wammu.Webbrowser.Open("http://%swammu.eu/support/?version=%s" % (Wammu.Utils.GetWebsiteLang(), Wammu.__version__))
2225 def ReportBug(self, evt = None):
2226 Wammu.Webbrowser.Open("http://bugs.cihar.com/set_project.php?ref=bug_report_page.php&project_id=1")
2228 def PhoneDB(self, evt = None):
2229 Wammu.Webbrowser.Open("http://%swammu.eu/phones/" % Wammu.Utils.GetWebsiteLang())
2231 def Talkback(self, evt = None):
2232 Wammu.TalkbackDialog.DoTalkback(self, self.cfg, 0)
2234 def Donate(self, evt = None):
2235 Wammu.Webbrowser.Open("http://%swammu.eu/donate/?src=wammu" % Wammu.Utils.GetWebsiteLang())
2237 def SaveLog(self, evt = None):
2239 Saves debug log to file.
2241 dlg = wx.FileDialog(self,
2242 _('Save debug log as...'),
2243 os.getcwd(),
2244 'wammu.log',
2246 wx.SAVE | wx.OVERWRITE_PROMPT | wx.CHANGE_DIR)
2247 if dlg.ShowModal() == wx.ID_OK:
2248 Wammu.ErrorLog.SaveLog(filename = dlg.GetPath())