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