[FritzCall] ADD: WLAN toggle for 05.50; search for faces and/or names
[enigma2-plugins.git] / fritzcall / src / plugin.py
1 # -*- coding: utf-8 -*-
2 '''
3 Update rev
4 $Author: michael $
5 $Revision: 773 $
6 $Date: 2013-04-07 10:03:55 +0200 (Sun, 07 Apr 2013) $
7 $Id: plugin.py 773 2013-04-07 08:03:55Z michael $
8 '''
9
10
11 from Screens.Screen import Screen
12 from Screens.MessageBox import MessageBox
13 from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog
14 from Screens.InputBox import InputBox
15 from Screens import Standby
16 from Screens.HelpMenu import HelpableScreen
17
18 from enigma import eTimer, eSize, ePoint #@UnresolvedImport # pylint: disable=E0611
19 from enigma import eDVBVolumecontrol, eConsoleAppContainer, eBackgroundFileEraser #@UnresolvedImport # pylint: disable=E0611
20 #BgFileEraser = eBackgroundFileEraser.getInstance()
21 #BgFileEraser.erase("blabla.txt")
22
23 from Components.ActionMap import ActionMap
24 from Components.Label import Label
25 from Components.Button import Button
26 from Components.Pixmap import Pixmap
27 from Components.Sources.List import List
28 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger
29 from Components.ConfigList import ConfigListScreen
30 from Components.Harddisk import harddiskmanager
31 try:
32         from Components.config import ConfigPassword
33 except ImportError:
34         ConfigPassword = ConfigText
35
36 from Plugins.Plugin import PluginDescriptor
37 from Tools import Notifications
38 from Tools.NumericalTextInput import NumericalTextInput
39 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE, SCOPE_CONFIG, SCOPE_MEDIA
40 from Tools.LoadPixmap import LoadPixmap
41 from GlobalActions import globalActionMap # for muting
42
43 from twisted.internet import reactor #@UnresolvedImport
44 from twisted.internet.protocol import ReconnectingClientFactory #@UnresolvedImport
45 from twisted.protocols.basic import LineReceiver #@UnresolvedImport
46
47 import re, time, os, traceback
48
49 from nrzuname import ReverseLookupAndNotifier
50 import FritzOutlookCSV, FritzLDIF
51 from . import _, __, initDebug, debug #@UnresolvedImport # pylint: disable=E0611,F0401
52
53 from enigma import getDesktop
54 DESKTOP_WIDTH = getDesktop(0).size().width()
55 DESKTOP_HEIGHT = getDesktop(0).size().height()
56
57 #
58 # this is pure magic.
59 # It returns the first value, if HD (1280x720),
60 # the second if SD (720x576),
61 # else something scaled accordingly
62 # if one of the parameters is -1, scale proportionally
63 #
64 def scaleH(y2, y1):
65         if y2 == -1:
66                 y2 = y1*1280/720
67         elif y1 == -1:
68                 y1 = y2*720/1280
69         return scale(y2, y1, 1280, 720, DESKTOP_WIDTH)
70 def scaleV(y2, y1):
71         if y2 == -1:
72                 y2 = y1*720/576
73         elif y1 == -1:
74                 y1 = y2*576/720
75         return scale(y2, y1, 720, 576, DESKTOP_HEIGHT)
76 def scale(y2, y1, x2, x1, x):
77         return (y2 - y1) * (x - x1) / (x2 - x1) + y1
78
79 my_global_session = None
80
81 config.plugins.FritzCall = ConfigSubsection()
82 config.plugins.FritzCall.fwVersion = ConfigSelection(choices=[("none", _("not configured")), ("old", _("before 05.27")), ("05.27", "05.27, 05.28"), ("05.50", "05.29, 05.50")])
83 config.plugins.FritzCall.debug = ConfigEnableDisable(default=False)
84 #config.plugins.FritzCall.muteOnCall = ConfigSelection(choices=[(None, _("no")), ("ring", _("on ring")), ("connect", _("on connect"))])
85 #config.plugins.FritzCall.muteOnCall = ConfigSelection(choices=[(None, _("no")), ("ring", _("on ring"))])
86 config.plugins.FritzCall.muteOnCall = ConfigEnableDisable(default=False)
87 config.plugins.FritzCall.hostname = ConfigText(default="fritz.box", fixed_size=False)
88 config.plugins.FritzCall.afterStandby = ConfigSelection(choices=[("none", _("show nothing")), ("inList", _("show as list")), ("each", _("show each call"))])
89 config.plugins.FritzCall.filter = ConfigEnableDisable(default=False)
90 config.plugins.FritzCall.filtermsn = ConfigText(default="", fixed_size=False)
91 config.plugins.FritzCall.filtermsn.setUseableChars('0123456789,')
92 config.plugins.FritzCall.filterCallList = ConfigEnableDisable(default=True)
93 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default=False)
94 config.plugins.FritzCall.timeout = ConfigInteger(default=15, limits=(0, 60))
95 config.plugins.FritzCall.lookup = ConfigEnableDisable(default=False)
96 config.plugins.FritzCall.internal = ConfigEnableDisable(default=False)
97 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default=False)
98 config.plugins.FritzCall.fritzphonebookName = ConfigText(default='Dreambox', fixed_size=False)
99 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default=False)
100 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default=False)
101 config.plugins.FritzCall.enable = ConfigEnableDisable(default=False)
102 config.plugins.FritzCall.username = ConfigText(default='BoxAdmin', fixed_size=False)
103 config.plugins.FritzCall.password = ConfigPassword(default="", fixed_size=False)
104 config.plugins.FritzCall.extension = ConfigText(default='1', fixed_size=False)
105 config.plugins.FritzCall.extension.setUseableChars('0123456789')
106 config.plugins.FritzCall.showType = ConfigEnableDisable(default=True)
107 config.plugins.FritzCall.showShortcut = ConfigEnableDisable(default=False)
108 config.plugins.FritzCall.showVanity = ConfigEnableDisable(default=False)
109 config.plugins.FritzCall.prefix = ConfigText(default="", fixed_size=False)
110 config.plugins.FritzCall.prefix.setUseableChars('0123456789')
111 config.plugins.FritzCall.connectionVerbose = ConfigEnableDisable(default=True)
112 config.plugins.FritzCall.ignoreUnknown = ConfigEnableDisable(default=False)
113 config.plugins.FritzCall.reloadPhonebookTime = ConfigInteger(default=8, limits=(0, 99))
114 config.plugins.FritzCall.FritzExtendedSearchFaces = ConfigEnableDisable(default=False)
115 config.plugins.FritzCall.FritzExtendedSearchNames = ConfigEnableDisable(default=False)
116
117
118 def getMountedDevs():
119         def handleMountpoint(loc):
120                 # debug("[FritzCall] handleMountpoint: %s" %repr(loc))
121                 mp = loc[0]
122                 while mp[-1] == '/':
123                         mp = mp[:-1]
124                 #=======================================================================
125                 # if os.path.exists(os.path.join(mp, "PhoneBook.txt")):
126                 #       if os.access(os.path.join(mp, "PhoneBook.txt"), os.W_OK):
127                 #               desc = ' *'
128                 #       else:
129                 #               desc = ' -'
130                 # else:
131                 #       desc = ''
132                 # desc = loc[1] + desc
133                 #=======================================================================
134                 desc = loc[1]
135                 return (mp, desc + " (" + mp + ")")
136
137         mountedDevs = [(resolveFilename(SCOPE_CONFIG), _("Flash")),
138                                    (resolveFilename(SCOPE_MEDIA, "cf"), _("Compact Flash")),
139                                    (resolveFilename(SCOPE_MEDIA, "usb"), _("USB Device"))]
140         mountedDevs += map(lambda p: (p.mountpoint, (_(p.description) if p.description else "")), harddiskmanager.getMountedPartitions(True))
141         mediaDir = resolveFilename(SCOPE_MEDIA)
142         for p in os.listdir(mediaDir):
143                 if os.path.join(mediaDir, p) not in [path[0] for path in mountedDevs]:
144                         mountedDevs.append((os.path.join(mediaDir, p), _("Media directory")))
145         debug("[FritzCall] getMountedDevs1: %s" %repr(mountedDevs))
146         mountedDevs = filter(lambda path: os.path.isdir(path[0]) and os.access(path[0], os.W_OK|os.X_OK), mountedDevs)
147         # put this after the write/executable check, that is far too slow...
148         netDir = resolveFilename(SCOPE_MEDIA, "net")
149         if os.path.isdir(netDir):
150                 mountedDevs += map(lambda p: (os.path.join(netDir, p), _("Network mount")), os.listdir(netDir))
151         mountedDevs = map(handleMountpoint, mountedDevs)
152         return mountedDevs
153 config.plugins.FritzCall.phonebookLocation = ConfigSelection(choices=getMountedDevs())
154
155 countryCodes = [
156         ("0049", _("Germany")),
157         ("0031", _("The Netherlands")),
158         ("0033", _("France")),
159         ("0039", _("Italy")),
160         ("0041", _("Switzerland")),
161         ("0043", _("Austria")),
162         ("", _("Others"))
163         ]
164 config.plugins.FritzCall.country = ConfigSelection(choices=countryCodes)
165
166 FBF_ALL_CALLS = "."
167 FBF_IN_CALLS = "1"
168 FBF_MISSED_CALLS = "2"
169 FBF_OUT_CALLS = "3"
170 fbfCallsChoices = {FBF_ALL_CALLS: _("All calls"),
171                                    FBF_IN_CALLS: _("Incoming calls"),
172                                    FBF_MISSED_CALLS: _("Missed calls"),
173                                    FBF_OUT_CALLS: _("Outgoing calls")
174                                    }
175 config.plugins.FritzCall.fbfCalls = ConfigSelection(choices=fbfCallsChoices)
176
177 config.plugins.FritzCall.name = ConfigText(default="", fixed_size=False)
178 config.plugins.FritzCall.number = ConfigText(default="", fixed_size=False)
179 config.plugins.FritzCall.number.setUseableChars('0123456789')
180
181 phonebook = None
182 fritzbox = None
183
184 avon = {}
185
186 def initAvon():
187         avonFileName = resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/avon.dat")
188         if os.path.exists(avonFileName):
189                 for line in open(avonFileName):
190                         line = line.decode("iso-8859-1").encode('utf-8')
191                         if line[0] == '#':
192                                 continue
193                         parts = line.split(':')
194                         if len(parts) == 2:
195                                 avon[parts[0].replace('-','').replace('*','').replace('/','')] = parts[1]
196
197 def resolveNumberWithAvon(number, countrycode):
198         if not number or number[0] != '0':
199                 return ""
200                 
201         countrycode = countrycode.replace('00','+')
202         if number[:2] == '00':
203                 normNumber = '+' + number[2:]
204         elif number[:1] == '0':
205                 normNumber = countrycode + number[1:]
206         else: # this should can not happen, but safety first
207                 return ""
208         
209         # debug('normNumer: ' + normNumber)
210         for i in reversed(range(min(10, len(number)))):
211                 if avon.has_key(normNumber[:i]):
212                         return '[' + avon[normNumber[:i]].strip() + ']'
213         return ""
214
215 def handleReverseLookupResult(name):
216         found = re.match("NA: ([^;]*);VN: ([^;]*);STR: ([^;]*);HNR: ([^;]*);PLZ: ([^;]*);ORT: ([^;]*)", name)
217         if found:
218                 ( name, firstname, street, streetno, zipcode, city ) = (found.group(1),
219                                                                                                 found.group(2),
220                                                                                                 found.group(3),
221                                                                                                 found.group(4),
222                                                                                                 found.group(5),
223                                                                                                 found.group(6)
224                                                                                                 )
225                 if firstname:
226                         name += ' ' + firstname
227                 if street or streetno or zipcode or city:
228                         name += ', '
229                 if street:
230                         name += street
231                 if streetno:
232                         name += ' ' + streetno
233                 if (street or streetno) and (zipcode or city):
234                         name += ', '
235                 if zipcode and city:
236                         name += zipcode + ' ' + city
237                 elif zipcode:
238                         name += zipcode
239                 elif city:
240                         name += city
241         return name
242
243 from xml.dom.minidom import parse
244 cbcInfos = {}
245 def initCbC():
246         callbycallFileName = resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/callbycall_world.xml")
247         if os.path.exists(callbycallFileName):
248                 dom = parse(callbycallFileName)
249                 for top in dom.getElementsByTagName("callbycalls"):
250                         for cbc in top.getElementsByTagName("country"):
251                                 code = cbc.getAttribute("code").replace("+","00")
252                                 cbcInfos[code] = cbc.getElementsByTagName("callbycall")
253         else:
254                 debug("[FritzCall] initCbC: callbycallFileName does not exist?!?!")
255
256 def stripCbCPrefix(number, countrycode):
257         if number and number[:2] != "00" and cbcInfos.has_key(countrycode):
258                 for cbc in cbcInfos[countrycode]:
259                         if len(cbc.getElementsByTagName("length"))<1 or len(cbc.getElementsByTagName("prefix"))<1:
260                                 debug("[FritzCall] stripCbCPrefix: entries for " + countrycode + " %s invalid")
261                                 return number
262                         length = int(cbc.getElementsByTagName("length")[0].childNodes[0].data)
263                         prefix = cbc.getElementsByTagName("prefix")[0].childNodes[0].data
264                         # if re.match('^'+prefix, number):
265                         if number[:len(prefix)] == prefix:
266                                 return number[length:]
267         return number
268
269 import FritzCallFBF
270
271 class FritzAbout(Screen):
272
273         def __init__(self, session):
274                 textFieldWidth = scaleV(350, 250)
275                 width = 5 + 150 + 20 + textFieldWidth + 5 + 175 + 5
276                 height = 5 + 175 + 5 + 25 + 5
277                 self.skin = """
278                         <screen name="FritzAbout" position="center,center" size="%d,%d" title="About FritzCall" >
279                                 <widget name="text" position="175,%d" size="%d,%d" font="Regular;%d" />
280                                 <ePixmap position="5,37" size="150,110" pixmap="%s" transparent="1" alphatest="blend" />
281                                 <ePixmap position="%d,5" size="175,175" pixmap="%s" transparent="1" alphatest="blend" />
282                                 <widget name="url" position="20,185" size="%d,25" font="Regular;%d" />
283                         </screen>""" % (
284                                                         width, height, # size
285                                                         (height-scaleV(150,130)) / 2, # text vertical position
286                                                         textFieldWidth,
287                                                         scaleV(150,130), # text height
288                                                         scaleV(24,21), # text font size
289                                                         resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png"), # 150x110
290                                                         5 + 150 + 5 + textFieldWidth + 5, # qr code horizontal offset
291                                                         resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/website.png"), # 175x175
292                                                         width-40, # url width
293                                                         scaleV(24,21) # url font size
294                                                         )
295                 Screen.__init__(self, session)
296                 self["aboutActions"] = ActionMap(["OkCancelActions"],
297                 {
298                 "cancel": self.exit,
299                 "ok": self.exit,
300                 }, -2)
301                 self["text"] = Label(
302                                                         "FritzCall Plugin" + "\n\n" +
303                                                         "$Author: michael $"[1:-2] + "\n" +
304                                                         "$Revision: 773 $"[1:-2] + "\n" + 
305                                                         "$Date: 2013-04-07 10:03:55 +0200 (Sun, 07 Apr 2013) $"[1:23] + "\n"
306                                                         )
307                 self["url"] = Label("http://wiki.blue-panel.com/index.php/FritzCall")
308                 self.onLayoutFinish.append(self.setWindowTitle)
309
310         def setWindowTitle(self):
311                 # TRANSLATORS: this is a window title.
312                 self.setTitle(_("About FritzCall"))
313
314         def exit(self):
315                 self.close()
316
317 from FritzCallFBF import FBF_dectActive, FBF_faxActive, FBF_rufumlActive, FBF_tamActive
318
319 class FritzMenu(Screen, HelpableScreen):
320         def __init__(self, session):
321                 if not fritzbox or not fritzbox.info:
322                         return
323
324                 if config.plugins.FritzCall.fwVersion.value == "old" or config.plugins.FritzCall.fwVersion.value == "05.27":
325                         fontSize = scaleV(24, 21) # indeed this is font size +2
326                         noButtons = 2 # reset, wlan
327         
328                         if fritzbox.info[FBF_tamActive]:
329                                 noButtons += 1 # toggle mailboxes
330                         width = max(DESKTOP_WIDTH - scaleH(500, 250), noButtons*140+(noButtons+1)*10)
331                         # boxInfo 2 lines, gap, internet 2 lines, gap, dsl/wlan each 1 line, gap, buttons
332                         height = 5 + 2*fontSize + 10 + 2*fontSize + 10 + 2*fontSize + 10 + 40 + 5
333                         if fritzbox.info[FBF_tamActive] is not None:
334                                 height += fontSize
335                         if fritzbox.info[FBF_dectActive] is not None:
336                                 height += fontSize
337                         if fritzbox.info[FBF_faxActive] is not None:
338                                 height += fontSize
339                         if fritzbox.info[FBF_rufumlActive] is not None:
340                                 height += fontSize
341                         buttonsGap = (width-noButtons*140)/(noButtons+1)
342                         buttonsVPos = height-40-5
343         
344                         varLinePos = 4
345                         if fritzbox.info[FBF_tamActive] is not None:
346                                 mailboxLine = """
347                                         <widget name="FBFMailbox" position="%d,%d" size="%d,%d" font="Regular;%d" />
348                                         <widget name="mailbox_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
349                                         <widget name="mailbox_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
350                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
351                                         <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
352                                         """ % (
353                                                         40, 5+2*fontSize+10+varLinePos*fontSize+10, # position mailbox
354                                                         width-40-20, fontSize, # size mailbox
355                                                         fontSize-2,
356                                                         "skin_default/buttons/button_green_off.png",
357                                                         20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
358                                                         "skin_default/buttons/button_green.png",
359                                                         20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
360                                                         noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
361                                                         noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
362                                         )
363                                 varLinePos += 1
364                         else:
365                                 mailboxLine = ""
366         
367                         if fritzbox.info[FBF_dectActive] is not None:
368                                 dectLine = """
369                                         <widget name="FBFDect" position="%d,%d" size="%d,%d" font="Regular;%d" />
370                                         <widget name="dect_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
371                                         <widget name="dect_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
372                                         """ % (
373                                                         40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
374                                                         width-40-20, fontSize, # size dect
375                                                         fontSize-2,
376                                                         "skin_default/buttons/button_green_off.png",
377                                                         20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
378                                                         "skin_default/buttons/button_green.png",
379                                                         20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
380                                         )
381                                 varLinePos += 1
382                         else:
383                                 dectLine = ""
384         
385                         if fritzbox.info[FBF_faxActive] is not None:
386                                 faxLine = """
387                                         <widget name="FBFFax" position="%d,%d" size="%d,%d" font="Regular;%d" />
388                                         <widget name="fax_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
389                                         <widget name="fax_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
390                                         """ % (
391                                                         40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
392                                                         width-40-20, fontSize, # size dect
393                                                         fontSize-2,
394                                                         "skin_default/buttons/button_green_off.png",
395                                                         20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
396                                                         "skin_default/buttons/button_green.png",
397                                                         20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
398                                         )
399                                 varLinePos += 1
400                         else:
401                                 faxLine = ""
402         
403                         if fritzbox.info[FBF_rufumlActive] is not None:
404                                 rufumlLine = """
405                                         <widget name="FBFRufuml" position="%d,%d" size="%d,%d" font="Regular;%d" />
406                                         <widget name="rufuml_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
407                                         <widget name="rufuml_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
408                                         """ % (
409                                                         40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
410                                                         width-40-20, fontSize, # size dect
411                                                         fontSize-2,
412                                                         "skin_default/buttons/button_green_off.png",
413                                                         20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
414                                                         "skin_default/buttons/button_green.png",
415                                                         20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
416                                         )
417                                 varLinePos += 1
418                         else:
419                                 rufumlLine = ""
420                 
421                         self.skin = """
422                                 <screen name="FritzMenu" position="center,center" size="%d,%d" title="FRITZ!Box Fon Status" >
423                                         <widget name="FBFInfo" position="%d,%d" size="%d,%d" font="Regular;%d" />
424                                         <widget name="FBFInternet" position="%d,%d" size="%d,%d" font="Regular;%d" />
425                                         <widget name="internet_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
426                                         <widget name="internet_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
427                                         <widget name="FBFDsl" position="%d,%d" size="%d,%d" font="Regular;%d" />
428                                         <widget name="dsl_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
429                                         <widget name="dsl_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
430                                         <widget name="FBFWlan" position="%d,%d" size="%d,%d" font="Regular;%d" />
431                                         <widget name="wlan_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
432                                         <widget name="wlan_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
433                                         %s
434                                         %s
435                                         %s
436                                         %s
437                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
438                                         <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
439                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
440                                         <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
441                                 </screen>""" % (
442                                                         width, height, # size
443                                                         40, 5, # position info
444                                                         width-2*40, 2*fontSize, # size info
445                                                         fontSize-2,
446                                                         40, 5+2*fontSize+10, # position internet
447                                                         width-40, 2*fontSize, # size internet
448                                                         fontSize-2,
449                                                         "skin_default/buttons/button_green_off.png",
450                                                         20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
451                                                         "skin_default/buttons/button_green.png",
452                                                         20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
453                                                         40, 5+2*fontSize+10+2*fontSize+10, # position dsl
454                                                         width-40-20, fontSize, # size dsl
455                                                         fontSize-2,
456                                                         "skin_default/buttons/button_green_off.png",
457                                                         20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
458                                                         "skin_default/buttons/button_green.png",
459                                                         20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
460                                                         40, 5+2*fontSize+10+3*fontSize+10, # position wlan
461                                                         width-40-20, fontSize, # size wlan
462                                                         fontSize-2,
463                                                         "skin_default/buttons/button_green_off.png",
464                                                         20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
465                                                         "skin_default/buttons/button_green.png",
466                                                         20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
467                                                         mailboxLine,
468                                                         dectLine,
469                                                         faxLine,
470                                                         rufumlLine,
471                                                         buttonsGap, buttonsVPos, "skin_default/buttons/red.png", buttonsGap, buttonsVPos,
472                                                         buttonsGap+140+buttonsGap, buttonsVPos, "skin_default/buttons/green.png", buttonsGap+140+buttonsGap, buttonsVPos,
473                                                         )
474         
475                         Screen.__init__(self, session)
476                         HelpableScreen.__init__(self)
477                         # TRANSLATORS: keep it short, this is a button
478                         self["key_red"] = Button(_("Reset"))
479                         # TRANSLATORS: keep it short, this is a button
480                         self["key_green"] = Button(_("Toggle WLAN"))
481                         self._mailboxActive = False
482                         if fritzbox.info[FBF_tamActive] is not None:
483                                 # TRANSLATORS: keep it short, this is a button
484                                 self["key_yellow"] = Button(_("Toggle Mailbox"))
485                                 self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "EPGSelectActions"],
486                                                                                                 {
487                                                                                                 "cancel": self._exit,
488                                                                                                 "ok": self._exit,
489                                                                                                 "red": self._reset,
490                                                                                                 "green": self._toggleWlan,
491                                                                                                 "yellow": (lambda: self._toggleMailbox(-1)),
492                                                                                                 "0": (lambda: self._toggleMailbox(0)),
493                                                                                                 "1": (lambda: self._toggleMailbox(1)),
494                                                                                                 "2": (lambda: self._toggleMailbox(2)),
495                                                                                                 "3": (lambda: self._toggleMailbox(3)),
496                                                                                                 "4": (lambda: self._toggleMailbox(4)),
497                                                                                                 "info": self._getInfo,
498                                                                                                 }, -2)
499                                 # TRANSLATORS: keep it short, this is a help text
500                                 self.helpList.append((self["menuActions"], "ColorActions", [("yellow", _("Toggle all mailboxes"))]))
501                                 # TRANSLATORS: keep it short, this is a help text
502                                 self.helpList.append((self["menuActions"], "NumberActions", [("0", _("Toggle 1. mailbox"))]))
503                                 # TRANSLATORS: keep it short, this is a help text
504                                 self.helpList.append((self["menuActions"], "NumberActions", [("1", _("Toggle 2. mailbox"))]))
505                                 # TRANSLATORS: keep it short, this is a help text
506                                 self.helpList.append((self["menuActions"], "NumberActions", [("2", _("Toggle 3. mailbox"))]))
507                                 # TRANSLATORS: keep it short, this is a help text
508                                 self.helpList.append((self["menuActions"], "NumberActions", [("3", _("Toggle 4. mailbox"))]))
509                                 # TRANSLATORS: keep it short, this is a help text
510                                 self.helpList.append((self["menuActions"], "NumberActions", [("4", _("Toggle 5. mailbox"))]))
511                                 self["FBFMailbox"] = Label(_('Mailbox'))
512                                 self["mailbox_inactive"] = Pixmap()
513                                 self["mailbox_active"] = Pixmap()
514                                 self["mailbox_active"].hide()
515                         else:
516                                 self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
517                                                                                                 {
518                                                                                                 "cancel": self._exit,
519                                                                                                 "ok": self._exit,
520                                                                                                 "green": self._toggleWlan,
521                                                                                                 "red": self._reset,
522                                                                                                 "info": self._getInfo,
523                                                                                                 }, -2)
524         
525                         # TRANSLATORS: keep it short, this is a help text
526                         self.helpList.append((self["menuActions"], "OkCancelActions", [("cancel", _("Quit"))]))
527                         # TRANSLATORS: keep it short, this is a help text
528                         self.helpList.append((self["menuActions"], "OkCancelActions", [("ok", _("Quit"))]))
529                         # TRANSLATORS: keep it short, this is a help text
530                         self.helpList.append((self["menuActions"], "ColorActions", [("green", _("Toggle WLAN"))]))
531                         # TRANSLATORS: keep it short, this is a help text
532                         self.helpList.append((self["menuActions"], "ColorActions", [("red", _("Reset"))]))
533                         # TRANSLATORS: keep it short, this is a help text
534                         self.helpList.append((self["menuActions"], "EPGSelectActions", [("info", _("Refresh status"))]))
535         
536                         self["FBFInfo"] = Label(_('Getting status from FRITZ!Box Fon...'))
537         
538                         self["FBFInternet"] = Label('Internet')
539                         self["internet_inactive"] = Pixmap()
540                         self["internet_active"] = Pixmap()
541                         self["internet_active"].hide()
542         
543                         self["FBFDsl"] = Label('DSL')
544                         self["dsl_inactive"] = Pixmap()
545                         self["dsl_inactive"].hide()
546                         self["dsl_active"] = Pixmap()
547                         self["dsl_active"].hide()
548         
549                         self["FBFWlan"] = Label('WLAN ')
550                         self["wlan_inactive"] = Pixmap()
551                         self["wlan_inactive"].hide()
552                         self["wlan_active"] = Pixmap()
553                         self["wlan_active"].hide()
554                         self._wlanActive = False
555         
556                         if fritzbox.info[FBF_dectActive] is not None: 
557                                 self["FBFDect"] = Label('DECT')
558                                 self["dect_inactive"] = Pixmap()
559                                 self["dect_active"] = Pixmap()
560                                 self["dect_active"].hide()
561         
562                         if fritzbox.info[FBF_faxActive] is not None: 
563                                 self["FBFFax"] = Label('Fax')
564                                 self["fax_inactive"] = Pixmap()
565                                 self["fax_active"] = Pixmap()
566                                 self["fax_active"].hide()
567         
568                         if fritzbox.info[FBF_rufumlActive] is not None: 
569                                 self["FBFRufuml"] = Label(_('Call redirection'))
570                                 self["rufuml_inactive"] = Pixmap()
571                                 self["rufuml_active"] = Pixmap()
572                                 self["rufuml_active"].hide()
573                 else:
574                         fontSize = scaleV(24, 21) # indeed this is font size +2
575         
576                         noButtons = 1
577                         width = max(DESKTOP_WIDTH - scaleH(500, 250), noButtons*140+(noButtons+1)*10)
578                         # boxInfo 2 lines, gap, internet 2 lines, gap, dsl/wlan/dect/fax/rufuml each 1 line, gap
579                         height = 5 + 2*fontSize + 10 + 2*fontSize + 10 + 5*fontSize + 10 + 40 + 5
580                         buttonsGap = (width-noButtons*140)/(noButtons+1)
581                         buttonsVPos = height-40-5
582         
583                         self.skin = """
584                                 <screen name="FritzMenuNew" position="center,center" size="%d,%d" title="FRITZ!Box Fon Status" >
585                                         <widget name="FBFInfo" position="%d,%d" size="%d,%d" font="Regular;%d" />
586                                         <widget name="FBFInternet" position="%d,%d" size="%d,%d" font="Regular;%d" />
587                                         <widget name="internet_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
588                                         <widget name="internet_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
589                                         <widget name="FBFDsl" position="%d,%d" size="%d,%d" font="Regular;%d" />
590                                         <widget name="dsl_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
591                                         <widget name="dsl_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
592                                         <widget name="FBFWlan" position="%d,%d" size="%d,%d" font="Regular;%d" />
593                                         <widget name="wlan_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
594                                         <widget name="wlan_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
595                                         <widget name="FBFDect" position="%d,%d" size="%d,%d" font="Regular;%d" />
596                                         <widget name="dect_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
597                                         <widget name="dect_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
598                                         <widget name="FBFFax" position="%d,%d" size="%d,%d" font="Regular;%d" />
599                                         <widget name="fax_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
600                                         <widget name="fax_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
601                                         <widget name="FBFRufuml" position="%d,%d" size="%d,%d" font="Regular;%d" />
602                                         <widget name="rufuml_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
603                                         <widget name="rufuml_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
604                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
605                                         <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
606                                 </screen>""" % (
607                                                         width, height, # size
608                                                         40, 5, # position info
609                                                         width-2*40, 2*fontSize, # size info
610                                                         fontSize-2,
611                                                         40, 5+2*fontSize+10, # position internet
612                                                         width-40, 2*fontSize, # size internet
613                                                         fontSize-2,
614                                                         "skin_default/buttons/button_green_off.png",
615                                                         20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
616                                                         "skin_default/buttons/button_green.png",
617                                                         20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
618                                                         40, 5+2*fontSize+10+2*fontSize+10, # position dsl
619                                                         width-40-20, fontSize, # size dsl
620                                                         fontSize-2,
621                                                         "skin_default/buttons/button_green_off.png",
622                                                         20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
623                                                         "skin_default/buttons/button_green.png",
624                                                         20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
625                                                         40, 5+2*fontSize+10+3*fontSize+10, # position wlan
626                                                         width-40-20, fontSize, # size wlan
627                                                         fontSize-2,
628                                                         "skin_default/buttons/button_green_off.png",
629                                                         20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
630                                                         "skin_default/buttons/button_green.png",
631                                                         20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
632                                                         40, 5+2*fontSize+10+4*fontSize+10, # position dect
633                                                         width-40-20, fontSize, # size dect
634                                                         fontSize-2,
635                                                         "skin_default/buttons/button_green_off.png",
636                                                         20, 5+2*fontSize+10+4*fontSize+10+(fontSize-16)/2, # position button dect
637                                                         "skin_default/buttons/button_green.png",
638                                                         20, 5+2*fontSize+10+4*fontSize+10+(fontSize-16)/2, # position button dect
639                                                         40, 5+2*fontSize+10+5*fontSize+10, # position dect
640                                                         width-40-20, fontSize, # size fax
641                                                         fontSize-2,
642                                                         "skin_default/buttons/button_green_off.png",
643                                                         20, 5+2*fontSize+10+5*fontSize+10+(fontSize-16)/2, # position button fax
644                                                         "skin_default/buttons/button_green.png",
645                                                         20, 5+2*fontSize+10+5*fontSize+10+(fontSize-16)/2, # position button fax
646                                                         40, 5+2*fontSize+10+6*fontSize+10, # position dect
647                                                         width-40-20, fontSize, # size rufuml
648                                                         fontSize-2,
649                                                         "skin_default/buttons/button_green_off.png",
650                                                         20, 5+2*fontSize+10+6*fontSize+10+(fontSize-16)/2, # position button rufuml
651                                                         "skin_default/buttons/button_green.png",
652                                                         20, 5+2*fontSize+10+6*fontSize+10+(fontSize-16)/2, # position button rufuml
653                                                         # buttonsGap, buttonsVPos, "skin_default/buttons/red.png", buttonsGap, buttonsVPos,
654                                                         buttonsGap, buttonsVPos, "skin_default/buttons/green.png", buttonsGap, buttonsVPos,
655                                                         # buttonsGap+140+buttonsGap, buttonsVPos, "skin_default/buttons/green.png", buttonsGap+140+buttonsGap, buttonsVPos,
656                                                         )
657         
658                         Screen.__init__(self, session)
659                         HelpableScreen.__init__(self)
660                         # TRANSLATORS: keep it short, this is a button
661                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
662                                                                                         {
663                                                                                         "cancel": self._exit,
664                                                                                         "ok": self._exit,
665                                                                                         "green": self._toggleWlan,
666                                                                                         "red": self._reset,
667                                                                                         "info": self._getInfo,
668                                                                                         }, -2)
669         
670                         # TRANSLATORS: keep it short, this is a help text
671                         self.helpList.append((self["menuActions"], "OkCancelActions", [("cancel", _("Quit"))]))
672                         # TRANSLATORS: keep it short, this is a help text
673                         self.helpList.append((self["menuActions"], "OkCancelActions", [("ok", _("Quit"))]))
674                         # TRANSLATORS: keep it short, this is a help text
675                         self.helpList.append((self["menuActions"], "ColorActions", [("green", _("Toggle WLAN"))]))
676                         # TRANSLATORS: keep it short, this is a help text
677                         self.helpList.append((self["menuActions"], "ColorActions", [("red", _("Reset"))]))
678                         # TRANSLATORS: keep it short, this is a help text
679                         self.helpList.append((self["menuActions"], "EPGSelectActions", [("info", _("Refresh status"))]))
680         
681                         # TRANSLATORS: keep it short, this is a button
682                         self["key_red"] = Button(_("Reset"))
683                         # TRANSLATORS: keep it short, this is a button
684                         self["key_green"] = Button(_("Toggle WLAN"))
685
686                         self["FBFInfo"] = Label(_('Getting status from FRITZ!Box Fon...'))
687         
688                         self["FBFInternet"] = Label('Internet')
689                         self["internet_inactive"] = Pixmap()
690                         self["internet_inactive"].hide()
691                         self["internet_active"] = Pixmap()
692                         self["internet_active"].hide()
693         
694                         self["FBFDsl"] = Label('DSL')
695                         self["dsl_inactive"] = Pixmap()
696                         self["dsl_inactive"].hide()
697                         self["dsl_active"] = Pixmap()
698                         self["dsl_active"].hide()
699         
700                         self["FBFWlan"] = Label('WLAN ')
701                         self["wlan_inactive"] = Pixmap()
702                         self["wlan_inactive"].hide()
703                         self["wlan_active"] = Pixmap()
704                         self["wlan_active"].hide()
705                         self._wlanActive = False
706         
707                         self["FBFDect"] = Label('DECT')
708                         self["dect_inactive"] = Pixmap()
709                         self["dect_inactive"].hide()
710                         self["dect_active"] = Pixmap()
711                         self["dect_active"].hide()
712         
713                         self["FBFFax"] = Label('Fax')
714                         self["fax_inactive"] = Pixmap()
715                         self["fax_inactive"].hide()
716                         self["fax_active"] = Pixmap()
717                         self["fax_active"].hide()
718         
719                         self["FBFRufuml"] = Label(_('Call redirection'))
720                         self["rufuml_inactive"] = Pixmap()
721                         self["rufuml_inactive"].hide()
722                         self["rufuml_active"] = Pixmap()
723                         self["rufuml_active"].hide()
724                         
725                 #=======================================================================
726                 # self._timer = eTimer()
727                 # self._timer.callback.append(self._getInfo)
728                 # self.onShown.append(lambda: self._timer.start(5000))
729                 # self.onHide.append(self._timer.stop)
730                 #=======================================================================
731                 self._getInfo()
732                 self.onLayoutFinish.append(self.setWindowTitle)
733
734         def setWindowTitle(self):
735                 # TRANSLATORS: this is a window title.
736                 self.setTitle(_("FRITZ!Box Fon Status"))
737
738         def _getInfo(self):
739                 if fritzbox:
740                         fritzbox.getInfo(self._fillMenu)
741                         self._fillMenu(fritzbox.info, True)
742
743         def _fillMenu(self, status, refreshing=False):
744                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = status
745                 if wlanState:
746                         self._wlanActive = (wlanState[0] == '1')
747                 self._mailboxActive = False
748                 try:
749                         if not self.has_key("FBFInfo"): # screen is closed already
750                                 return
751
752                         if refreshing:
753                                 self["FBFInfo"].setText(_("Refreshing..."))
754                         else:
755                                 if boxInfo:
756                                         self["FBFInfo"].setText(boxInfo.replace(', ', '\n'))
757                                 else:
758                                         self["FBFInfo"].setText('BoxInfo ' + _('Status not available'))
759
760                         if ipAddress:
761                                 if upTime:
762                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress + '\n' + _('Connected since') + ' ' + upTime)
763                                 else:
764                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress)
765                                 self["internet_inactive"].hide()
766                                 self["internet_active"].show()
767                         else:
768                                 self["internet_active"].hide()
769                                 self["internet_inactive"].show()
770
771                         if dslState:
772                                 if dslState[0] == '5':
773                                         self["dsl_inactive"].hide()
774                                         self["dsl_active"].show()
775                                         if dslState[1]:
776                                                 self["FBFDsl"].setText('DSL ' + dslState[1])
777                                 else:
778                                         self["dsl_active"].hide()
779                                         self["dsl_inactive"].show()
780                         else:
781                                 self["FBFDsl"].setText('DSL ' + _('Status not available'))
782                                 self["dsl_active"].hide()
783                                 self["dsl_inactive"].hide()
784
785                         if wlanState:
786                                 if wlanState[0 ] == '1':
787                                         self["wlan_inactive"].hide()
788                                         self["wlan_active"].show()
789                                         message = 'WLAN'
790                                         if wlanState[1] == '0':
791                                                 message += ' ' + _('not encrypted')
792                                         else:
793                                                 message += ' ' + _('encrypted')
794                                         if wlanState[2]:
795                                                 if wlanState[2] == '0':
796                                                         message = message + ', ' + _('no device active')
797                                                 elif wlanState[2] == '1' or wlanState[2] == 'ein':
798                                                         message = message + ', ' + _('one device active')
799                                                 else:
800                                                         message = message + ', ' + wlanState[2] + ' ' + _('devices active')
801                                         self["FBFWlan"].setText(message)
802                                 else:
803                                         self["wlan_active"].hide()
804                                         self["wlan_inactive"].show()
805                                         self["FBFWlan"].setText('WLAN')
806                         else:
807                                 self["FBFWlan"].setText('WLAN ' + _('Status not available'))
808                                 self["wlan_active"].hide()
809                                 self["wlan_inactive"].hide()
810
811                         if fritzbox.info[FBF_tamActive]:
812                                 if  not tamActive or tamActive[0] == 0:
813                                         self._mailboxActive = False
814                                         self["mailbox_active"].hide()
815                                         self["mailbox_inactive"].show()
816                                         self["FBFMailbox"].setText(_('No mailbox active'))
817                                 else:
818                                         self._mailboxActive = True
819                                         message = ''
820                                         for i in range(min(len(tamActive)-1, 5)):
821                                                 if tamActive[i+1]:
822                                                         message = message + str(i) + ','
823                                         if message:
824                                                 message = '(' + message[:-1] + ')'
825                                         self["mailbox_inactive"].hide()
826                                         self["mailbox_active"].show()
827                                         if tamActive[0] == 1:
828                                                 self["FBFMailbox"].setText(_('One mailbox active') + ' ' + message)
829                                         else:
830                                                 self["FBFMailbox"].setText(str(tamActive[0]) + ' ' + _('mailboxes active') + ' ' + message)
831         
832                         if dectActive and self.has_key("dect_inactive"):
833                                 self["dect_inactive"].hide()
834                                 self["dect_active"].show()
835                                 if dectActive == 0:
836                                         self["FBFDect"].setText(_('No DECT phone registered'))
837                                 else:
838                                         if dectActive == 1:
839                                                 self["FBFDect"].setText(_('One DECT phone registered'))
840                                         else:
841                                                 self["FBFDect"].setText(str(dectActive) + ' ' + _('DECT phones registered'))
842                         else:
843                                 self["dect_active"].hide()
844                                 self["dect_inactive"].show()
845                                 self["FBFFax"].setText(_('DECT inactive'))
846
847                         if faxActive:
848                                 self["fax_inactive"].hide()
849                                 self["fax_active"].show()
850                                 self["FBFFax"].setText(_('Software fax active'))
851                         else:
852                                 self["fax_active"].hide()
853                                 self["fax_inactive"].show()
854                                 self["FBFFax"].setText(_('Software fax inactive'))
855
856                         if rufumlActive:
857                                 self["rufuml_inactive"].hide()
858                                 self["rufuml_active"].show()
859                                 if rufumlActive == -1: # means no number available
860                                         self["FBFRufuml"].setText(_('Call redirection active'))
861                                 elif rufumlActive == 1:
862                                         self["FBFRufuml"].setText(_('One call redirection active'))
863                                 else:
864                                         self["FBFRufuml"].setText(str(rufumlActive) + ' ' + _('call redirections active'))
865                         else:
866                                 self["rufuml_active"].hide()
867                                 self["rufuml_inactive"].show()
868                                 self["FBFRufuml"].setText(_('No call redirection active'))
869
870                 except KeyError:
871                         debug("[FritzCallFBF] _fillMenu: " + traceback.format_exc())
872
873         def _toggleWlan(self):
874                 if self._wlanActive:
875                         debug("[FritzMenu] toggleWlan off")
876                         fritzbox.changeWLAN('0')
877                 else:
878                         debug("[FritzMenu] toggleWlan on")
879                         fritzbox.changeWLAN('1')
880                 self._getInfo()
881
882         def _toggleMailbox(self, which):
883                 debug("[FritzMenu] toggleMailbox")
884                 if fritzbox.info[FBF_tamActive]:
885                         debug("[FritzMenu] toggleMailbox off")
886                         fritzbox.changeMailbox(which)
887
888         def _reset(self):
889                 fritzbox.reset()
890                 self._exit()
891
892         def _exit(self):
893                 #=======================================================================
894                 # self._timer.stop()
895                 #=======================================================================
896                 self.close()
897
898
899 class FritzDisplayCalls(Screen, HelpableScreen):
900
901         def __init__(self, session, text=""): #@UnusedVariable # pylint: disable=W0613
902                 self.width = DESKTOP_WIDTH * scaleH(75, 85)/100
903                 self.height = DESKTOP_HEIGHT * 0.75
904                 dateFieldWidth = scaleH(180, 105)
905                 dirFieldWidth = 16
906                 lengthFieldWidth = scaleH(55, 45)
907                 scrollbarWidth = scaleH(35, 35)
908                 entriesWidth = self.width -scaleH(40, 5) -5
909                 hereFieldWidth = entriesWidth -dirFieldWidth -5 -dateFieldWidth -5 -lengthFieldWidth -scrollbarWidth
910                 fieldWidth = entriesWidth -dirFieldWidth -5 -5 -scrollbarWidth
911                 fontSize = scaleV(22, 20)
912                 itemHeight = 2*fontSize+5
913                 entriesHeight = self.height -scaleV(15, 10) -5 -fontSize -5 -5 -5 -40 -5
914                 buttonGap = (self.width -4*140)/5
915                 buttonV = self.height -40
916                 debug("[FritzDisplayCalls] width: " + str(self.width))
917                 self.skin = """
918                         <screen name="FritzDisplayCalls" position="center,center" size="%d,%d" title="Phone calls" >
919                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
920                                 <widget name="statusbar" position="%d,%d" size="%d,%d" font="Regular;%d" backgroundColor="#aaaaaa" transparent="1" />
921                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
922                                 <widget source="entries" render="Listbox" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1">
923                                         <convert type="TemplatedMultiContent">
924                                                 {"template": [
925                                                                 MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 1), # index 0 is the number, index 1 is date
926                                                                 MultiContentEntryPixmapAlphaTest(pos = (%d,%d), size = (%d,%d), png = 2), # index 1 i direction pixmap
927                                                                 MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 3), # index 2 is remote name/number
928                                                                 MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 4), # index 3 is length of call
929                                                                 MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_RIGHT|RT_VALIGN_CENTER, text = 5), # index 4 is my number/name for number
930                                                         ],
931                                                 "fonts": [gFont("Regular", %d), gFont("Regular", %d)],
932                                                 "itemHeight": %d
933                                                 }
934                                         </convert>
935                                 </widget>
936                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
937                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
938                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
939                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
940                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
941                                 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
942                                 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
943                                 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
944                                 <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
945                         </screen>""" % (
946                                                 # scaleH(90, 75), scaleV(100, 78), # position 
947                                                 self.width, self.height, # size
948                                                 self.width, # eLabel width
949                                                 scaleH(40, 5), scaleV(10, 5), # statusbar position
950                                                 self.width, fontSize+5, # statusbar size
951                                                 scaleV(21, 21), # statusbar font size
952                                                 scaleV(10, 5)+5+fontSize+5, # eLabel position vertical
953                                                 self.width, # eLabel width
954                                                 scaleH(40, 5), scaleV(10, 5)+5+fontSize+5+5, # entries position
955                                                 entriesWidth, entriesHeight, # entries size
956                                                 5+dirFieldWidth+5, fontSize+5, dateFieldWidth, fontSize, # date pos/size
957                                                 5, (itemHeight-dirFieldWidth)/2, dirFieldWidth, dirFieldWidth, # dir pos/size
958                                                 5+dirFieldWidth+5, 5, fieldWidth, fontSize, # caller pos/size
959                                                 2+dirFieldWidth+2+dateFieldWidth+5, fontSize+5, lengthFieldWidth, fontSize, # length pos/size
960                                                 2+dirFieldWidth+2+dateFieldWidth+5+lengthFieldWidth+5, fontSize+5, hereFieldWidth, fontSize, # my number pos/size
961                                                 fontSize-4, fontSize, # fontsize
962                                                 itemHeight, # itemHeight
963                                                 buttonV-5, # eLabel position vertical
964                                                 self.width, # eLabel width
965                                                 buttonGap, buttonV, "skin_default/buttons/red.png", # widget red
966                                                 2*buttonGap+140, buttonV, "skin_default/buttons/green.png", # widget green
967                                                 3*buttonGap+2*140, buttonV, "skin_default/buttons/yellow.png", # widget yellow
968                                                 4*buttonGap+3*140, buttonV, "skin_default/buttons/blue.png", # widget blue
969                                                 buttonGap, buttonV, scaleV(22, 21), # widget red
970                                                 2*buttonGap+140, buttonV, scaleV(22, 21), # widget green
971                                                 3*buttonGap+2*140, buttonV, scaleV(22, 21), # widget yellow
972                                                 4*buttonGap+3*140, buttonV, scaleV(22, 21), # widget blue
973                                                                                                                 )
974                 # debug("[FritzDisplayCalls] skin: " + self.skin)
975                 Screen.__init__(self, session)
976                 HelpableScreen.__init__(self)
977
978                 # TRANSLATORS: keep it short, this is a button
979                 self["key_yellow"] = Button(_("All"))
980                 # TRANSLATORS: keep it short, this is a button
981                 self["key_red"] = Button(_("Missed"))
982                 # TRANSLATORS: keep it short, this is a button
983                 self["key_blue"] = Button(_("Incoming"))
984                 # TRANSLATORS: keep it short, this is a button
985                 self["key_green"] = Button(_("Outgoing"))
986
987                 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
988                 {
989                         "yellow": (lambda: self.display(FBF_ALL_CALLS)),
990                         "red": (lambda: self.display(FBF_MISSED_CALLS)),
991                         "blue": (lambda: self.display(FBF_IN_CALLS)),
992                         "green": (lambda: self.display(FBF_OUT_CALLS)),
993                         "cancel": self.ok,
994                         "ok": self.showEntry, }, - 2)
995
996                 # TRANSLATORS: keep it short, this is a help text
997                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
998                 # TRANSLATORS: keep it short, this is a help text
999                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1000                 # TRANSLATORS: keep it short, this is a help text
1001                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Display all calls"))]))
1002                 # TRANSLATORS: keep it short, this is a help text
1003                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Display missed calls"))]))
1004                 # TRANSLATORS: keep it short, this is a help text
1005                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Display incoming calls"))]))
1006                 # TRANSLATORS: keep it short, this is a help text
1007                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Display outgoing calls"))]))
1008
1009                 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
1010                 self.list = []
1011                 self["entries"] = List(self.list)
1012                 #=======================================================================
1013                 # fontSize = scaleV(22, 18)
1014                 # fontHeight = scaleV(24, 20)
1015                 # self["entries"].l.setFont(0, gFont("Regular", fontSize))
1016                 # self["entries"].l.setItemHeight(fontHeight)
1017                 #=======================================================================
1018                 debug("[FritzDisplayCalls] init: '''%s'''" % config.plugins.FritzCall.fbfCalls.value)
1019                 self.display(config.plugins.FritzCall.fbfCalls.value)
1020                 self.onLayoutFinish.append(self.setWindowTitle)
1021
1022         def setWindowTitle(self):
1023                 # TRANSLATORS: this is a window title.
1024                 self.setTitle(_("Phone calls"))
1025
1026         def ok(self):
1027                 self.close()
1028
1029         def display(self, which=None):
1030                 debug("[FritzDisplayCalls] display")
1031                 if which:
1032                         config.plugins.FritzCall.fbfCalls.value = which
1033                         config.plugins.FritzCall.fbfCalls.save()
1034                 else:
1035                         which = config.plugins.FritzCall.fbfCalls.value
1036                 fritzbox.getCalls(self, lambda x: self.gotCalls(x, which), which)
1037
1038         def gotCalls(self, listOfCalls, which):
1039                 debug("[FritzDisplayCalls] gotCalls")
1040                 self.updateStatus(fbfCallsChoices[which] + " (" + str(len(listOfCalls)) + ")")
1041
1042                 directout = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callout.png"))
1043                 directin = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callin.png"))
1044                 directfailed = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callinfailed.png"))
1045                 def pixDir(direct):
1046                         if direct == FBF_OUT_CALLS:
1047                                 direct = directout
1048                         elif direct == FBF_IN_CALLS:
1049                                 direct = directin
1050                         else:
1051                                 direct = directfailed
1052                         return direct
1053
1054                 # debug("[FritzDisplayCalls] gotCalls: %s" %repr(listOfCalls))
1055                 self.list = [(number, date[:6] + ' ' + date[9:14], pixDir(direct), remote, length, here) for (number, date, direct, remote, length, here) in listOfCalls]
1056                 self["entries"].setList(self.list)
1057                 #=======================================================================
1058                 # if len(self.list) > 1:
1059                 #       self["entries"].setIndex(1)
1060                 #=======================================================================
1061
1062         def updateStatus(self, text):
1063                 if self.has_key("statusbar"):
1064                         self["statusbar"].setText(_("Getting calls from FRITZ!Box...") + ' ' + text)
1065
1066         def showEntry(self):
1067                 debug("[FritzDisplayCalls] showEntry")
1068                 cur = self["entries"].getCurrent()
1069                 if cur:
1070                         if cur[0]:
1071                                 debug("[FritzDisplayCalls] showEntry %s" % (cur[0]))
1072                                 number = cur[0]
1073                                 fullname = phonebook.search(cur[0])
1074                                 if fullname:
1075                                         # we have a name for this number
1076                                         name = fullname
1077                                         self.session.open(FritzOfferAction, self, number, name)
1078                                 elif cur[3]:
1079                                         name = cur[3]
1080                                         self.session.open(FritzOfferAction, self, number, name)
1081                                 else:
1082                                         # we don't
1083                                         fullname = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
1084                                         if fullname:
1085                                                 name = fullname
1086                                                 self.session.open(FritzOfferAction, self, number, name)
1087                                         else:
1088                                                 self.session.open(FritzOfferAction, self, number)
1089                         else:
1090                                 # we do not even have a number...
1091                                 self.session.open(MessageBox,
1092                                                   _("UNKNOWN"),
1093                                                   type=MessageBox.TYPE_INFO)
1094
1095
1096 class FritzOfferAction(Screen):
1097
1098         def __init__(self, session, parent, number, name=""):
1099                 # the layout will completely be recalculated in finishLayout
1100                 self.skin = """
1101                         <screen name="FritzOfferAction" title="Do what?" >
1102                                 <widget name="text" size="%d,%d" font="Regular;%d" />
1103                                 <widget name="FacePixmap" size="%d,%d" alphatest="on" />
1104                                 <widget name="key_red_p" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1105                                 <widget name="key_green_p" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1106                                 <widget name="key_yellow_p" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1107                                 <widget name="key_red" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1108                                 <widget name="key_green" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1109                                 <widget name="key_yellow" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1110                         </screen>""" % (
1111                                                         DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
1112                                                         scaleH(22,21), # text
1113                                                         DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
1114                                                         "skin_default/buttons/red.png",
1115                                                         "skin_default/buttons/green.png",
1116                                                         "skin_default/buttons/yellow.png",
1117                                                         ) 
1118                 debug("[FritzOfferAction] init: %s, %s" %(number, name))
1119                 Screen.__init__(self, session)
1120         
1121                 # TRANSLATORS: keep it short, this is a button
1122                 self["key_red"] = Button(_("Lookup"))
1123                 # TRANSLATORS: keep it short, this is a button
1124                 self["key_green"] = Button(_("Call"))
1125                 # TRANSLATORS: keep it short, this is a button
1126                 self["key_yellow"] = Button(_("Save"))
1127                 # TRANSLATORS: keep it short, this is a button
1128                 # self["key_blue"] = Button(_("Search"))
1129
1130                 self["FritzOfferActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1131                 {
1132                         "red": self._lookup,
1133                         "green": self._call,
1134                         "yellow": self._add,
1135                         "cancel": self._exit,
1136                         "ok": self._exit, }, - 2)
1137
1138                 self._session = session
1139                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0":
1140                         number = number[1:]
1141                 self._number = number
1142                 self._name = name.replace("\n", ", ")
1143                 self["text"] = Label(number + "\n\n" + name.replace(", ", "\n"))
1144                 self._parent = parent
1145                 self._lookupState = 0
1146                 self["key_red_p"] = Pixmap()
1147                 self["key_green_p"] = Pixmap()
1148                 self["key_yellow_p"] = Pixmap()
1149                 self["FacePixmap"] = Pixmap()
1150                 self.onLayoutFinish.append(self._finishLayout)
1151                 self.onLayoutFinish.append(self.setWindowTitle)
1152
1153         def setWindowTitle(self):
1154                 # TRANSLATORS: this is a window title.
1155                 self.setTitle(_("Do what?"))
1156
1157         def _finishLayout(self):
1158                 # pylint: disable=W0142
1159                 debug("[FritzCall] FritzOfferAction/finishLayout number: %s/%s" % (self._number, self._name))
1160
1161                 faceFile = findFace(self._number, self._name)
1162                 picPixmap = LoadPixmap(faceFile)
1163                 if not picPixmap:       # that means most probably, that the picture is not 8 bit...
1164                         Notifications.AddNotification(MessageBox, _("Found picture\n\n%s\n\nBut did not load. Probably not PNG, 8-bit") %faceFile, type = MessageBox.TYPE_ERROR)
1165                         picPixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_error.png"))
1166                 picSize = picPixmap.size()
1167                 self["FacePixmap"].instance.setPixmap(picPixmap)
1168
1169                 noButtons = 3
1170                 # recalculate window size
1171                 textSize = self["text"].getSize()
1172                 textSize = (textSize[0]+20, textSize[1]+20) # don't know, why, but size is too small
1173                 debug("[FritzCall] FritzOfferAction/finishLayout textsize: %s/%s" % textSize)
1174                 textSize = eSize(*textSize)
1175                 width = max(scaleH(620, 545), noButtons*145, picSize.width() + textSize.width() + 30)
1176                 height = max(picSize.height()+5, textSize.height()+5, scaleV(-1, 136)) + 5 + 40 + 5
1177                 buttonsGap = (width-noButtons*140)/(noButtons+1)
1178                 buttonsVPos = height-40-5
1179                 wSize = (width, height)
1180                 wSize = eSize(*wSize)
1181
1182                 # center the smaller vertically
1183                 hGap = (width-picSize.width()-textSize.width())/3
1184                 picPos = (hGap, (height-50-picSize.height())/2+5)
1185                 textPos = (hGap+picSize.width()+hGap, (height-50-textSize.height())/2+5)
1186
1187                 # resize screen
1188                 self.instance.resize(wSize)
1189                 # resize text
1190                 self["text"].instance.resize(textSize)
1191                 # resize pixmap
1192                 self["FacePixmap"].instance.resize(picSize)
1193                 # move buttons
1194                 buttonPos = (buttonsGap, buttonsVPos)
1195                 self["key_red_p"].instance.move(ePoint(*buttonPos))
1196                 self["key_red"].instance.move(ePoint(*buttonPos))
1197                 buttonPos = (buttonsGap+140+buttonsGap, buttonsVPos)
1198                 self["key_green_p"].instance.move(ePoint(*buttonPos))
1199                 self["key_green"].instance.move(ePoint(*buttonPos))
1200                 buttonPos = (buttonsGap+140+buttonsGap+140+buttonsGap, buttonsVPos)
1201                 self["key_yellow_p"].instance.move(ePoint(*buttonPos))
1202                 self["key_yellow"].instance.move(ePoint(*buttonPos))
1203                 # move text
1204                 self["text"].instance.move(ePoint(*textPos))
1205                 # move pixmap
1206                 self["FacePixmap"].instance.move(ePoint(*picPos))
1207                 # center window
1208                 self.instance.move(ePoint((DESKTOP_WIDTH-wSize.width())/2, (DESKTOP_HEIGHT-wSize.height())/2))
1209
1210         def _setTextAndResize(self, message):
1211                 # pylint: disable=W0142
1212                 self["text"].instance.resize(eSize(*(DESKTOP_WIDTH, DESKTOP_HEIGHT)))
1213                 self["text"].setText(self._number + "\n\n" + message)
1214                 self._finishLayout()
1215
1216         def _lookup(self):
1217                 phonebookLocation = config.plugins.FritzCall.phonebookLocation.value
1218                 if self._lookupState == 0:
1219                         self._lookupState = 1
1220                         self._setTextAndResize(_("Reverse searching..."))
1221                         ReverseLookupAndNotifier(self._number, self._lookedUp, "UTF-8", config.plugins.FritzCall.country.value)
1222                         return
1223                 if self._lookupState == 1 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.csv")):
1224                         self._setTextAndResize(_("Searching in Outlook export..."))
1225                         self._lookupState = 2
1226                         self._lookedUp(self._number, FritzOutlookCSV.findNumber(self._number, os.path.join(phonebookLocation, "PhoneBook.csv"))) #@UndefinedVariable
1227                         return
1228                 else:
1229                         self._lookupState = 2
1230                 if self._lookupState == 2 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.ldif")):
1231                         self._setTextAndResize(_("Searching in LDIF..."))
1232                         self._lookupState = 0
1233                         FritzLDIF.FindNumber(self._number, open(os.path.join(phonebookLocation, "PhoneBook.ldif")), self._lookedUp)
1234                         return
1235                 else:
1236                         self._lookupState = 0
1237                         self._lookup()
1238
1239         def _lookedUp(self, number, name):
1240                 name = handleReverseLookupResult(name)
1241                 if not name:
1242                         if self._lookupState == 1:
1243                                 name = _("No result from reverse lookup")
1244                         elif self._lookupState == 2:
1245                                 name = _("No result from Outlook export")
1246                         else:
1247                                 name = _("No result from LDIF")
1248                 self._name = name
1249                 self._number = number
1250                 debug("[FritzOfferAction] lookedUp: " + str(name.replace(", ", "\n")))
1251                 self._setTextAndResize(str(name.replace(", ", "\n")))
1252
1253         def _call(self):
1254                 if fritzbox:
1255                         debug("[FritzOfferAction] call: %s" %self._number)
1256                         self.session.open(MessageBox, _("Calling %s") %self._number, type=MessageBox.TYPE_INFO)
1257                         fritzbox.dial(self._number)
1258                 else:
1259                         debug("[FritzOfferAction] call: no fritzbox object?!?!")
1260                         self.session.open(MessageBox, _("FRITZ!Box not available for calling"), type=MessageBox.TYPE_INFO)
1261
1262         def _add(self):
1263                 debug("[FritzOfferAction] add: %s, %s" %(self._number, self._name))
1264                 phonebook.FritzDisplayPhonebook(self._session).add(self._parent, self._number, self._name)
1265                 self._exit()
1266
1267         def _exit(self):
1268                 self.close()
1269
1270
1271 class FritzCallPhonebook:
1272         def __init__(self):
1273                 debug("[FritzCallPhonebook] init")
1274                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1275                 self.phonebook = {}
1276                 if config.plugins.FritzCall.reloadPhonebookTime.value > 0:
1277                         self.loop = eTimer()
1278                         self.loop.callback.append(self.startReload)
1279                         self.loop.start(config.plugins.FritzCall.reloadPhonebookTime.value*60*60*1000, 1)
1280                 self.reload()
1281
1282         def startReload(self):
1283                 self.loop.stop()
1284                 debug("[FritzCallPhonebook] reloading phonebooks " + time.ctime())
1285                 self.reload()
1286                 self.loop.start(config.plugins.FritzCall.reloadPhonebookTime.value*60*60*1000, 1)
1287
1288         def reload(self):
1289                 debug("[FritzCallPhonebook] reload")
1290                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1291                 self.phonebook = {}
1292
1293                 if not config.plugins.FritzCall.enable.value:
1294                         return
1295
1296                 phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
1297                 if config.plugins.FritzCall.phonebook.value and os.path.exists(phonebookFilename):
1298                         debug("[FritzCallPhonebook] reload: read " + phonebookFilename)
1299                         phonebookTxtCorrupt = False
1300                         self.phonebook = {}
1301                         for line in open(phonebookFilename):
1302                                 try:
1303                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
1304                                         line = line.decode("utf-8")
1305                                 except UnicodeDecodeError: # this is just for the case, somebody wrote latin1 chars into PhoneBook.txt
1306                                         try:
1307                                                 line = line.decode("iso-8859-1")
1308                                                 debug("[FritzCallPhonebook] Fallback to ISO-8859-1 in %s" % line)
1309                                                 phonebookTxtCorrupt = True
1310                                         except UnicodeDecodeError:
1311                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1312                                                 phonebookTxtCorrupt = True
1313                                 line = line.encode("utf-8")
1314                                 elems = line.split('#')
1315                                 if len(elems) == 2:
1316                                         try:
1317                                                 debug("[FritzCallPhonebook] reload: Adding '''%s''' with '''%s''' from internal phonebook!" % (__(elems[1].strip()), __(elems[0], False)))
1318                                                 self.phonebook[elems[0]] = elems[1]
1319                                         except ValueError: # how could this possibly happen?!?!
1320                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1321                                                 phonebookTxtCorrupt = True
1322                                 else:
1323                                         debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1324                                         phonebookTxtCorrupt = True
1325                                         
1326                                 #===============================================================
1327                                 # found = re.match("^(\d+)#(.*)$", line)
1328                                 # if found:
1329                                 #       try:
1330                                 #               self.phonebook[found.group(1)] = found.group(2)
1331                                 #       except ValueError: # how could this possibly happen?!?!
1332                                 #               debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1333                                 #               phonebookTxtCorrupt = True
1334                                 # else:
1335                                 #       debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1336                                 #       phonebookTxtCorrupt = True
1337                                 #===============================================================
1338
1339                         if phonebookTxtCorrupt:
1340                                 # dump phonebook to PhoneBook.txt
1341                                 debug("[FritzCallPhonebook] dump Phonebook.txt")
1342                                 try:
1343                                         os.rename(phonebookFilename, phonebookFilename + ".bck")
1344                                         fNew = open(phonebookFilename, 'w')
1345                                         # Beware: strings in phonebook.phonebook are utf-8!
1346                                         for (number, name) in self.phonebook.iteritems():
1347                                                 # Beware: strings in PhoneBook.txt have to be in utf-8!
1348                                                 fNew.write(number + "#" + name.encode("utf-8"))
1349                                         fNew.close()
1350                                 except (IOError, OSError):
1351                                         debug("[FritzCallPhonebook] error renaming or writing to %s" %phonebookFilename)
1352
1353                 if fritzbox and config.plugins.FritzCall.fritzphonebook.value:
1354                         fritzbox.loadFritzBoxPhonebook(self)
1355
1356 #===============================================================================
1357 #               #
1358 #               # read entries from Outlook export
1359 #               #
1360 #               # not reliable with coding yet
1361 #               # 
1362 #               # import csv exported from Outlook 2007 with csv(Windows)
1363 #               csvFilename = "/tmp/PhoneBook.csv"
1364 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(csvFilename):
1365 #                       try:
1366 #                               readOutlookCSV(csvFilename, self.add)
1367 #                               os.rename(csvFilename, csvFilename + ".done")
1368 #                       except ImportError:
1369 #                               debug("[FritzCallPhonebook] CSV import failed" %line)
1370 #===============================================================================
1371
1372                 
1373 #===============================================================================
1374 #               #
1375 #               # read entries from LDIF
1376 #               #
1377 #               # import ldif exported from Thunderbird 2.0.0.19
1378 #               ldifFilename = "/tmp/PhoneBook.ldif"
1379 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(ldifFilename):
1380 #                       try:
1381 #                               parser = MyLDIF(open(ldifFilename), self.add)
1382 #                               parser.parse()
1383 #                               os.rename(ldifFilename, ldifFilename + ".done")
1384 #                       except ImportError:
1385 #                               debug("[FritzCallPhonebook] LDIF import failed" %line)
1386 #===============================================================================
1387
1388         def search(self, number):
1389                 # debug("[FritzCallPhonebook] Searching for %s" %number)
1390                 name = ""
1391                 if not self.phonebook or not number:
1392                         return
1393
1394                 if config.plugins.FritzCall.prefix.value:
1395                         prefix = config.plugins.FritzCall.prefix.value
1396                         if number[0] != '0':
1397                                 number = prefix + number
1398                                 # debug("[FritzCallPhonebook] search: added prefix: %s" %number)
1399                         elif number[:len(prefix)] == prefix and self.phonebook.has_key(number[len(prefix):]):
1400                                 # debug("[FritzCallPhonebook] search: same prefix")
1401                                 name = self.phonebook[number[len(prefix):]]
1402                                 # debug("[FritzCallPhonebook] search: result: %s" %name)
1403                 else:
1404                         prefix = ""
1405                                 
1406                 if not name and self.phonebook.has_key(number):
1407                         name = self.phonebook[number]
1408
1409                 if not name and config.plugins.FritzCall.FritzExtendedSearchNames.value:
1410                         for k in range(len(number)-1, 0, -1):
1411                                 name = self.search(number[:k])
1412                                 if name:
1413                                         debug("[FritzCallPhonebook] search result for shortened number: %s" % name)
1414                                         break
1415
1416                 return name.replace(", ", "\n").strip()
1417
1418         def add(self, number, name):
1419                 '''
1420                 
1421                 @param number: number of entry
1422                 @param name: name of entry, has to be in utf-8
1423                 '''
1424                 debug("[FritzCallPhonebook] add")
1425                 name = name.replace("\n", ", ").replace('#','') # this is just for safety reasons. add should only be called with newlines converted into commas
1426                 self.remove(number)
1427                 self.phonebook[number] = name
1428                 if number and number != 0:
1429                         if config.plugins.FritzCall.phonebook.value:
1430                                 try:
1431                                         name = name.strip() + "\n"
1432                                         string = "%s#%s" % (number, name)
1433                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
1434                                         f = open(os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt"), 'a')
1435                                         f.write(string)
1436                                         f.close()
1437                                         debug("[FritzCallPhonebook] added %s with %s to Phonebook.txt" % (number, name.strip()))
1438                                         return True
1439         
1440                                 except IOError:
1441                                         return False
1442
1443         def remove(self, number):
1444                 if number in self.phonebook:
1445                         debug("[FritzCallPhonebook] remove entry in phonebook")
1446                         del self.phonebook[number]
1447                         if config.plugins.FritzCall.phonebook.value:
1448                                 try:
1449                                         phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
1450                                         debug("[FritzCallPhonebook] remove entry in Phonebook.txt")
1451                                         fOld = open(phonebookFilename, 'r')
1452                                         fNew = open(phonebookFilename + str(os.getpid()), 'w')
1453                                         line = fOld.readline()
1454                                         while (line):
1455                                                 elems = line.split('#')
1456                                                 if len(elems) == 2 and not elems[0] == number:
1457                                                         fNew.write(line)
1458                                                 line = fOld.readline()
1459                                         fOld.close()
1460                                         fNew.close()
1461                                         # os.remove(phonebookFilename)
1462                                         eBackgroundFileEraser.getInstance().erase(phonebookFilename)
1463                                         os.rename(phonebookFilename + str(os.getpid()), phonebookFilename)
1464                                         debug("[FritzCallPhonebook] removed %s from Phonebook.txt" % number)
1465                                         return True
1466         
1467                                 except (IOError, OSError):
1468                                         debug("[FritzCallPhonebook] error removing %s from %s" %(number, phonebookFilename))
1469                 return False
1470
1471         class FritzDisplayPhonebook(Screen, HelpableScreen, NumericalTextInput):
1472         
1473                 def __init__(self, session):
1474                         self.entriesWidth = DESKTOP_WIDTH * scaleH(75, 85)/100
1475                         self.height = DESKTOP_HEIGHT * 0.75
1476                         numberFieldWidth = scaleH(220, 160)
1477                         fieldWidth = self.entriesWidth -5 -numberFieldWidth -10
1478                         fontSize = scaleV(22, 18)
1479                         fontHeight = scaleV(24, 20)
1480                         buttonGap = (self.entriesWidth-4*140)/5
1481                         debug("[FritzDisplayPhonebook] width: " + str(self.entriesWidth))
1482                         self.skin = """
1483                                 <screen name="FritzDisplayPhonebook" position="center,center" size="%d,%d" title="Phonebook" >
1484                                         <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1485                                         <widget source="entries" render="Listbox" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1">
1486                                                 <convert type="TemplatedMultiContent">
1487                                                         {"template": [
1488                                                                         MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_LEFT, text = 1), # index 0 is the name, index 1 is shortname
1489                                                                         MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_LEFT, text = 2), # index 2 is number
1490                                                                 ],
1491                                                         "fonts": [gFont("Regular", %d)],
1492                                                         "itemHeight": %d
1493                                                         }
1494                                                 </convert>
1495                                         </widget>
1496                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1497                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1498                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1499                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1500                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1501                                         <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1502                                         <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1503                                         <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1504                                         <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1505                                 </screen>""" % (
1506                                                 # scaleH(90, 75), scaleV(100, 73), # position 
1507                                                 self.entriesWidth, self.height, # size
1508                                                 self.entriesWidth, # eLabel width
1509                                                 scaleH(40, 5), scaleV(20, 5), # entries position
1510                                                 self.entriesWidth-scaleH(40, 5), self.height-scaleV(20, 5)-5-5-40, # entries size
1511                                                 0, 0, fieldWidth, scaleH(24,20), # name pos/size
1512                                                 fieldWidth +5, 0, numberFieldWidth, scaleH(24,20), # dir pos/size
1513                                                 fontSize, # fontsize
1514                                                 fontHeight, # itemHeight
1515                                                 self.height-40-5, # eLabel position vertical
1516                                                 self.entriesWidth, # eLabel width
1517                                                 buttonGap, self.height-40, "skin_default/buttons/red.png", # ePixmap red
1518                                                 2*buttonGap+140, self.height-40, "skin_default/buttons/green.png", # ePixmap green
1519                                                 3*buttonGap+2*140, self.height-40, "skin_default/buttons/yellow.png", # ePixmap yellow
1520                                                 4*buttonGap+3*140, self.height-40, "skin_default/buttons/blue.png", # ePixmap blue
1521                                                 buttonGap, self.height-40, scaleV(22, 21), # widget red
1522                                                 2*buttonGap+140, self.height-40, scaleV(22, 21), # widget green
1523                                                 3*buttonGap+2*140, self.height-40, scaleV(22, 21), # widget yellow
1524                                                 4*buttonGap+3*140, self.height-40, scaleV(22, 21), # widget blue
1525                                                 )
1526         
1527                         # debug("[FritzDisplayCalls] skin: " + self.skin)
1528                         Screen.__init__(self, session)
1529                         NumericalTextInput.__init__(self)
1530                         HelpableScreen.__init__(self)
1531                 
1532                         # TRANSLATORS: keep it short, this is a button
1533                         self["key_red"] = Button(_("Delete"))
1534                         # TRANSLATORS: keep it short, this is a button
1535                         self["key_green"] = Button(_("New"))
1536                         # TRANSLATORS: keep it short, this is a button
1537                         self["key_yellow"] = Button(_("Edit"))
1538                         # TRANSLATORS: keep it short, this is a button
1539                         self["key_blue"] = Button(_("Search"))
1540         
1541                         self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1542                         {
1543                                 "red": self.delete,
1544                                 "green": self.add,
1545                                 "yellow": self.edit,
1546                                 "blue": self.search,
1547                                 "cancel": self.exit,
1548                                 "ok": self.showEntry, }, - 2)
1549         
1550                         # TRANSLATORS: keep it short, this is a help text
1551                         self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
1552                         # TRANSLATORS: keep it short, this is a help text
1553                         self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1554                         # TRANSLATORS: keep it short, this is a help text
1555                         self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
1556                         # TRANSLATORS: keep it short, this is a help text
1557                         self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
1558                         # TRANSLATORS: keep it short, this is a help text
1559                         self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
1560                         # TRANSLATORS: keep it short, this is a help text
1561                         self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
1562         
1563                         self["entries"] = List([])
1564                         debug("[FritzCallPhonebook] displayPhonebook init")
1565                         self.help_window = None
1566                         self.sortlist = []
1567                         self.onLayoutFinish.append(self.setWindowTitle)
1568                         self.display()
1569
1570                 def setWindowTitle(self):
1571                         # TRANSLATORS: this is a window title.
1572                         self.setTitle(_("Phonebook"))
1573
1574                 def display(self, filterNumber=""):
1575                         debug("[FritzCallPhonebook] displayPhonebook/display")
1576                         self.sortlist = []
1577                         # Beware: strings in phonebook.phonebook are utf-8!
1578                         sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
1579                         for (low, name, number) in sortlistHelp:
1580                                 if number == "01234567890":
1581                                         continue
1582                                 try:
1583                                         low = low.decode("utf-8")
1584                                 except UnicodeDecodeError:  # this should definitely not happen
1585                                         try:
1586                                                 low = low.decode("iso-8859-1")
1587                                         except UnicodeDecodeError:
1588                                                 debug("[FritzCallPhonebook] displayPhonebook/display: corrupt phonebook entry for %s" % number)
1589                                                 # self.session.open(MessageBox, _("Corrupt phonebook entry\nfor number %s\nDeleting.") %number, type = MessageBox.TYPE_ERROR)
1590                                                 phonebook.remove(number)
1591                                                 continue
1592                                 else:
1593                                         if filterNumber:
1594                                                 filterNumber = filterNumber.lower()
1595                                                 if low.find(filterNumber) == - 1:
1596                                                         continue
1597                                         name = name.strip().decode("utf-8")
1598                                         number = number.strip().decode("utf-8")
1599                                         comma = name.find(',')
1600                                         if comma != -1:
1601                                                 shortname = name[:comma]
1602                                         else:
1603                                                 shortname = name
1604                                         number = number.encode("utf-8", "replace")
1605                                         name = name.encode("utf-8", "replace")
1606                                         shortname = shortname.encode('utf-8', 'replace')
1607                                         self.sortlist.append((name, shortname, number))
1608                                 
1609                         self["entries"].setList(self.sortlist)
1610         
1611                 def showEntry(self):
1612                         cur = self["entries"].getCurrent()
1613                         if cur:
1614                                 debug("[FritzCallPhonebook] displayPhonebook/showEntry %s" % (repr(cur)))
1615                                 number = cur[2]
1616                                 name = cur[0]
1617                                 self.session.open(FritzOfferAction, self, number, name)
1618         
1619                 def delete(self):
1620                         cur = self["entries"].getCurrent()
1621                         if cur:
1622                                 debug("[FritzCallPhonebook] displayPhonebook/delete %s" % (repr(cur)))
1623                                 self.session.openWithCallback(
1624                                         self.deleteConfirmed,
1625                                         MessageBox,
1626                                         _("Do you really want to delete entry for\n\n%(number)s\n\n%(name)s?") 
1627                                         % { 'number':str(cur[2]), 'name':str(cur[0]).replace(", ", "\n") }
1628                                                                 )
1629                         else:
1630                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1631         
1632                 def deleteConfirmed(self, ret):
1633                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed")
1634                         #
1635                         # if ret: delete number from sortlist, delete number from phonebook.phonebook and write it to disk
1636                         #
1637                         cur = self["entries"].getCurrent()
1638                         if cur:
1639                                 if ret:
1640                                         # delete number from sortlist, delete number from phonebook.phonebook and write it to disk
1641                                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed %s" % (repr(cur)))
1642                                         phonebook.remove(cur[2])
1643                                         self.display()
1644                                 # else:
1645                                         # self.session.open(MessageBox, _("Not deleted."), MessageBox.TYPE_INFO)
1646                         else:
1647                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1648         
1649                 def add(self, parent=None, number="", name=""):
1650                         class AddScreen(Screen, ConfigListScreen):
1651                                 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
1652         
1653                                 def __init__(self, session, parent, number="", name=""):
1654                                         #
1655                                         # setup screen with two ConfigText and OK and ABORT button
1656                                         # 
1657                                         noButtons = 2
1658                                         width = max(scaleH(-1, 570), noButtons*140)
1659                                         height = scaleV(-1, 100) # = 5 + 126 + 40 + 5; 6 lines of text possible
1660                                         buttonsGap = (width-noButtons*140)/(noButtons+1)
1661                                         buttonsVPos = height-40-5
1662                                         self.skin = """
1663                                                 <screen position="center,center" size="%d,%d" title="Add entry to phonebook" >
1664                                                 <widget name="config" position="5,5" size="%d,%d" scrollbarMode="showOnDemand" />
1665                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1666                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1667                                                 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1668                                                 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1669                                                 </screen>""" % (
1670                                                                                 width, height,
1671                                                                                 width - 5 - 5, height - 5 - 40 - 5,
1672                                                                                 buttonsGap, buttonsVPos, "skin_default/buttons/red.png",
1673                                                                                 buttonsGap+140+buttonsGap, buttonsVPos, "skin_default/buttons/green.png",
1674                                                                                 buttonsGap, buttonsVPos,
1675                                                                                 buttonsGap+140+buttonsGap, buttonsVPos,
1676                                                                                 )
1677                                         Screen.__init__(self, session)
1678                                         self.session = session
1679                                         self.parent = parent
1680                                         # TRANSLATORS: keep it short, this is a button
1681                                         self["key_red"] = Button(_("Cancel"))
1682                                         # TRANSLATORS: keep it short, this is a button
1683                                         self["key_green"] = Button(_("OK"))
1684                                         self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
1685                                         {
1686                                                 "cancel": self.cancel,
1687                                                 "red": self.cancel,
1688                                                 "green": self.add,
1689                                                 "ok": self.add,
1690                                         }, - 2)
1691         
1692                                         self.list = [ ]
1693                                         ConfigListScreen.__init__(self, self.list, session=session)
1694                                         self.name = name
1695                                         self.number = number
1696                                         config.plugins.FritzCall.name.value = name
1697                                         config.plugins.FritzCall.number.value = number
1698                                         self.list.append(getConfigListEntry(_("Name"), config.plugins.FritzCall.name))
1699                                         self.list.append(getConfigListEntry(_("Number"), config.plugins.FritzCall.number))
1700                                         self["config"].list = self.list
1701                                         self["config"].l.setList(self.list)
1702                                         self.onLayoutFinish.append(self.setWindowTitle)
1703                         
1704                                 def setWindowTitle(self):
1705                                         # TRANSLATORS: this is a window title.
1706                                         self.setTitle(_("Add entry to phonebook"))
1707
1708                                 def add(self):
1709                                         # get texts from Screen
1710                                         # add (number,name) to sortlist and phonebook.phonebook and disk
1711                                         self.name = config.plugins.FritzCall.name.value
1712                                         self.number = config.plugins.FritzCall.number.value
1713                                         if not self.number or not self.name:
1714                                                 self.session.open(MessageBox, _("Entry incomplete."), type=MessageBox.TYPE_ERROR)
1715                                                 return
1716                                         # add (number,name) to sortlist and phonebook.phonebook and disk
1717         #                                       oldname = phonebook.search(self.number)
1718         #                                       if oldname:
1719         #                                               self.session.openWithCallback(
1720         #                                                       self.overwriteConfirmed,
1721         #                                                       MessageBox,
1722         #                                                       _("Do you really want to overwrite entry for %(number)s\n\n%(name)s\n\nwith\n\n%(newname)s?")
1723         #                                                       % {
1724         #                                                       'number':self.number,
1725         #                                                       'name': oldname,
1726         #                                                       'newname':self.name.replace(", ","\n")
1727         #                                                       }
1728         #                                                       )
1729         #                                               self.close()
1730         #                                               return
1731                                         phonebook.add(self.number, self.name)
1732                                         self.close()
1733                                         self.parent.display()
1734         
1735                                 def overwriteConfirmed(self, ret):
1736                                         if ret:
1737                                                 phonebook.remove(self.number)
1738                                                 phonebook.add(self.number, self.name)
1739                                                 self.parent.display()
1740         
1741                                 def cancel(self):
1742                                         self.close()
1743         
1744                         debug("[FritzCallPhonebook] displayPhonebook/add")
1745                         if not parent:
1746                                 parent = self
1747                         self.session.open(AddScreen, parent, number, name)
1748         
1749                 def edit(self):
1750                         debug("[FritzCallPhonebook] displayPhonebook/edit")
1751                         cur = self["entries"].getCurrent()
1752                         if cur is None:
1753                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1754                         else:
1755                                 self.add(self, cur[2], cur[0])
1756         
1757                 def search(self):
1758                         debug("[FritzCallPhonebook] displayPhonebook/search")
1759                         self.help_window = self.session.instantiateDialog(NumericalTextInputHelpDialog, self)
1760                         self.help_window.show()
1761                         # VirtualKeyboard instead of InputBox?
1762                         self.session.openWithCallback(self.doSearch, InputBox, _("Enter Search Terms"), _("Search phonebook"))
1763         
1764                 def doSearch(self, searchTerms):
1765                         if not searchTerms:
1766                                 searchTerms = ""
1767                         debug("[FritzCallPhonebook] displayPhonebook/doSearch: " + searchTerms)
1768                         if self.help_window:
1769                                 self.session.deleteDialog(self.help_window)
1770                                 self.help_window = None
1771                         self.display(searchTerms)
1772         
1773                 def exit(self):
1774                         self.close()
1775
1776 phonebook = FritzCallPhonebook()
1777
1778
1779
1780 class FritzCallSetup(Screen, ConfigListScreen, HelpableScreen):
1781
1782         def __init__(self, session, args=None): #@UnusedVariable # pylint: disable=W0613
1783                 self.width = scaleH(20+4*(140+90)+2*(35+40)+20, 4*140+2*35)
1784                 width = self.width
1785                 debug("[FritzCallSetup] width: " + str(self.width))
1786                 self.skin = """
1787                         <screen name="FritzCallSetup" position="center,center" size="%d,%d" title="FritzCall Setup" >
1788                         <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1789                         <widget name="consideration" position="%d,%d" halign="center" size="%d,%d" font="Regular;%d" backgroundColor="#20040404" transparent="1" />
1790                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1791                         <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" backgroundColor="#20040404" transparent="1" />
1792                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1793                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1794                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1795                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1796                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1797                         <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1798                         <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1799                         <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1800                         <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1801                         <ePixmap position="%d,%d" zPosition="4" size="35,25" pixmap="%s" transparent="1" alphatest="on" />
1802                         <ePixmap position="%d,%d" zPosition="4" size="35,25" pixmap="%s" transparent="1" alphatest="on" />
1803                         </screen>""" % (
1804                                                 # (DESKTOP_WIDTH-width)/2, scaleV(100, 73), # position 
1805                                                 width, scaleV(560, 430), # size
1806                                                 width, # eLabel width
1807                                                 scaleH(40, 20), scaleV(10, 5), # consideration position
1808                                                 scaleH(width-80, width-40), scaleV(25, 45), # consideration size
1809                                                 scaleV(22, 20), # consideration font size
1810                                                 scaleV(40, 50), # eLabel position vertical
1811                                                 width, # eLabel width
1812                                                 scaleH(40, 5), scaleV(60, 57), # config position
1813                                                 scaleH(width-80, width-10), scaleV(453, 328), # config size
1814                                                 scaleV(518, 390), # eLabel position vertical
1815                                                 width, # eLabel width
1816                                                 scaleH(20, 0), scaleV(525, 395), "skin_default/buttons/red.png", # pixmap red
1817                                                 scaleH(20+140+90, 140), scaleV(525, 395), "skin_default/buttons/green.png", # pixmap green
1818                                                 scaleH(20+2*(140+90), 2*140), scaleV(525, 395), "skin_default/buttons/yellow.png", # pixmap yellow
1819                                                 scaleH(20+3*(140+90), 3*140), scaleV(525, 395), "skin_default/buttons/blue.png", # pixmap blue
1820                                                 scaleH(20, 0), scaleV(525, 395), scaleV(21, 21), # widget red
1821                                                 scaleH(20+(140+90), 140), scaleV(525, 395), scaleV(21, 21), # widget green
1822                                                 scaleH(20+2*(140+90), 2*140), scaleV(525, 395), scaleV(21, 21), # widget yellow
1823                                                 scaleH(20+3*(140+90), 3*140), scaleV(525, 395), scaleV(21, 21), # widget blue
1824                                                 scaleH(20+4*(140+90), 4*140), scaleV(532, 402), "skin_default/buttons/key_info.png", # button info
1825                                                 scaleH(20+4*(140+90)+(35+40), 4*140+35), scaleV(532, 402), "skin_default/buttons/key_menu.png", # button menu
1826                                                                                                         )
1827
1828                 Screen.__init__(self, session)
1829                 HelpableScreen.__init__(self)
1830                 self.session = session
1831
1832                 self["consideration"] = Label(_("You need to enable the monitoring on your FRITZ!Box by dialing #96*5*!"))
1833                 self.list = []
1834
1835                 # Initialize Buttons
1836                 # TRANSLATORS: keep it short, this is a button
1837                 self["key_red"] = Button(_("Cancel"))
1838                 # TRANSLATORS: keep it short, this is a button
1839                 self["key_green"] = Button(_("OK"))
1840                 # TRANSLATORS: keep it short, this is a button
1841                 self["key_yellow"] = Button(_("Phone calls"))
1842                 # TRANSLATORS: keep it short, this is a button
1843                 self["key_blue"] = Button(_("Phonebook"))
1844                 # TRANSLATORS: keep it short, this is a button
1845                 self["key_info"] = Button(_("About FritzCall"))
1846                 # TRANSLATORS: keep it short, this is a button
1847                 self["key_menu"] = Button(_("FRITZ!Box Fon Status"))
1848
1849                 self["setupActions"] = ActionMap(["ColorActions", "OkCancelActions", "MenuActions", "EPGSelectActions"],
1850                 {
1851                         "red": self.cancel,
1852                         "green": self.save,
1853                         "yellow": self.displayCalls,
1854                         "blue": self.displayPhonebook,
1855                         "cancel": self.cancel,
1856                         "ok": self.save,
1857                         "menu": self.menu,
1858                         "info": self.about,
1859                 }, - 2)
1860
1861                 # TRANSLATORS: keep it short, this is a help text
1862                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("quit"))]))
1863                 # TRANSLATORS: keep it short, this is a help text
1864                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("save and quit"))]))
1865                 # TRANSLATORS: keep it short, this is a help text
1866                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("display calls"))]))
1867                 # TRANSLATORS: keep it short, this is a help text
1868                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("display phonebook"))]))
1869                 # TRANSLATORS: keep it short, this is a help text
1870                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("save and quit"))]))
1871                 # TRANSLATORS: keep it short, this is a help text
1872                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("quit"))]))
1873                 # TRANSLATORS: keep it short, this is a help text
1874                 self.helpList.append((self["setupActions"], "MenuActions", [("menu", _("FRITZ!Box Fon Status"))]))
1875                 # TRANSLATORS: keep it short, this is a help text
1876                 self.helpList.append((self["setupActions"], "EPGSelectActions", [("info", _("About FritzCall"))]))
1877
1878                 ConfigListScreen.__init__(self, self.list, session=session)
1879
1880                 # get new list of locations for PhoneBook.txt
1881                 self._mountedDevs = getMountedDevs()
1882                 self.createSetup()
1883                 self.onLayoutFinish.append(self.setWindowTitle)
1884
1885         def setWindowTitle(self):
1886                 # TRANSLATORS: this is a window title.
1887                 self.setTitle(_("FritzCall Setup") + " (" + "$Revision: 773 $"[1: - 1] + "$Date: 2013-04-07 10:03:55 +0200 (Sun, 07 Apr 2013) $"[7:23] + ")")
1888
1889         def keyLeft(self):
1890                 ConfigListScreen.keyLeft(self)
1891                 self.createSetup()
1892
1893         def keyRight(self):
1894                 ConfigListScreen.keyRight(self)
1895                 self.createSetup()
1896
1897         def createSetup(self):
1898                 self.list = [ ]
1899                 self.list.append(getConfigListEntry(_("Call monitoring"), config.plugins.FritzCall.enable))
1900                 if config.plugins.FritzCall.enable.value:
1901                         self.list.append(getConfigListEntry(_("FRITZ!Box FON address (Name or IP)"), config.plugins.FritzCall.hostname))
1902                         self.list.append(getConfigListEntry(_("FRITZ!Box FON firmware version"), config.plugins.FritzCall.fwVersion))
1903
1904                         self.list.append(getConfigListEntry(_("Show after Standby"), config.plugins.FritzCall.afterStandby))
1905
1906                         self.list.append(getConfigListEntry(_("Show only calls for specific MSN"), config.plugins.FritzCall.filter))
1907                         if config.plugins.FritzCall.filter.value:
1908                                 self.list.append(getConfigListEntry(_("MSN to show (separated by ,)"), config.plugins.FritzCall.filtermsn))
1909                                 self.list.append(getConfigListEntry(_("Filter also list of calls"), config.plugins.FritzCall.filterCallList))
1910                         self.list.append(getConfigListEntry(_("Mute on call"), config.plugins.FritzCall.muteOnCall))
1911
1912                         self.list.append(getConfigListEntry(_("Show Outgoing Calls"), config.plugins.FritzCall.showOutgoing))
1913                         # not only for outgoing: config.plugins.FritzCall.showOutgoing.value:
1914                         self.list.append(getConfigListEntry(_("Areacode to add to calls without one (if necessary)"), config.plugins.FritzCall.prefix))
1915                         self.list.append(getConfigListEntry(_("Timeout for Call Notifications (seconds)"), config.plugins.FritzCall.timeout))
1916                         self.list.append(getConfigListEntry(_("Reverse Lookup Caller ID (select country below)"), config.plugins.FritzCall.lookup))
1917                         if config.plugins.FritzCall.lookup.value:
1918                                 self.list.append(getConfigListEntry(_("Country"), config.plugins.FritzCall.country))
1919
1920                         if config.plugins.FritzCall.fwVersion.value == "05.50":
1921                                 self.list.append(getConfigListEntry(_("User name Accessing FRITZ!Box"), config.plugins.FritzCall.username))
1922                         # TODO: make password unreadable?
1923                         self.list.append(getConfigListEntry(_("Password Accessing FRITZ!Box"), config.plugins.FritzCall.password))
1924                         self.list.append(getConfigListEntry(_("Extension number to initiate call on"), config.plugins.FritzCall.extension))
1925                         self.list.append(getConfigListEntry(_("Read PhoneBook from FRITZ!Box"), config.plugins.FritzCall.fritzphonebook))
1926                         if config.plugins.FritzCall.fritzphonebook.value:
1927                                 self.list.append(getConfigListEntry(_("FRITZ!Box PhoneBook to read"), config.plugins.FritzCall.fritzphonebookName))
1928                                 self.list.append(getConfigListEntry(_("Append type of number"), config.plugins.FritzCall.showType))
1929                                 self.list.append(getConfigListEntry(_("Append shortcut number"), config.plugins.FritzCall.showShortcut))
1930                                 self.list.append(getConfigListEntry(_("Append vanity name"), config.plugins.FritzCall.showVanity))
1931
1932                         self.list.append(getConfigListEntry(_("Use internal PhoneBook"), config.plugins.FritzCall.phonebook))
1933                         if config.plugins.FritzCall.phonebook.value:
1934                                 if config.plugins.FritzCall.phonebookLocation.value in self._mountedDevs:
1935                                         config.plugins.FritzCall.phonebookLocation.setChoices(self._mountedDevs, config.plugins.FritzCall.phonebookLocation.value)
1936                                 else:
1937                                         config.plugins.FritzCall.phonebookLocation.setChoices(self._mountedDevs)
1938                                 path = config.plugins.FritzCall.phonebookLocation.value
1939                                 # check whether we can write to PhoneBook.txt
1940                                 if os.path.exists(os.path.join(path[0], "PhoneBook.txt")):
1941                                         if not os.access(os.path.join(path[0], "PhoneBook.txt"), os.W_OK):
1942                                                 debug("[FritzCallSetup] createSetup: %s/PhoneBook.txt not writable, resetting to default" %(path[0]))
1943                                                 config.plugins.FritzCall.phonebookLocation.setChoices(self._mountedDevs)
1944                                 elif not (os.path.isdir(path[0]) and os.access(path[0], os.W_OK|os.X_OK)):
1945                                         debug("[FritzCallSetup] createSetup: directory %s not writable, resetting to default" %(path[0]))
1946                                         config.plugins.FritzCall.phonebookLocation.setChoices(self._mountedDevs)
1947
1948                                 self.list.append(getConfigListEntry(_("PhoneBook Location"), config.plugins.FritzCall.phonebookLocation))
1949                                 if config.plugins.FritzCall.lookup.value:
1950                                         self.list.append(getConfigListEntry(_("Automatically add new Caller to PhoneBook"), config.plugins.FritzCall.addcallers))
1951
1952                         if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
1953                                 self.list.append(getConfigListEntry(_("Reload interval for phonebooks (hours)"), config.plugins.FritzCall.reloadPhonebookTime))
1954
1955                         if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
1956                                 self.list.append(getConfigListEntry(_("Extended Search for names"), config.plugins.FritzCall.FritzExtendedSearchNames))
1957                         self.list.append(getConfigListEntry(_("Extended Search for faces"), config.plugins.FritzCall.FritzExtendedSearchFaces))
1958
1959                         self.list.append(getConfigListEntry(_("Strip Leading 0"), config.plugins.FritzCall.internal))
1960                         # self.list.append(getConfigListEntry(_("Default display mode for FRITZ!Box calls"), config.plugins.FritzCall.fbfCalls))
1961                         self.list.append(getConfigListEntry(_("Display connection infos"), config.plugins.FritzCall.connectionVerbose))
1962                         self.list.append(getConfigListEntry(_("Ignore callers with no phone number"), config.plugins.FritzCall.ignoreUnknown))
1963                         self.list.append(getConfigListEntry(_("Debug"), config.plugins.FritzCall.debug))
1964
1965                 self["config"].list = self.list
1966                 self["config"].l.setList(self.list)
1967
1968         def save(self):
1969 #               debug("[FritzCallSetup] save"
1970                 for x in self["config"].list:
1971                         x[1].save()
1972                 if config.plugins.FritzCall.phonebookLocation.isChanged():
1973                         global phonebook
1974                         phonebook = FritzCallPhonebook()
1975                 if fritz_call:
1976                         if config.plugins.FritzCall.enable.value:
1977                                 fritz_call.connect()
1978                         else:
1979                                 fritz_call.shutdown()
1980                 self.close()
1981
1982         def cancel(self):
1983 #               debug("[FritzCallSetup] cancel"
1984                 for x in self["config"].list:
1985                         x[1].cancel()
1986                 self.close()
1987
1988         def displayCalls(self):
1989                 if config.plugins.FritzCall.enable.value:
1990                         if fritzbox:
1991                                 self.session.open(FritzDisplayCalls)
1992                         else:
1993                                 self.session.open(MessageBox, _("Cannot get calls from FRITZ!Box"), type=MessageBox.TYPE_INFO)
1994                 else:
1995                         self.session.open(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
1996
1997         def displayPhonebook(self):
1998                 if phonebook:
1999                         if config.plugins.FritzCall.enable.value:
2000                                 self.session.open(phonebook.FritzDisplayPhonebook)
2001                         else:
2002                                 self.session.open(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
2003                 else:
2004                         self.session.open(MessageBox, _("No phonebook"), type=MessageBox.TYPE_INFO)
2005
2006         def about(self):
2007                 self.session.open(FritzAbout)
2008
2009         def menu(self):
2010                 if config.plugins.FritzCall.enable.value:
2011                         if fritzbox and fritzbox.info:
2012                                 self.session.open(FritzMenu)
2013                         else:
2014                                 self.session.open(MessageBox, _("Cannot get infos from FRITZ!Box yet; try later"), type=MessageBox.TYPE_INFO)
2015                 else:
2016                         self.session.open(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
2017
2018 standbyMode = False
2019
2020 class FritzCallList:
2021         def __init__(self):
2022                 self.callList = [ ]
2023         
2024         def add(self, event, date, number, caller, phone):
2025                 debug("[FritzCallList] add: %s %s" % (number, caller))
2026                 if len(self.callList) > 10:
2027                         if self.callList[0] != "Start":
2028                                 self.callList[0] = "Start"
2029                         del self.callList[1]
2030
2031                 self.callList.append((event, number, date, caller, phone))
2032         
2033         def display(self):
2034                 debug("[FritzCallList] display")
2035                 global standbyMode
2036                 standbyMode = False
2037                 # Standby.inStandby.onClose.remove(self.display) object does not exist anymore...
2038                 # build screen from call list
2039                 text = "\n"
2040
2041                 if not self.callList:
2042                         text = _("no calls") 
2043                 else:
2044                         if self.callList[0] == "Start":
2045                                 text = text + _("Last 10 calls:\n")
2046                                 del self.callList[0]
2047         
2048                         for call in self.callList:
2049                                 (event, number, date, caller, phone) = call
2050                                 if event == "RING":
2051                                         direction = "->"
2052                                 else:
2053                                         direction = "<-"
2054         
2055                                 # shorten the date info
2056                                 date = date[:6] + date[9:14]
2057         
2058                                 # our phone could be of the form "0123456789 (home)", then we only take "home"
2059                                 oBrack = phone.find('(')
2060                                 cBrack = phone.find(')')
2061                                 if oBrack != -1 and cBrack != -1:
2062                                         phone = phone[oBrack+1:cBrack]
2063         
2064                                 # should not happen, for safety reasons
2065                                 if not caller:
2066                                         caller = _("UNKNOWN")
2067                                 
2068                                 #  if we have an unknown number, show the number
2069                                 if caller == _("UNKNOWN") and number != "":
2070                                         caller = number
2071                                 else:
2072                                         # strip off the address part of the remote caller/callee, if there is any
2073                                         nl = caller.find('\n')
2074                                         if nl != -1:
2075                                                 caller = caller[:nl]
2076                                         elif caller[0] == '[' and caller[-1] == ']':
2077                                                 # that means, we've got an unknown number with a city added from avon.dat 
2078                                                 if (len(number) + 1 + len(caller) + len(phone)) <= 40:
2079                                                         caller = number + ' ' + caller
2080                                                 else:
2081                                                         caller = number
2082         
2083                                 while (len(caller) + len(phone)) > 40:
2084                                         if len(caller) > len(phone):
2085                                                 caller = caller[: - 1]
2086                                         else:
2087                                                 phone = phone[: - 1]
2088         
2089                                 text = text + "%s %s %s %s\n" % (date, caller, direction, phone)
2090                                 debug("[FritzCallList] display: '%s %s %s %s'" % (date, caller, direction, phone))
2091
2092                 # display screen
2093                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO)
2094                 # TODO please HELP: from where can I get a session?
2095                 # my_global_session.open(FritzDisplayCalls, text)
2096                 self.callList = [ ]
2097
2098 callList = FritzCallList()
2099
2100 def findFace(number, name):
2101         debug("[FritzCall] findFace number/name: %s/%s" % (number, name))
2102         if name:
2103                 sep = name.find(',')
2104                 if sep != -1:
2105                         name = name[:sep]
2106                 sep = name.find('\n')
2107                 if sep != -1:
2108                         name = name[:sep]
2109         else:
2110                 name = _("UNKNOWN")
2111
2112         facesDir = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "FritzCallFaces")
2113         numberFile = os.path.join(facesDir, number)
2114         nameFile = os.path.join(facesDir, name)
2115         facesFile = ""
2116         if number and os.path.exists(numberFile):
2117                 facesFile = numberFile
2118         elif number and os.path.exists(numberFile + ".png"):
2119                 facesFile = numberFile + ".png"
2120         elif number and os.path.exists(numberFile + ".PNG"):
2121                 facesFile = numberFile + ".PNG"
2122         elif name and os.path.exists(nameFile + ".png"):
2123                 facesFile = nameFile + ".png"
2124         elif name and os.path.exists(nameFile + ".PNG"):
2125                 facesFile = nameFile + ".PNG"
2126         else:
2127                 sep = name.find(' (')
2128                 if sep != -1:
2129                         name = name[:sep]
2130                 nameFile = os.path.join(facesDir, name)
2131                 if name and os.path.exists(nameFile + ".png"):
2132                         facesFile = nameFile + ".png"
2133                 elif name and os.path.exists(nameFile + ".PNG"):
2134                         facesFile = nameFile + ".PNG"
2135
2136         if not facesFile and config.plugins.FritzCall.FritzExtendedSearchFaces.value:
2137                 for k in range(len(number)-1, 0, -1):
2138                         if os.path.exists(os.path.join(facesDir, number[:k])):
2139                                 facesFile = os.path.join(facesDir, number[:k])
2140                                 break
2141                         elif os.path.exists(os.path.join(facesDir, number[:k]) + ".png"):
2142                                 facesFile = os.path.join(facesDir, number[:k]) + ".png"
2143                                 break
2144                         elif os.path.exists(os.path.join(facesDir, number[:k]) + ".PNG"):
2145                                 facesFile = os.path.join(facesDir, number[:k]) + ".PNG"
2146                                 break
2147
2148         if not facesFile:
2149                 facesFile = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_info.png")
2150
2151         debug("[FritzCall] findFace result: %s" % (facesFile))
2152         return facesFile
2153
2154 class MessageBoxPixmap(Screen):
2155         def __init__(self, session, text, number = "", name = "", timeout = -1):
2156                 self.skin = """
2157         <screen name="MessageBoxPixmap" position="center,center" size="600,10" title="New Call">
2158                 <widget name="text" position="115,8" size="520,0" font="Regular;%d" />
2159                 <widget name="InfoPixmap" pixmap="%s" position="5,5" size="100,100" alphatest="on" />
2160         </screen>
2161                 """ % (
2162                         # scaleH(350, 60), scaleV(175, 245),
2163                         scaleV(24, 22), resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_info.png")
2164                         )
2165                 debug("[FritzCall] MessageBoxPixmap number: %s" % number)
2166                 Screen.__init__(self, session)
2167                 # MessageBox.__init__(self, session, text, type=MessageBox.TYPE_INFO, timeout=timeout)
2168                 self["text"] = Label(text)
2169                 self["InfoPixmap"] = Pixmap()
2170                 self._session = session
2171                 self._number = number
2172                 self._name = name
2173                 self._timerRunning = False
2174                 self._timer = None
2175                 self._timeout = timeout
2176                 self._origTitle = None
2177                 self._initTimeout()
2178                 self.onLayoutFinish.append(self._finishLayout)
2179                 self["actions"] = ActionMap(["OkCancelActions"],
2180                 {
2181                         "cancel": self._exit,
2182                         "ok": self._exit, }, - 2)
2183
2184         def _finishLayout(self):
2185                 # pylint: disable=W0142
2186                 debug("[FritzCall] MessageBoxPixmap/setInfoPixmap number: %s/%s" % (self._number, self._name))
2187
2188                 self.setTitle(_("New call"))
2189
2190                 faceFile = findFace(self._number, self._name)
2191                 picPixmap = LoadPixmap(faceFile)
2192                 if not picPixmap:       # that means most probably, that the picture is not 8 bit...
2193                         Notifications.AddNotification(MessageBox, _("Found picture\n\n%s\n\nBut did not load. Probably not PNG, 8-bit") %faceFile, type = MessageBox.TYPE_ERROR)
2194                         picPixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_error.png"))
2195                 picSize = picPixmap.size()
2196                 self["InfoPixmap"].instance.setPixmap(picPixmap)
2197
2198                 # recalculate window size
2199                 textSize = self["text"].getSize()
2200                 textSize = (textSize[0]+20, textSize[1]+20) # don't know, why, but size is too small
2201                 textSize = eSize(*textSize)
2202                 width = max(scaleH(600, 280), picSize.width() + textSize.width() + 30)
2203                 height = max(scaleV(300, 250), picSize.height()+10, textSize.height()+10)
2204                 wSize = (width, height)
2205                 wSize = eSize(*wSize)
2206
2207                 # center the smaller vertically
2208                 hGap = (width-picSize.width()-textSize.width())/3
2209                 picPos = (hGap, (height-picSize.height())/2+1)
2210                 textPos = (hGap+picSize.width()+hGap, (height-textSize.height())/2+1)
2211
2212                 # resize screen
2213                 self.instance.resize(wSize)
2214                 # resize text
2215                 self["text"].instance.resize(textSize)
2216                 # resize pixmap
2217                 self["InfoPixmap"].instance.resize(picSize)
2218                 # move text
2219                 self["text"].instance.move(ePoint(*textPos))
2220                 # move pixmap
2221                 self["InfoPixmap"].instance.move(ePoint(*picPos))
2222                 # center window
2223                 self.instance.move(ePoint((DESKTOP_WIDTH-wSize.width())/2, (DESKTOP_HEIGHT-wSize.height())/2))
2224
2225         def _initTimeout(self):
2226                 if self._timeout > 0:
2227                         self._timer = eTimer()
2228                         self._timer.callback.append(self._timerTick)
2229                         self.onExecBegin.append(self._startTimer)
2230                         self._origTitle = None
2231                         if self.execing:
2232                                 self._timerTick()
2233                         else:
2234                                 self.onShown.append(self.__onShown)
2235                         self._timerRunning = True
2236                 else:
2237                         self._timerRunning = False
2238
2239         def __onShown(self):
2240                 self.onShown.remove(self.__onShown)
2241                 self._timerTick()
2242
2243         def _startTimer(self):
2244                 self._timer.start(1000)
2245
2246 #===============================================================================
2247 #       def stopTimer(self):
2248 #               if self._timerRunning:
2249 #                       del self._timer
2250 #                       self.setTitle(self._origTitle)
2251 #                       self._timerRunning = False
2252 #===============================================================================
2253
2254         def _timerTick(self):
2255                 if self.execing:
2256                         self._timeout -= 1
2257                         if self._origTitle is None:
2258                                 self._origTitle = self.instance.getTitle()
2259                         self.setTitle(self._origTitle + " (" + str(self._timeout) + ")")
2260                         if self._timeout == 0:
2261                                 self._timer.stop()
2262                                 self._timerRunning = False
2263                                 self._exit()
2264
2265         def _exit(self):
2266                 self.close()
2267
2268 def runUserActionScript(event, date, number, caller, phone):
2269         # user exit
2270         # call FritzCallserAction.sh in the same dir as Phonebook.txt with the following parameters:
2271         # event: "RING" (incomning) or "CALL" (outgoing)
2272         # date of event, format: "dd.mm.yy hh.mm.ss"
2273         # telephone number which is calling/is called
2274         # caller's name and address, format Name\n Street\n ZIP City
2275         # line/number which is called/which is used for calling
2276         userActionScript = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "FritzCallUserAction.sh")
2277         if os.path.exists(userActionScript) and os.access(userActionScript, os.X_OK):
2278                 cmd = userActionScript + ' "' + event + '" "' + date + '" "' + number + '" "' + caller + '" "' + phone + '"'
2279                 debug("[FritzCall] runUserActionScript: calling: %s" % cmd)
2280                 eConsoleAppContainer().execute(cmd)
2281
2282 userActionList = [runUserActionScript]
2283 def registerUserAction(fun):
2284         #===========================================================================
2285         # other plugins can register a function, which is then called for each displayed call
2286         # it must take the arguments event,date,number,caller,phone
2287         #
2288         #example:
2289         # def FritzCallEvent(event,date,number,caller,phone):
2290         # ......
2291         # 
2292         # try:
2293         #       from Plugins.Extensions.FritzCall.plugin import registerUserAction as FritzCallRegisterUserAction
2294         #       FritzCallRegisterUserAction(FritzCallEvent)
2295         # except:
2296         #       print "import of FritzCall failed"
2297         #===========================================================================
2298         debug("[FritzCall] registerUserAction: register: %s" % fun.__name__)
2299         userActionList.append(fun)
2300                         
2301 mutedOnConnID = None
2302 def notifyCall(event, date, number, caller, phone, connID):
2303         if Standby.inStandby is None or config.plugins.FritzCall.afterStandby.value == "each":
2304                 if event == "RING":
2305                         global mutedOnConnID
2306                         if config.plugins.FritzCall.muteOnCall.value and not mutedOnConnID:
2307                                 debug("[FritzCall] mute on connID: %s" % connID)
2308                                 mutedOnConnID = connID
2309                                 # eDVBVolumecontrol.getInstance().volumeMute() # with this, we get no mute icon...
2310                                 if not eDVBVolumecontrol.getInstance().isMuted():
2311                                         globalActionMap.actions["volumeMute"]()
2312                         text = _("Incoming Call on %(date)s at %(time)s from\n---------------------------------------------\n%(number)s\n%(caller)s\n---------------------------------------------\nto: %(phone)s") % { 'date':date[:8], 'time':date[9:], 'number':number, 'caller':caller, 'phone':phone }
2313                 else:
2314                         text = _("Outgoing Call on %(date)s at %(time)s to\n---------------------------------------------\n%(number)s\n%(caller)s\n---------------------------------------------\nfrom: %(phone)s") % { 'date':date[:8], 'time':date[9:], 'number':number, 'caller':caller, 'phone':phone }
2315                 debug("[FritzCall] notifyCall:\n%s" % text)
2316                 # Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
2317                 Notifications.AddNotification(MessageBoxPixmap, text, number=number, name=caller, timeout=config.plugins.FritzCall.timeout.value)
2318         elif config.plugins.FritzCall.afterStandby.value == "inList":
2319                 #
2320                 # if not yet done, register function to show call list
2321                 global standbyMode
2322                 if not standbyMode :
2323                         standbyMode = True
2324                         Standby.inStandby.onHide.append(callList.display) #@UndefinedVariable
2325                 # add text/timeout to call list
2326                 callList.add(event, date, number, caller, phone)
2327                 debug("[FritzCall] notifyCall: added to callList")
2328         else: # this is the "None" case
2329                 debug("[FritzCall] notifyCall: standby and no show")
2330
2331         for fun in userActionList:
2332                 debug("[FritzCall] notifyCall: call user action: %s" % fun.__name__)
2333                 fun(event, date, number, caller, phone)
2334
2335
2336 #===============================================================================
2337 #               We need a separate class for each invocation of reverseLookup to retain
2338 #               the necessary data for the notification
2339 #===============================================================================
2340
2341 countries = { }
2342 reverselookupMtime = 0
2343
2344 class FritzReverseLookupAndNotifier:
2345         def __init__(self, event, number, caller, phone, date, connID):
2346                 '''
2347                 
2348                 Initiate a reverse lookup for the given number in the configured country
2349                 
2350                 @param event: CALL or RING
2351                 @param number: number to be looked up
2352                 @param caller: caller including name and address
2353                 @param phone: Number (and name) of or own phone
2354                 @param date: date of call
2355                 '''
2356                 debug("[FritzReverseLookupAndNotifier] reverse Lookup for %s!" % number)
2357                 self.event = event
2358                 self.number = number
2359                 self.caller = caller
2360                 self.phone = phone
2361                 self.date = date
2362                 self.connID = connID
2363
2364                 if number[0] != "0":
2365                         self.notifyAndReset(number, caller)
2366                         return
2367
2368                 ReverseLookupAndNotifier(number, self.notifyAndReset, "UTF-8", config.plugins.FritzCall.country.value)
2369
2370         def notifyAndReset(self, number, caller):
2371                 '''
2372                 
2373                 this gets called with the result of the reverse lookup
2374                 
2375                 @param number: number
2376                 @param caller: name and address of remote. it comes in with name, address and city separated by commas
2377                 '''
2378                 debug("[FritzReverseLookupAndNotifier] got: " + caller)
2379                 self.number = number
2380 #===============================================================================
2381 #               if not caller and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.csv"):
2382 #                       caller = FritzOutlookCSV.findNumber(number, config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.csv") #@UndefinedVariable
2383 #                       debug("[FritzReverseLookupAndNotifier] got from Outlook csv: " + caller)
2384 #===============================================================================
2385 #===============================================================================
2386 #               if not caller and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.ldif"):
2387 #                       caller = FritzLDIF.findNumber(number, open(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.ldif"))
2388 #                       debug("[FritzReverseLookupAndNotifier] got from ldif: " + caller)
2389 #===============================================================================
2390
2391                 name = handleReverseLookupResult(caller)
2392                 if name:
2393                         self.caller = name.replace(", ", "\n").replace('#','')
2394                         # TODO: I don't know, why we store only for incoming calls...
2395                         # if self.number != 0 and config.plugins.FritzCall.addcallers.value and self.event == "RING":
2396                         if self.number != 0 and config.plugins.FritzCall.addcallers.value:
2397                                 debug("[FritzReverseLookupAndNotifier] add to phonebook")
2398                                 phonebook.add(self.number, self.caller)
2399                 else:
2400                         name = resolveNumberWithAvon(self.number, config.plugins.FritzCall.country.value)
2401                         if not name:
2402                                 self.caller = _("UNKNOWN")
2403                         else:
2404                                 self.caller = name
2405                 notifyCall(self.event, self.date, self.number, self.caller, self.phone, self.connID)
2406                 # kill that object...
2407
2408 class FritzProtocol(LineReceiver):
2409         def __init__(self):
2410                 debug("[FritzProtocol] " + "$Revision: 773 $"[1:-1]     + "$Date: 2013-04-07 10:03:55 +0200 (Sun, 07 Apr 2013) $"[7:23] + " starting")
2411                 global mutedOnConnID
2412                 mutedOnConnID = None
2413                 self.number = '0'
2414                 self.caller = None
2415                 self.phone = None
2416                 self.date = '0'
2417                 self.event = None
2418                 self.connID = None
2419
2420         def resetValues(self):