Add NumberZapSummary Screen (support)
[openblackhole/openblackhole-enigma2.git] / lib / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector, SilentBouquetSelector
2
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.Harddisk import harddiskmanager
6 from Components.Input import Input
7 from Components.Label import Label
8 from Components.MovieList import AUDIO_EXTENSIONS, MOVIE_EXTENSIONS, DVD_EXTENSIONS
9 from Components.PluginComponent import plugins
10 from Components.ServiceEventTracker import ServiceEventTracker
11 from Components.Sources.Boolean import Boolean
12 from Components.config import config, ConfigBoolean, ConfigClock
13 from Components.SystemInfo import SystemInfo
14 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath, ConfigSelection
15 from Components.Sources.StaticText import StaticText
16 from EpgSelection import EPGSelection
17 from Plugins.Plugin import PluginDescriptor
18
19 from Screen import Screen
20 from Screens import ScreenSaver
21 from Screens import Standby
22 from Screens.ChoiceBox import ChoiceBox
23 from Screens.Dish import Dish
24 from Screens.EventView import EventViewEPGSelect, EventViewSimple, EventViewRecording
25 from Screens.InputBox import InputBox
26 from Screens.MessageBox import MessageBox
27 from Screens.MinuteInput import MinuteInput
28 from Screens.TimerSelection import TimerSelection
29 from Screens.PictureInPicture import PictureInPicture
30 import Screens.Standby
31 from Screens.SubtitleDisplay import SubtitleDisplay
32 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
33 from Screens.TimeDateInput import TimeDateInput
34 from Screens.UnhandledKey import UnhandledKey
35 from ServiceReference import ServiceReference, isPlayableForCur
36
37 from Tools import Notifications, ASCIItranslit
38 from Tools.Directories import fileExists, getRecordingFilename, moveFiles
39
40 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
41         iPlayableService, eServiceReference, eEPGCache, eActionMap
42
43 from time import time, localtime, strftime
44 from os import stat as os_stat
45 from os import rename as os_rename
46 import os
47 from bisect import insort
48 from sys import maxint
49
50 from RecordTimer import RecordTimerEntry, RecordTimer, findSafeRecordPath
51
52 # hack alert!
53 from Menu import MainMenu, mdom
54
55 def isStandardInfoBar(self):
56         return self.__class__.__name__ == "InfoBar"
57
58 def setResumePoint(session):
59         global resumePointCache, resumePointCacheLast
60         service = session.nav.getCurrentService()
61         ref = session.nav.getCurrentlyPlayingServiceOrGroup()
62         if (service is not None) and (ref is not None): # and (ref.type != 1):
63                 # ref type 1 has its own memory...
64                 seek = service.seek()
65                 if seek:
66                         pos = seek.getPlayPosition()
67                         if not pos[0]:
68                                 key = ref.toString()
69                                 lru = int(time())
70                                 l = seek.getLength()
71                                 if l:
72                                         l = l[1]
73                                 else:
74                                         l = None
75                                 resumePointCache[key] = [lru, pos[1], l]
76                                 if len(resumePointCache) > 50:
77                                         candidate = key
78                                         for k,v in resumePointCache.items():
79                                                 if v[0] < lru:
80                                                         candidate = k
81                                         del resumePointCache[candidate]
82                                 if lru - resumePointCacheLast > 3600:
83                                         saveResumePoints()
84
85 def delResumePoint(ref):
86         global resumePointCache, resumePointCacheLast
87         try:
88                 del resumePointCache[ref.toString()]
89         except KeyError:
90                 pass
91         if int(time()) - resumePointCacheLast > 3600:
92                 saveResumePoints()
93
94 def getResumePoint(session):
95         global resumePointCache
96         ref = session.nav.getCurrentlyPlayingServiceOrGroup()
97         if (ref is not None) and (ref.type != 1):
98                 try:
99                         entry = resumePointCache[ref.toString()]
100                         entry[0] = int(time()) # update LRU timestamp
101                         return entry[1]
102                 except KeyError:
103                         return None
104
105 def saveResumePoints():
106         global resumePointCache, resumePointCacheLast
107         import cPickle
108         try:
109                 f = open('/home/root/resumepoints.pkl', 'wb')
110                 cPickle.dump(resumePointCache, f, cPickle.HIGHEST_PROTOCOL)
111         except Exception, ex:
112                 print "[InfoBar] Failed to write resumepoints:", ex
113         resumePointCacheLast = int(time())
114
115 def loadResumePoints():
116         import cPickle
117         try:
118                 return cPickle.load(open('/home/root/resumepoints.pkl', 'rb'))
119         except Exception, ex:
120                 print "[InfoBar] Failed to load resumepoints:", ex
121                 return {}
122
123 resumePointCache = loadResumePoints()
124 resumePointCacheLast = int(time())
125
126 class InfoBarDish:
127         def __init__(self):
128                 self.dishDialog = self.session.instantiateDialog(Dish)
129
130 class InfoBarUnhandledKey:
131         def __init__(self):
132                 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
133                 self.hideUnhandledKeySymbolTimer = eTimer()
134                 self.hideUnhandledKeySymbolTimer.callback.append(self.unhandledKeyDialog.hide)
135                 self.checkUnusedTimer = eTimer()
136                 self.checkUnusedTimer.callback.append(self.checkUnused)
137                 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
138                 eActionMap.getInstance().bindAction('', -maxint -1, self.actionA) #highest prio
139                 eActionMap.getInstance().bindAction('', maxint, self.actionB) #lowest prio
140                 self.flags = (1<<1)
141                 self.uflags = 0
142
143         #this function is called on every keypress!
144         def actionA(self, key, flag):
145                 self.unhandledKeyDialog.hide()
146                 if flag != 4:
147                         if self.flags & (1<<1):
148                                 self.flags = self.uflags = 0
149                         self.flags |= (1<<flag)
150                         if flag == 1: # break
151                                 self.checkUnusedTimer.start(0, True)
152                 return 0
153
154         #this function is only called when no other action has handled this key
155         def actionB(self, key, flag):
156                 if flag != 4:
157                         self.uflags |= (1<<flag)
158
159         def checkUnused(self):
160                 if self.flags == self.uflags:
161                         self.unhandledKeyDialog.show()
162                         self.hideUnhandledKeySymbolTimer.start(2000, True)
163
164 class InfoBarScreenSaver:
165         def __init__(self):
166                 self.onExecBegin.append(self.__onExecBegin)
167                 self.onExecEnd.append(self.__onExecEnd)
168                 self.screenSaverTimer = eTimer()
169                 self.screenSaverTimer.callback.append(self.screensaverTimeout)
170                 self.screensaver = self.session.instantiateDialog(ScreenSaver.Screensaver)
171                 self.onLayoutFinish.append(self.__layoutFinished)
172
173         def __layoutFinished(self):
174                 self.screensaver.hide()
175
176         def __onExecBegin(self):
177                 self.ScreenSaverTimerStart()
178
179         def __onExecEnd(self):
180                 if self.screensaver.shown:
181                         self.screensaver.hide()
182                         eActionMap.getInstance().unbindAction('', self.keypressScreenSaver)
183                 self.screenSaverTimer.stop()
184
185         def ScreenSaverTimerStart(self):
186                 time = int(config.usage.screen_saver.value)
187                 flag = self.seekstate[0]
188                 if not flag:
189                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
190                         if ref:
191                                 ref = ref.toString().split(":")
192                                 flag = ref[2] == "2" or os.path.splitext(ref[10])[1].lower() in AUDIO_EXTENSIONS
193                 if time and flag:
194                         self.screenSaverTimer.startLongTimer(time)
195                 else:
196                         self.screenSaverTimer.stop()
197
198         def screensaverTimeout(self):
199                 if self.execing and not Standby.inStandby and not Standby.inTryQuitMainloop:
200                         self.hide()
201                         if hasattr(self, "pvrStateDialog"):
202                                 self.pvrStateDialog.hide()
203                         self.screensaver.show()
204                         eActionMap.getInstance().bindAction('', -maxint - 1, self.keypressScreenSaver)
205
206         def keypressScreenSaver(self, key, flag):
207                 if flag:
208                         self.screensaver.hide()
209                         self.show()
210                         self.ScreenSaverTimerStart()
211                         eActionMap.getInstance().unbindAction('', self.keypressScreenSaver)
212
213 class SecondInfoBar(Screen):
214
215         def __init__(self, session):
216                 Screen.__init__(self, session)
217                 self.skin = None
218
219 class InfoBarShowHide(InfoBarScreenSaver):
220         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
221         fancy animations. """
222         STATE_HIDDEN = 0
223         STATE_HIDING = 1
224         STATE_SHOWING = 2
225         STATE_SHOWN = 3
226
227         def __init__(self):
228                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
229                         {
230                                 "toggleShow": self.toggleShow,
231                                 "hide": self.keyHide,
232                         }, 1) # lower prio to make it possible to override ok and cancel..
233
234                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
235                         {
236                                 iPlayableService.evStart: self.serviceStarted,
237                         })
238
239                 InfoBarScreenSaver.__init__(self)
240                 self.__state = self.STATE_SHOWN
241                 self.__locked = 0
242
243                 self.hideTimer = eTimer()
244                 self.hideTimer.callback.append(self.doTimerHide)
245                 self.hideTimer.start(5000, True)
246
247                 self.onShow.append(self.__onShow)
248                 self.onHide.append(self.__onHide)
249
250                 self.onShowHideNotifiers = []
251
252                 self.secondInfoBarScreen = ""
253                 if isStandardInfoBar(self):
254                         self.secondInfoBarScreen = self.session.instantiateDialog(SecondInfoBar)
255                         self.secondInfoBarScreen.show()
256
257                 self.onLayoutFinish.append(self.__layoutFinished)
258
259         def __layoutFinished(self):
260                 if self.secondInfoBarScreen:
261                         self.secondInfoBarScreen.hide()
262
263         def __onShow(self):
264                 self.__state = self.STATE_SHOWN
265                 for x in self.onShowHideNotifiers:
266                         x(True)
267                 self.startHideTimer()
268
269         def __onHide(self):
270                 self.__state = self.STATE_HIDDEN
271                 if self.secondInfoBarScreen:
272                         self.secondInfoBarScreen.hide()
273                 for x in self.onShowHideNotifiers:
274                         x(False)
275
276         def keyHide(self):
277                 if self.__state == self.STATE_SHOWN:
278                         self.hide()
279                 elif self.session.pipshown and "popup" in config.usage.pip_hideOnExit.value:
280                         if config.usage.pip_hideOnExit.value == "popup":
281                                 self.session.openWithCallback(self.hidePipOnExitCallback, MessageBox, _("Disable Picture in Picture"), simple=True)
282                         else:
283                                 self.hidePipOnExitCallback(True)
284
285         def hidePipOnExitCallback(self, answer):
286                 if answer == True:
287                         self.showPiP()
288
289         def connectShowHideNotifier(self, fnc):
290                 if not fnc in self.onShowHideNotifiers:
291                         self.onShowHideNotifiers.append(fnc)
292
293         def disconnectShowHideNotifier(self, fnc):
294                 if fnc in self.onShowHideNotifiers:
295                         self.onShowHideNotifiers.remove(fnc)
296
297         def serviceStarted(self):
298                 if self.execing:
299                         if config.usage.show_infobar_on_zap.value:
300                                 self.doShow()
301
302         def startHideTimer(self):
303                 if self.__state == self.STATE_SHOWN and not self.__locked:
304                         self.hideTimer.stop()
305                         if self.secondInfoBarScreen and self.secondInfoBarScreen.shown:
306                                 idx = config.usage.show_second_infobar.index - 1
307                         else:
308                                 idx = config.usage.infobar_timeout.index
309                         if idx:
310                                 self.hideTimer.start(idx*1000, True)
311
312         def doShow(self):
313                 self.show()
314                 self.startHideTimer()
315
316         def doTimerHide(self):
317                 self.hideTimer.stop()
318                 if self.__state == self.STATE_SHOWN:
319                         self.hide()
320
321         def toggleShow(self):
322                 if self.__state == self.STATE_HIDDEN:
323                         self.show()
324                         if self.secondInfoBarScreen:
325                                 self.secondInfoBarScreen.hide()
326                 elif isStandardInfoBar(self) and config.usage.show_second_infobar.value == "EPG":
327                         self.showDefaultEPG()
328                 elif self.secondInfoBarScreen and config.usage.show_second_infobar.value and not self.secondInfoBarScreen.shown:
329                         self.secondInfoBarScreen.show()
330                         self.startHideTimer()
331                 else:
332                         self.hide()
333                         self.hideTimer.stop()
334
335         def lockShow(self):
336                 self.__locked = self.__locked + 1
337                 if self.execing:
338                         self.show()
339                         self.hideTimer.stop()
340
341         def unlockShow(self):
342                 self.__locked = self.__locked - 1
343                 if self.execing:
344                         self.startHideTimer()
345
346 #       def startShow(self):
347 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
348 #               self.__state = self.STATE_SHOWN
349 #
350 #       def startHide(self):
351 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
352 #               self.__state = self.STATE_HIDDEN
353
354 class NumberZapSummary(Screen):
355         def __init__(self, session, parent):
356                 Screen.__init__(self, session, parent = parent)
357                 self["channel"] = StaticText("Test1")
358                 self["number"] = StaticText("Test2")
359                 self["servicename"] = StaticText("Test3")
360                 self.onShow.append(self.__onShow)
361                 self.onHide.append(self.__onHide)
362
363         def __onShow(self):
364                 self.parent.onChanged.append(self.selectionChanged)
365                 self.selectionChanged()
366
367         def __onHide(self):
368                 self.parent.onChanged.remove(self.selectionChanged)
369
370         def selectionChanged(self):
371                 self["number"].text = self.parent["number"].getText()
372                 self["channel"].text = self.parent["channel"].getText()
373                 self["servicename"].text = self.parent["servicename"].getText()
374
375 class NumberZap(Screen):
376         def quit(self):
377                 self.Timer.stop()
378                 self.close()
379
380         def keyOK(self):
381                 self.Timer.stop()
382                 self.close(self.service, self.bouquet)
383
384         def handleServiceName(self):
385                 if self.searchNumber:
386                         self.service, self.bouquet = self.searchNumber(int(self["number"].getText()))
387                         self ["servicename"].text = ServiceReference(self.service).getServiceName()
388                         if not self.startBouquet:
389                                 self.startBouquet = self.bouquet
390
391         def keyBlue(self):
392                 self.Timer.start(3000, True)
393                 if self.searchNumber:
394                         if self.startBouquet == self.bouquet:
395                                 self.service, self.bouquet = self.searchNumber(int(self["number"].getText()), firstBouquetOnly = True)
396                         else:
397                                 self.service, self.bouquet = self.searchNumber(int(self["number"].getText()))
398                         self ["servicename"].text = ServiceReference(self.service).getServiceName()
399                 for x in self.onChanged:
400                         x()
401
402         def keyNumberGlobal(self, number):
403                 self.Timer.start(1000, True)
404                 self.field = self.field + str(number)
405                 self["number"].setText(self.field)
406
407                 self.handleServiceName()
408
409                 if len(self.field) >= 5:
410                         self.keyOK()
411                 for x in self.onChanged:
412                         x()
413
414         def __init__(self, session, number, searchNumberFunction = None):
415                 Screen.__init__(self, session)
416                 self.field = str(number)
417                 self.searchNumber = searchNumberFunction
418                 self.startBouquet = None
419                 self.onChanged = []
420
421                 self["channel"] = Label(_("Channel:"))
422                 self["number"] = Label(self.field)
423                 self["servicename"] = Label()
424
425                 self.handleServiceName()
426
427                 self["actions"] = NumberActionMap( [ "SetupActions", "ShortcutActions" ],
428                         {
429                                 "cancel": self.quit,
430                                 "ok": self.keyOK,
431                                 "blue": self.keyBlue,
432                                 "1": self.keyNumberGlobal,
433                                 "2": self.keyNumberGlobal,
434                                 "3": self.keyNumberGlobal,
435                                 "4": self.keyNumberGlobal,
436                                 "5": self.keyNumberGlobal,
437                                 "6": self.keyNumberGlobal,
438                                 "7": self.keyNumberGlobal,
439                                 "8": self.keyNumberGlobal,
440                                 "9": self.keyNumberGlobal,
441                                 "0": self.keyNumberGlobal
442                         })
443
444                 self.Timer = eTimer()
445                 self.Timer.callback.append(self.keyOK)
446                 self.Timer.start(3000, True)
447
448         def createSummary(self):
449                 return NumberZapSummary
450
451 class InfoBarNumberZap:
452         """ Handles an initial number for NumberZapping """
453         def __init__(self):
454                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
455                         {
456                                 "1": self.keyNumberGlobal,
457                                 "2": self.keyNumberGlobal,
458                                 "3": self.keyNumberGlobal,
459                                 "4": self.keyNumberGlobal,
460                                 "5": self.keyNumberGlobal,
461                                 "6": self.keyNumberGlobal,
462                                 "7": self.keyNumberGlobal,
463                                 "8": self.keyNumberGlobal,
464                                 "9": self.keyNumberGlobal,
465                                 "0": self.keyNumberGlobal,
466                         })
467
468         def keyNumberGlobal(self, number):
469                 if number == 0:
470                         if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
471                                 self.pipDoHandle0Action()
472                         else:
473                                 self.servicelist.recallPrevService()
474                 else:
475                         if self.has_key("TimeshiftActions") and self.timeshiftEnabled():
476                                 ts = self.getTimeshift()
477                                 if ts and ts.isTimeshiftActive():
478                                         return
479                         self.session.openWithCallback(self.numberEntered, NumberZap, number, self.searchNumber)
480
481         def numberEntered(self, service = None, bouquet = None):
482                 if service:
483                         self.selectAndStartService(service, bouquet)
484
485         def searchNumberHelper(self, serviceHandler, num, bouquet):
486                 servicelist = serviceHandler.list(bouquet)
487                 if servicelist:
488                         serviceIterator = servicelist.getNext()
489                         while serviceIterator.valid():
490                                 if num == serviceIterator.getChannelNum():
491                                         return serviceIterator
492                                 serviceIterator = servicelist.getNext()
493                 return None
494
495         def searchNumber(self, number, firstBouquetOnly = False):
496                 bouquet = self.servicelist.getRoot()
497                 service = None
498                 serviceHandler = eServiceCenter.getInstance()
499                 if not firstBouquetOnly:
500                         service = self.searchNumberHelper(serviceHandler, number, bouquet)
501                 if config.usage.multibouquet.value and not service:
502                         bouquet = self.servicelist.bouquet_root
503                         bouquetlist = serviceHandler.list(bouquet)
504                         if bouquetlist:
505                                 bouquet = bouquetlist.getNext()
506                                 while bouquet.valid():
507                                         if bouquet.flags & eServiceReference.isDirectory:
508                                                 service = self.searchNumberHelper(serviceHandler, number, bouquet)
509                                                 if service:
510                                                         playable = not (service.flags & (eServiceReference.isMarker|eServiceReference.isDirectory)) or (service.flags & eServiceReference.isNumberedMarker)
511                                                         if not playable:
512                                                                 service = None
513                                                         break
514                                                 if config.usage.alternative_number_mode.value or firstBouquetOnly:
515                                                         break
516                                         bouquet = bouquetlist.getNext()
517                 return service, bouquet
518
519         def selectAndStartService(self, service, bouquet):
520                 if service:
521                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
522                                 self.servicelist.clearPath()
523                                 if self.servicelist.bouquet_root != bouquet:
524                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
525                                 self.servicelist.enterPath(bouquet)
526                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
527                         self.servicelist.zap(enable_pipzap = True)
528                         self.servicelist.correctChannelNumber()
529                         self.servicelist.startRoot = None
530
531         def zapToNumber(self, number):
532                 service, bouquet = self.searchNumber(number)
533                 self.selectAndStartService(service, bouquet)
534
535 config.misc.initialchannelselection = ConfigBoolean(default = True)
536
537 class InfoBarChannelSelection:
538         """ ChannelSelection - handles the channelSelection dialog and the initial
539         channelChange actions which open the channelSelection dialog """
540         def __init__(self):
541                 #instantiate forever
542                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
543
544                 if config.misc.initialchannelselection.value:
545                         self.onShown.append(self.firstRun)
546
547                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
548                         {
549                                 "switchChannelUp": (self.switchChannelUp, _("Open service list and select previous channel")),
550                                 "switchChannelDown": (self.switchChannelDown, _("Open service list and select next channel")),
551                                 "zapUp": (self.zapUp, _("Switch to previous channel")),
552                                 "zapDown": (self.zapDown, _("Switch next channel")),
553                                 "historyBack": (self.historyBack, _("Switch to previous channel in history")),
554                                 "historyNext": (self.historyNext, _("Switch to next channel in history")),
555                                 "openServiceList": (self.openServiceList, _("Open service list")),
556                         })
557
558         def showTvChannelList(self, zap=False):
559                 self.servicelist.setModeTv()
560                 if zap:
561                         self.servicelist.zap()
562
563         def showRadioChannelList(self, zap=False):
564                 self.servicelist.setModeRadio()
565                 if zap:
566                         self.servicelist.zap()
567
568         def firstRun(self):
569                 self.onShown.remove(self.firstRun)
570                 config.misc.initialchannelselection.value = False
571                 config.misc.initialchannelselection.save()
572                 self.switchChannelDown()
573
574         def historyBack(self):
575                 self.checkTimeshiftRunning(self.historyBackCheckTimeshiftCallback)
576
577         def historyBackCheckTimeshiftCallback(self, answer):
578                 if answer:
579                         self.servicelist.historyBack()
580
581         def historyNext(self):
582                 self.checkTimeshiftRunning(self.historyNextCheckTimeshiftCallback)
583
584         def historyNextCheckTimeshiftCallback(self, answer):
585                 if answer:
586                         self.servicelist.historyNext()
587
588         def switchChannelUp(self):
589                 if "keep" not in config.usage.servicelist_cursor_behavior.value:
590                         self.servicelist.moveUp()
591                 self.session.execDialog(self.servicelist)
592
593         def switchChannelDown(self):
594                 if "keep" not in config.usage.servicelist_cursor_behavior.value:
595                         self.servicelist.moveDown()
596                 self.session.execDialog(self.servicelist)
597
598         def openServiceList(self):
599                 self.session.execDialog(self.servicelist)
600
601         def zapUp(self):
602                 if self.servicelist.inBouquet():
603                         prev = self.servicelist.getCurrentSelection()
604                         if prev:
605                                 prev = prev.toString()
606                                 while True:
607                                         if config.usage.quickzap_bouquet_change.value:
608                                                 if self.servicelist.atBegin():
609                                                         self.servicelist.prevBouquet()
610                                         self.servicelist.moveUp()
611                                         cur = self.servicelist.getCurrentSelection()
612                                         if cur:
613                                                 if self.servicelist.dopipzap:
614                                                         isPlayable = self.session.pip.isPlayableForPipService(cur)
615                                                 else:
616                                                         isPlayable = isPlayableForCur(cur)
617                                         if cur and (cur.toString() == prev or isPlayable):
618                                                         break
619                 else:
620                         self.servicelist.moveUp()
621                 self.servicelist.zap(enable_pipzap = True)
622
623         def zapDown(self):
624                 if self.servicelist.inBouquet():
625                         prev = self.servicelist.getCurrentSelection()
626                         if prev:
627                                 prev = prev.toString()
628                                 while True:
629                                         if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
630                                                 self.servicelist.nextBouquet()
631                                         else:
632                                                 self.servicelist.moveDown()
633                                         cur = self.servicelist.getCurrentSelection()
634                                         if cur:
635                                                 if self.servicelist.dopipzap:
636                                                         isPlayable = self.session.pip.isPlayableForPipService(cur)
637                                                 else:
638                                                         isPlayable = isPlayableForCur(cur)
639                                         if cur and (cur.toString() == prev or isPlayable):
640                                                         break
641                 else:
642                         self.servicelist.moveDown()
643                 self.servicelist.zap(enable_pipzap = True)
644
645 class InfoBarMenu:
646         """ Handles a menu action, to open the (main) menu """
647         def __init__(self):
648                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
649                         {
650                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
651                         })
652                 self.session.infobar = None
653
654         def mainMenu(self):
655                 print "loading mainmenu XML..."
656                 menu = mdom.getroot()
657                 assert menu.tag == "menu", "root element in menu must be 'menu'!"
658
659                 self.session.infobar = self
660                 # so we can access the currently active infobar from screens opened from within the mainmenu
661                 # at the moment used from the SubserviceSelection
662
663                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
664
665         def mainMenuClosed(self, *val):
666                 self.session.infobar = None
667
668 class InfoBarSimpleEventView:
669         """ Opens the Eventview for now/next """
670         def __init__(self):
671                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
672                         {
673                                 "showEventInfo": (self.openEventView, _("Show event details")),
674                                 "showEventInfoSingleEPG": (self.openEventView, _("Show event details")),
675                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
676                         })
677
678         def showEventInfoWhenNotVisible(self):
679                 if self.shown:
680                         self.openEventView()
681                 else:
682                         self.toggleShow()
683                         return 1
684
685         def openEventView(self):
686                 epglist = [ ]
687                 self.epglist = epglist
688                 service = self.session.nav.getCurrentService()
689                 ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
690                 info = service.info()
691                 ptr=info.getEvent(0)
692                 if ptr:
693                         epglist.append(ptr)
694                 ptr=info.getEvent(1)
695                 if ptr:
696                         epglist.append(ptr)
697                 if epglist:
698                         self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
699
700         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
701                 epglist = self.epglist
702                 if len(epglist) > 1:
703                         tmp = epglist[0]
704                         epglist[0] = epglist[1]
705                         epglist[1] = tmp
706                         setEvent(epglist[0])
707
708 class SimpleServicelist:
709         def __init__(self, services):
710                 self.services = services
711                 self.length = len(services)
712                 self.current = 0
713
714         def selectService(self, service):
715                 if not self.length:
716                         self.current = -1
717                         return False
718                 else:
719                         self.current = 0
720                         while self.services[self.current].ref != service:
721                                 self.current += 1
722                                 if self.current >= self.length:
723                                         return False
724                 return True
725
726         def nextService(self):
727                 if not self.length:
728                         return
729                 if self.current+1 < self.length:
730                         self.current += 1
731                 else:
732                         self.current = 0
733
734         def prevService(self):
735                 if not self.length:
736                         return
737                 if self.current-1 > -1:
738                         self.current -= 1
739                 else:
740                         self.current = self.length - 1
741
742         def currentService(self):
743                 if not self.length or self.current >= self.length:
744                         return None
745                 return self.services[self.current]
746
747 class InfoBarEPG:
748         """ EPG - Opens an EPG list when the showEPGList action fires """
749         def __init__(self):
750                 self.is_now_next = False
751                 self.dlg_stack = [ ]
752                 self.bouquetSel = None
753                 self.eventView = None
754                 self.epglist = []
755                 self.defaultEPGType = self.getDefaultEPGtype()
756                 self.defaultGuideType = self.getDefaultGuidetype()
757                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
758                         {
759                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
760                         })
761
762                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
763                         {
764                                 "showEventInfo": (self.showDefaultEPG, _("Show EPG...")),
765                                 "showEventInfoSingleEPG": (self.showSingleEPG, _("Show single service EPG")),
766                                 "showEventInfoMultiEPG": (self.showMultiEPG, _("Show multi channel EPG")),
767                                 "showEventInfoPlugin": (self.showEventInfoPlugins, _("List EPG functions...")),
768                                 "showEventGuidePlugin": (self.showEventGuidePlugins, _("List EPG functions...")),
769                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
770                         })
771
772         def getEPGPluginList(self, getAll=False):
773                 pluginlist = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO) \
774                                 if 'selectedevent' not in p.__call__.func_code.co_varnames]
775                 if pluginlist:
776                         from Components.ServiceEventTracker import InfoBarCount
777                         if getAll or InfoBarCount == 1:
778                                 pluginlist.append((_("Show EPG for current channel..."), self.openSingleServiceEPG))
779                         pluginlist.append((_("Multi EPG"), self.openMultiServiceEPG))
780                         pluginlist.append((_("Current event EPG"), self.openEventView))
781                 return pluginlist
782
783         def getDefaultEPGtype(self):
784                 pluginlist = self.getEPGPluginList()
785                 config.usage.defaultEPGType=ConfigSelection(default = "None", choices = pluginlist)
786                 for plugin in pluginlist:
787                         if plugin[0] == config.usage.defaultEPGType.value:
788                                 return plugin[1]
789                 return None
790
791         def getDefaultGuidetype(self):
792                 pluginlist = self.getEPGPluginList()
793                 config.usage.defaultGuideType=ConfigSelection(default = "None", choices = pluginlist)
794                 for plugin in pluginlist:
795                         if plugin[0] == config.usage.defaultGuideType.value:
796                                 return plugin[1]
797                 return None
798
799         def showEventInfoWhenNotVisible(self):
800                 if self.shown:
801                         self.openEventView()
802                 else:
803                         self.toggleShow()
804                         return 1
805
806         def zapToService(self, service, preview = False, zapback = False):
807                 if self.servicelist.startServiceRef is None:
808                         self.servicelist.startServiceRef = self.session.nav.getCurrentlyPlayingServiceOrGroup()
809                 if service is not None:
810                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
811                                 self.servicelist.clearPath()
812                                 if self.servicelist.bouquet_root != self.epg_bouquet:
813                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
814                                 self.servicelist.enterPath(self.epg_bouquet)
815                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
816                 if not zapback or preview:
817                         self.servicelist.zap(enable_pipzap = True)
818                 if (self.servicelist.dopipzap or zapback) and not preview:
819                         self.servicelist.zapBack()
820                 if not preview:
821                         self.servicelist.startServiceRef = None
822                         self.servicelist.startRoot = None
823
824         def getBouquetServices(self, bouquet):
825                 services = [ ]
826                 servicelist = eServiceCenter.getInstance().list(bouquet)
827                 if not servicelist is None:
828                         while True:
829                                 service = servicelist.getNext()
830                                 if not service.valid(): #check if end of list
831                                         break
832                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
833                                         continue
834                                 services.append(ServiceReference(service))
835                 return services
836
837         def openBouquetEPG(self, bouquet, withCallback=True):
838                 services = self.getBouquetServices(bouquet)
839                 if services:
840                         self.epg_bouquet = bouquet
841                         if withCallback:
842                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
843                         else:
844                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
845
846         def changeBouquetCB(self, direction, epg):
847                 if self.bouquetSel:
848                         if direction > 0:
849                                 self.bouquetSel.down()
850                         else:
851                                 self.bouquetSel.up()
852                         bouquet = self.bouquetSel.getCurrent()
853                         services = self.getBouquetServices(bouquet)
854                         if services:
855                                 self.epg_bouquet = bouquet
856                                 epg.setServices(services)
857
858         def closed(self, ret=False):
859                 closedScreen = self.dlg_stack.pop()
860                 if self.bouquetSel and closedScreen == self.bouquetSel:
861                         self.bouquetSel = None
862                 elif self.eventView and closedScreen == self.eventView:
863                         self.eventView = None
864                 if ret:
865                         dlgs=len(self.dlg_stack)
866                         if dlgs > 0:
867                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
868
869         def openMultiServiceEPG(self, withCallback=True):
870                 bouquets = self.servicelist.getBouquetList()
871                 if bouquets is None:
872                         cnt = 0
873                 else:
874                         cnt = len(bouquets)
875                 if config.usage.multiepg_ask_bouquet.value:
876                         self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
877                 else:
878                         self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
879
880         def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
881                 if cnt > 1: # show bouquet list
882                         if withCallback:
883                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
884                                 self.dlg_stack.append(self.bouquetSel)
885                         else:
886                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
887                 elif cnt == 1:
888                         self.openBouquetEPG(bouquets[0][1], withCallback)
889
890         def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
891                 root = self.servicelist.getRoot()
892                 rootstr = root.toCompareString()
893                 current = 0
894                 for bouquet in bouquets:
895                         if bouquet[1].toCompareString() == rootstr:
896                                 break
897                         current += 1
898                 if current >= cnt:
899                         current = 0
900                 if cnt > 1: # create bouquet list for bouq+/-
901                         self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
902                 if cnt >= 1:
903                         self.openBouquetEPG(root, withCallback)
904
905         def changeServiceCB(self, direction, epg):
906                 if self.serviceSel:
907                         if direction > 0:
908                                 self.serviceSel.nextService()
909                         else:
910                                 self.serviceSel.prevService()
911                         epg.setService(self.serviceSel.currentService())
912
913         def SingleServiceEPGClosed(self, ret=False):
914                 self.serviceSel = None
915
916         def openSingleServiceEPG(self):
917                 from Components.ServiceEventTracker import InfoBarCount
918                 if InfoBarCount > 1:
919                         self.openMultiServiceEPG(False)
920                 else:
921                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
922                         if ref:
923                                 if self.servicelist.getMutableList() is not None: # bouquet in channellist
924                                         current_path = self.servicelist.getRoot()
925                                         services = self.getBouquetServices(current_path)
926                                         self.serviceSel = SimpleServicelist(services)
927                                         if self.serviceSel.selectService(ref):
928                                                 self.epg_bouquet = current_path
929                                                 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, self.zapToService, serviceChangeCB = self.changeServiceCB)
930                                         else:
931                                                 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
932                                 else:
933                                         self.session.open(EPGSelection, ref)
934
935         def runPlugin(self, plugin):
936                 plugin(session = self.session, servicelist = self.servicelist)
937
938         def showEventInfoPlugins(self):
939                 pluginlist = self.getEPGPluginList()
940                 if pluginlist:
941                         pluginlist.append((_("Select default EPG type..."), self.SelectDefaultInfoPlugin))
942                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = pluginlist, skin_name = "EPGExtensionsList")
943                 else:
944                         self.openSingleServiceEPG()
945
946         def EventInfoPluginChosen(self, answer):
947                 if answer is not None:
948                         answer[1]()
949
950         def SelectDefaultInfoPlugin(self):
951                 self.session.openWithCallback(self.DefaultInfoPluginChosen, ChoiceBox, title=_("Please select a default EPG type..."), list = self.getEPGPluginList(True), skin_name = "EPGExtensionsList")
952
953         def DefaultInfoPluginChosen(self, answer):
954                 if answer is not None:
955                         self.defaultEPGType = answer[1]
956                         config.usage.defaultEPGType.value = answer[0]
957                         config.usage.defaultEPGType.save()
958
959         def showEventGuidePlugins(self):
960                 pluginlist = self.getEPGPluginList()
961                 if pluginlist:
962                         pluginlist.append((_("Select default EPG type..."), self.SelectDefaultGuidePlugin))
963                         self.session.openWithCallback(self.EventGuidePluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = pluginlist, skin_name = "EPGExtensionsList")
964                 else:
965                         self.openSingleServiceEPG()
966
967         def EventGuidePluginChosen(self, answer):
968                 if answer is not None:
969                         answer[1]()
970
971         def SelectDefaultGuidePlugin(self):
972                 self.session.openWithCallback(self.DefaultGuidePluginChosen, ChoiceBox, title=_("Please select a default EPG type..."), list = self.getEPGPluginList(True), skin_name = "EPGExtensionsList")
973
974         def DefaultGuidePluginChosen(self, answer):
975                 if answer is not None:
976                         self.defaultGuideType = answer[1]
977                         config.usage.defaultGuideType.value = answer[0]
978                         config.usage.defaultGuideType.save()
979
980         def openSimilarList(self, eventid, refstr):
981                 self.session.open(EPGSelection, refstr, None, eventid)
982
983         def getNowNext(self):
984                 epglist = [ ]
985                 service = self.session.nav.getCurrentService()
986                 info = service and service.info()
987                 ptr = info and info.getEvent(0)
988                 if ptr:
989                         epglist.append(ptr)
990                 ptr = info and info.getEvent(1)
991                 if ptr:
992                         epglist.append(ptr)
993                 self.epglist = epglist
994
995         def __evEventInfoChanged(self):
996                 if self.is_now_next and len(self.dlg_stack) == 1:
997                         self.getNowNext()
998                         if self.eventView and self.epglist:
999                                 self.eventView.setEvent(self.epglist[0])
1000
1001         def showDefaultEPG(self):
1002                 if self.defaultEPGType is not None:
1003                         self.defaultEPGType()
1004                         return
1005                 self.openEventView()
1006
1007         def showSingleEPG(self):
1008                 if self.defaultGuideType is not None:
1009                         self.defaultGuideType()
1010                         return
1011                 pluginlist = self.getEPGPluginList()
1012                 self.openSingleServiceEPG()
1013
1014         def showMultiEPG(self):
1015                 if self.defaultGuideType is not None:
1016                         self.defaultGuideType()
1017                         return
1018                 pluginlist = self.getEPGPluginList()
1019                 self.openMultiServiceEPG()              
1020
1021         def openEventView(self):
1022                 from Components.ServiceEventTracker import InfoBarCount
1023                 if InfoBarCount > 1:
1024                         epglist = [ ]
1025                         self.epglist = epglist
1026                         service = self.session.nav.getCurrentService()
1027                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
1028                         info = service.info()
1029                         ptr=info.getEvent(0)
1030                         if ptr:
1031                                 epglist.append(ptr)
1032                         ptr=info.getEvent(1)
1033                         if ptr:
1034                                 epglist.append(ptr)
1035                         if epglist:
1036                                 self.session.open(EventViewRecording, epglist[0], ServiceReference(ref), self.eventViewCallback, self.openMultiServiceEPG, self.openSimilarList)
1037                 else:
1038                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
1039                         self.getNowNext()
1040                         epglist = self.epglist
1041                         if not epglist:
1042                                 self.is_now_next = False
1043                                 epg = eEPGCache.getInstance()
1044                                 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
1045                                 if ptr:
1046                                         epglist.append(ptr)
1047                                         ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
1048                                         if ptr:
1049                                                 epglist.append(ptr)
1050                         else:
1051                                 self.is_now_next = True
1052                         if epglist:
1053                                 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
1054                                 self.dlg_stack.append(self.eventView)
1055                 if not epglist:
1056                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
1057                         self.openMultiServiceEPG(False)
1058
1059         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
1060                 epglist = self.epglist
1061                 if len(epglist) > 1:
1062                         tmp = epglist[0]
1063                         epglist[0]=epglist[1]
1064                         epglist[1]=tmp
1065                         setEvent(epglist[0])
1066
1067 class InfoBarRdsDecoder:
1068         """provides RDS and Rass support/display"""
1069         def __init__(self):
1070                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
1071                 self.session.instantiateSummaryDialog(self.rds_display)
1072                 self.rass_interactive = None
1073
1074                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1075                         {
1076                                 iPlayableService.evEnd: self.__serviceStopped,
1077                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
1078                         })
1079
1080                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
1081                 {
1082                         "startRassInteractive": self.startRassInteractive
1083                 },-1)
1084
1085                 self["RdsActions"].setEnabled(False)
1086
1087                 self.onLayoutFinish.append(self.rds_display.show)
1088                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
1089
1090         def RassInteractivePossibilityChanged(self, state):
1091                 self["RdsActions"].setEnabled(state)
1092
1093         def RassSlidePicChanged(self):
1094                 if not self.rass_interactive:
1095                         service = self.session.nav.getCurrentService()
1096                         decoder = service and service.rdsDecoder()
1097                         if decoder:
1098                                 decoder.showRassSlidePicture()
1099
1100         def __serviceStopped(self):
1101                 if self.rass_interactive is not None:
1102                         rass_interactive = self.rass_interactive
1103                         self.rass_interactive = None
1104                         rass_interactive.close()
1105
1106         def startRassInteractive(self):
1107                 self.rds_display.hide()
1108                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
1109
1110         def RassInteractiveClosed(self, *val):
1111                 if self.rass_interactive is not None:
1112                         self.rass_interactive = None
1113                         self.RassSlidePicChanged()
1114                 self.rds_display.show()
1115
1116 class InfoBarSeek:
1117         """handles actions like seeking, pause"""
1118
1119         SEEK_STATE_PLAY = (0, 0, 0, ">")
1120         SEEK_STATE_PAUSE = (1, 0, 0, "||")
1121         SEEK_STATE_EOF = (1, 0, 0, "END")
1122
1123         def __init__(self, actionmap = "InfobarSeekActions"):
1124                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1125                         {
1126                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1127                                 iPlayableService.evStart: self.__serviceStarted,
1128                                 iPlayableService.evEOF: self.__evEOF,
1129                                 iPlayableService.evSOF: self.__evSOF,
1130                         })
1131                 self.fast_winding_hint_message_showed = False
1132
1133                 class InfoBarSeekActionMap(HelpableActionMap):
1134                         def __init__(self, screen, *args, **kwargs):
1135                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
1136                                 self.screen = screen
1137
1138                         def action(self, contexts, action):
1139                                 print "action:", action
1140                                 if action[:5] == "seek:":
1141                                         time = int(action[5:])
1142                                         self.screen.doSeekRelative(time * 90000)
1143                                         return 1
1144                                 elif action[:8] == "seekdef:":
1145                                         key = int(action[8:])
1146                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
1147                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
1148                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
1149                                         self.screen.doSeekRelative(time * 90000)
1150                                         return 1
1151                                 else:
1152                                         return HelpableActionMap.action(self, contexts, action)
1153
1154                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
1155                         {
1156                                 "playpauseService": self.playpauseService,
1157                                 "pauseService": (self.pauseService, _("Pause playback")),
1158                                 "unPauseService": (self.unPauseService, _("Continue playback")),
1159
1160                                 "seekFwd": (self.seekFwd, _("Seek forward")),
1161                                 "seekFwdManual": (self.seekFwdManual, _("Seek forward (enter time)")),
1162                                 "seekBack": (self.seekBack, _("Seek backward")),
1163                                 "seekBackManual": (self.seekBackManual, _("Seek backward (enter time)")),
1164                                 "jumpPreviousMark": (self.seekPreviousMark, _("Jump to previous marked position")),
1165                                 "jumpNextMark": (self.seekNextMark, _("Jump to next marked position")),
1166                         }, prio=-1)
1167                         # give them a little more priority to win over color buttons
1168
1169                 self["SeekActions"].setEnabled(False)
1170
1171                 self.seekstate = self.SEEK_STATE_PLAY
1172                 self.lastseekstate = self.SEEK_STATE_PLAY
1173
1174                 self.onPlayStateChanged = [ ]
1175
1176                 self.lockedBecauseOfSkipping = False
1177
1178                 self.__seekableStatusChanged()
1179
1180         def makeStateForward(self, n):
1181                 return (0, n, 0, ">> %dx" % n)
1182
1183         def makeStateBackward(self, n):
1184                 return (0, -n, 0, "<< %dx" % n)
1185
1186         def makeStateSlowMotion(self, n):
1187                 return (0, 0, n, "/%d" % n)
1188
1189         def isStateForward(self, state):
1190                 return state[1] > 1
1191
1192         def isStateBackward(self, state):
1193                 return state[1] < 0
1194
1195         def isStateSlowMotion(self, state):
1196                 return state[1] == 0 and state[2] > 1
1197
1198         def getHigher(self, n, lst):
1199                 for x in lst:
1200                         if x > n:
1201                                 return x
1202                 return False
1203
1204         def getLower(self, n, lst):
1205                 lst = lst[:]
1206                 lst.reverse()
1207                 for x in lst:
1208                         if x < n:
1209                                 return x
1210                 return False
1211
1212         def showAfterSeek(self):
1213                 if isinstance(self, InfoBarShowHide):
1214                         self.doShow()
1215
1216         def up(self):
1217                 pass
1218
1219         def down(self):
1220                 pass
1221
1222         def getSeek(self):
1223                 service = self.session.nav.getCurrentService()
1224                 if service is None:
1225                         return None
1226
1227                 seek = service.seek()
1228
1229                 if seek is None or not seek.isCurrentlySeekable():
1230                         return None
1231
1232                 return seek
1233
1234         def isSeekable(self):
1235                 if self.getSeek() is None or (isStandardInfoBar(self) and not self.timeshiftEnabled()):
1236                         return False
1237                 return True
1238
1239         def __seekableStatusChanged(self):
1240 #               print "seekable status changed!"
1241                 if not self.isSeekable():
1242                         self["SeekActions"].setEnabled(False)
1243 #                       print "not seekable, return to play"
1244                         self.setSeekState(self.SEEK_STATE_PLAY)
1245                 else:
1246                         self["SeekActions"].setEnabled(True)
1247 #                       print "seekable"
1248
1249         def __serviceStarted(self):
1250                 self.fast_winding_hint_message_showed = False
1251                 self.setSeekState(self.SEEK_STATE_PLAY)
1252                 self.__seekableStatusChanged()
1253
1254         def setSeekState(self, state):
1255                 service = self.session.nav.getCurrentService()
1256
1257                 if service is None:
1258                         return False
1259
1260                 if not self.isSeekable():
1261                         if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
1262                                 state = self.SEEK_STATE_PLAY
1263
1264                 pauseable = service.pause()
1265
1266                 if pauseable is None:
1267                         print "not pauseable."
1268                         state = self.SEEK_STATE_PLAY
1269
1270                 self.seekstate = state
1271
1272                 if pauseable is not None:
1273                         if self.seekstate[0]:
1274                                 print "resolved to PAUSE"
1275                                 pauseable.pause()
1276                         elif self.seekstate[1]:
1277                                 if not pauseable.setFastForward(self.seekstate[1]):
1278                                         print "resolved to FAST FORWARD"
1279                                 else:
1280                                         self.seekstate = self.SEEK_STATE_PLAY
1281                                         print "FAST FORWARD not possible: resolved to PLAY"
1282                         elif self.seekstate[2]:
1283                                 if not pauseable.setSlowMotion(self.seekstate[2]):
1284                                         print "resolved to SLOW MOTION"
1285                                 else:
1286                                         self.seekstate = self.SEEK_STATE_PAUSE
1287                                         print "SLOW MOTION not possible: resolved to PAUSE"
1288                         else:
1289                                 print "resolved to PLAY"
1290                                 pauseable.unpause()
1291
1292                 for c in self.onPlayStateChanged:
1293                         c(self.seekstate)
1294
1295                 self.checkSkipShowHideLock()
1296
1297                 if hasattr(self, "ScreenSaverTimerStart"):
1298                         self.ScreenSaverTimerStart()
1299
1300                 return True
1301
1302         def playpauseService(self):
1303                 if self.seekstate != self.SEEK_STATE_PLAY:
1304                         self.unPauseService()
1305                 else:
1306                         self.pauseService()
1307
1308         def pauseService(self):
1309                 if self.seekstate == self.SEEK_STATE_PAUSE:
1310                         if config.seek.on_pause.value == "play":
1311                                 self.unPauseService()
1312                         elif config.seek.on_pause.value == "step":
1313                                 self.doSeekRelative(1)
1314                         elif config.seek.on_pause.value == "last":
1315                                 self.setSeekState(self.lastseekstate)
1316                                 self.lastseekstate = self.SEEK_STATE_PLAY
1317                 else:
1318                         if self.seekstate != self.SEEK_STATE_EOF:
1319                                 self.lastseekstate = self.seekstate
1320                         self.setSeekState(self.SEEK_STATE_PAUSE)
1321
1322         def unPauseService(self):
1323                 print "unpause"
1324                 if self.seekstate == self.SEEK_STATE_PLAY:
1325                         return 0
1326                 self.setSeekState(self.SEEK_STATE_PLAY)
1327
1328         def doSeek(self, pts):
1329                 seekable = self.getSeek()
1330                 if seekable is None:
1331                         return
1332                 seekable.seekTo(pts)
1333
1334         def doSeekRelative(self, pts):
1335                 seekable = self.getSeek()
1336                 if seekable is None:
1337                         return
1338                 prevstate = self.seekstate
1339
1340                 if self.seekstate == self.SEEK_STATE_EOF:
1341                         if prevstate == self.SEEK_STATE_PAUSE:
1342                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1343                         else:
1344                                 self.setSeekState(self.SEEK_STATE_PLAY)
1345                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
1346                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
1347                         self.showAfterSeek()
1348
1349         def seekFwd(self):
1350                 seek = self.getSeek()
1351                 if seek and not (seek.isCurrentlySeekable() & 2):
1352                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1353                                 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
1354                                 self.fast_winding_hint_message_showed = True
1355                                 return
1356                         return 0 # trade as unhandled action
1357                 if self.seekstate == self.SEEK_STATE_PLAY:
1358                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1359                 elif self.seekstate == self.SEEK_STATE_PAUSE:
1360                         if len(config.seek.speeds_slowmotion.value):
1361                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
1362                         else:
1363                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1364                 elif self.seekstate == self.SEEK_STATE_EOF:
1365                         pass
1366                 elif self.isStateForward(self.seekstate):
1367                         speed = self.seekstate[1]
1368                         if self.seekstate[2]:
1369                                 speed /= self.seekstate[2]
1370                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
1371                         self.setSeekState(self.makeStateForward(speed))
1372                 elif self.isStateBackward(self.seekstate):
1373                         speed = -self.seekstate[1]
1374                         if self.seekstate[2]:
1375                                 speed /= self.seekstate[2]
1376                         speed = self.getLower(speed, config.seek.speeds_backward.value)
1377                         if speed:
1378                                 self.setSeekState(self.makeStateBackward(speed))
1379                         else:
1380                                 self.setSeekState(self.SEEK_STATE_PLAY)
1381                 elif self.isStateSlowMotion(self.seekstate):
1382                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
1383                         self.setSeekState(self.makeStateSlowMotion(speed))
1384
1385         def seekBack(self):
1386                 seek = self.getSeek()
1387                 if seek and not (seek.isCurrentlySeekable() & 2):
1388                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1389                                 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
1390                                 self.fast_winding_hint_message_showed = True
1391                                 return
1392                         return 0 # trade as unhandled action
1393                 seekstate = self.seekstate
1394                 if seekstate == self.SEEK_STATE_PLAY:
1395                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1396                 elif seekstate == self.SEEK_STATE_EOF:
1397                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1398                         self.doSeekRelative(-6)
1399                 elif seekstate == self.SEEK_STATE_PAUSE:
1400                         self.doSeekRelative(-1)
1401                 elif self.isStateForward(seekstate):
1402                         speed = seekstate[1]
1403                         if seekstate[2]:
1404                                 speed /= seekstate[2]
1405                         speed = self.getLower(speed, config.seek.speeds_forward.value)
1406                         if speed:
1407                                 self.setSeekState(self.makeStateForward(speed))
1408                         else:
1409                                 self.setSeekState(self.SEEK_STATE_PLAY)
1410                 elif self.isStateBackward(seekstate):
1411                         speed = -seekstate[1]
1412                         if seekstate[2]:
1413                                 speed /= seekstate[2]
1414                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1415                         self.setSeekState(self.makeStateBackward(speed))
1416                 elif self.isStateSlowMotion(seekstate):
1417                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1418                         if speed:
1419                                 self.setSeekState(self.makeStateSlowMotion(speed))
1420                         else:
1421                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1422
1423         def seekFwdManual(self):
1424                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1425
1426         def fwdSeekTo(self, minutes):
1427                 print "Seek", minutes, "minutes forward"
1428                 self.doSeekRelative(minutes * 60 * 90000)
1429
1430         def seekBackManual(self):
1431                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1432
1433         def rwdSeekTo(self, minutes):
1434                 print "rwdSeekTo"
1435                 self.doSeekRelative(-minutes * 60 * 90000)
1436
1437         def checkSkipShowHideLock(self):
1438                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1439
1440                 if config.usage.show_infobar_on_skip.value:
1441                         if self.lockedBecauseOfSkipping and not wantlock:
1442                                 self.unlockShow()
1443                                 self.lockedBecauseOfSkipping = False
1444
1445                         if wantlock and not self.lockedBecauseOfSkipping:
1446                                 self.lockShow()
1447                                 self.lockedBecauseOfSkipping = True
1448
1449         def calcRemainingTime(self):
1450                 seekable = self.getSeek()
1451                 if seekable is not None:
1452                         len = seekable.getLength()
1453                         try:
1454                                 tmp = self.cueGetEndCutPosition()
1455                                 if tmp:
1456                                         len = (False, tmp)
1457                         except:
1458                                 pass
1459                         pos = seekable.getPlayPosition()
1460                         speednom = self.seekstate[1] or 1
1461                         speedden = self.seekstate[2] or 1
1462                         if not len[0] and not pos[0]:
1463                                 if len[1] <= pos[1]:
1464                                         return 0
1465                                 time = (len[1] - pos[1])*speedden/(90*speednom)
1466                                 return time
1467                 return False
1468
1469         def __evEOF(self):
1470                 if self.seekstate == self.SEEK_STATE_EOF:
1471                         return
1472
1473                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1474                 seekstate = self.seekstate
1475                 if self.seekstate != self.SEEK_STATE_PAUSE:
1476                         self.setSeekState(self.SEEK_STATE_EOF)
1477
1478                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1479                         seekable = self.getSeek()
1480                         if seekable is not None:
1481                                 seekable.seekTo(-1)
1482                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1483                         self.doEofInternal(True)
1484                 else:
1485                         self.doEofInternal(False)
1486
1487         def doEofInternal(self, playing):
1488                 pass            # Defined in subclasses
1489
1490         def __evSOF(self):
1491                 self.setSeekState(self.SEEK_STATE_PLAY)
1492                 self.doSeek(0)
1493
1494         # This is needed, because some Mediaplayer use InfoBarSeek but not InfoBarCueSheetSupport
1495         def seekPreviousMark(self):
1496                 if isinstance(self, InfoBarCueSheetSupport):
1497                         self.jumpPreviousMark()
1498
1499         def seekNextMark(self):
1500                 if isinstance(self, InfoBarCueSheetSupport):
1501                         self.jumpNextMark()
1502
1503 from Screens.PVRState import PVRState, TimeshiftState
1504
1505 class InfoBarPVRState:
1506         def __init__(self, screen=PVRState, force_show = False):
1507                 self.onPlayStateChanged.append(self.__playStateChanged)
1508                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1509                 self.onShow.append(self._mayShow)
1510                 self.onHide.append(self.pvrStateDialog.hide)
1511                 self.force_show = force_show
1512
1513         def _mayShow(self):
1514                 if self.shown and self.seekstate != self.SEEK_STATE_PLAY:
1515                         self.pvrStateDialog.show()
1516
1517         def __playStateChanged(self, state):
1518                 playstateString = state[3]
1519                 self.pvrStateDialog["state"].setText(playstateString)
1520
1521                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1522                 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1523                         self.pvrStateDialog.hide()
1524                 else:
1525                         self._mayShow()
1526
1527 class InfoBarTimeshiftState(InfoBarPVRState):
1528         def __init__(self):
1529                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1530                 self.__hideTimer = eTimer()
1531                 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1532
1533         def _mayShow(self):
1534                 if self.shown and self.timeshiftEnabled():
1535                         self.pvrStateDialog.show()
1536                         if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1537                                 self.__hideTimer.start(5*1000, True)
1538
1539         def __hideTimeshiftState(self):
1540                 self.pvrStateDialog.hide()
1541
1542 class InfoBarShowMovies:
1543
1544         # i don't really like this class.
1545         # it calls a not further specified "movie list" on up/down/movieList,
1546         # so this is not more than an action map
1547         def __init__(self):
1548                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1549                         {
1550                                 "movieList": (self.showMovies, _("Open the movie list")),
1551                                 "up": (self.up, _("Open the movie list")),
1552                                 "down": (self.down, _("Open the movie list"))
1553                         })
1554
1555 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1556
1557 # Hrmf.
1558 #
1559 # Timeshift works the following way:
1560 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1561 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1562 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1563 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1564 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1565 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1566 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1567 #
1568
1569 # in other words:
1570 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1571 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1572 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1573 # - the user can now PVR around
1574 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1575 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1576 # after!
1577 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1578 # - if the user rewinds, or press pause, timeshift will be activated again
1579
1580 # note that a timeshift can be enabled ("recording") and
1581 # activated (currently time-shifting).
1582
1583 class InfoBarTimeshift:
1584         def __init__(self):
1585                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1586                         {
1587                                 "timeshiftStart": (self.startTimeshift, _("Start timeshift")),  # the "yellow key"
1588                                 "timeshiftStop": (self.stopTimeshift, _("Stop timeshift"))      # currently undefined :), probably 'TV'
1589                         }, prio=1)
1590                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1591                         {
1592                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1593                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1594                         }, prio=-1) # priority over record
1595
1596                 self["TimeshiftActivateActions"].setEnabled(False)
1597                 self.ts_rewind_timer = eTimer()
1598                 self.ts_rewind_timer.callback.append(self.rewindService)
1599                 self.ts_start_delay_timer = eTimer()
1600                 self.ts_start_delay_timer.callback.append(self.startTimeshiftWithoutPause)
1601                 self.save_timeshift_file = False
1602                 self.timeshift_was_activated = False
1603
1604                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1605                         {
1606                                 iPlayableService.evStart: self.__serviceStarted,
1607                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1608                                 iPlayableService.evEnd: self.__serviceEnd
1609                         })
1610
1611         def getTimeshift(self):
1612                 service = self.session.nav.getCurrentService()
1613                 return service and service.timeshift()
1614
1615         def timeshiftEnabled(self):
1616                 ts = self.getTimeshift()
1617                 return ts and ts.isTimeshiftEnabled()
1618
1619         def startTimeshift(self, pauseService = True):
1620                 print "enable timeshift"
1621                 ts = self.getTimeshift()
1622                 if ts is None:
1623                         if not pauseService and not int(config.usage.timeshift_start_delay.value):
1624                                 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR, simple = True)
1625                         print "no ts interface"
1626                         return 0
1627
1628                 if ts.isTimeshiftEnabled():
1629                         print "hu, timeshift already enabled?"
1630                 else:
1631                         if not ts.startTimeshift():
1632                                 # we remove the "relative time" for now.
1633                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1634
1635                                 if pauseService:
1636                                         # PAUSE.
1637                                         #self.setSeekState(self.SEEK_STATE_PAUSE)
1638                                         self.activateTimeshiftEnd(False)
1639
1640                                 # enable the "TimeshiftEnableActions", which will override
1641                                 # the startTimeshift actions
1642                                 self.__seekableStatusChanged()
1643
1644                                 # get current timeshift filename and calculate new
1645                                 self.save_timeshift_file = False
1646                                 self.save_timeshift_in_movie_dir = False
1647                                 self.current_timeshift_filename = ts.getTimeshiftFilename()
1648                                 self.new_timeshift_filename = self.generateNewTimeshiftFileName()
1649                         else:
1650                                 print "timeshift failed"
1651
1652         def startTimeshiftWithoutPause(self):
1653                 self.startTimeshift(False)
1654
1655         def stopTimeshift(self):
1656                 ts = self.getTimeshift()
1657                 if ts and ts.isTimeshiftEnabled():
1658                         if int(config.usage.timeshift_start_delay.value):
1659                                 ts.switchToLive()
1660                         else:
1661                                 self.checkTimeshiftRunning(self.stopTimeshiftcheckTimeshiftRunningCallback)
1662                 else:
1663                         return 0
1664
1665         def stopTimeshiftcheckTimeshiftRunningCallback(self, answer):
1666                 ts = self.getTimeshift()
1667                 if answer and ts:
1668                         ts.stopTimeshift()
1669                         self.pvrStateDialog.hide()
1670
1671                         # disable actions
1672                         self.__seekableStatusChanged()
1673
1674         # activates timeshift, and seeks to (almost) the end
1675         def activateTimeshiftEnd(self, back = True):
1676                 ts = self.getTimeshift()
1677                 print "activateTimeshiftEnd"
1678
1679                 if ts is None:
1680                         return
1681
1682                 if ts.isTimeshiftActive():
1683                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1684                         self.pauseService()
1685                 else:
1686                         print "play, ..."
1687                         ts.activateTimeshift() # activate timeshift will automatically pause
1688                         self.setSeekState(self.SEEK_STATE_PAUSE)
1689                         seekable = self.getSeek()
1690                         if seekable is not None:
1691                                 seekable.seekTo(-90000) # seek approx. 1 sec before end
1692                         self.timeshift_was_activated = True
1693                 if back:
1694                         self.ts_rewind_timer.start(200, 1)
1695
1696         def rewindService(self):
1697                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1698
1699         # generates only filename without path
1700         def generateNewTimeshiftFileName(self):
1701                 name = "timeshift record"
1702                 info = { }
1703                 self.getProgramInfoAndEvent(info, name)
1704
1705                 serviceref = info["serviceref"]
1706
1707                 service_name = ""
1708                 if isinstance(serviceref, eServiceReference):
1709                         service_name = ServiceReference(serviceref).getServiceName()
1710                 begin_date = strftime("%Y%m%d %H%M", localtime(time()))
1711                 filename = begin_date + " - " + service_name
1712
1713                 if config.recording.filename_composition.value == "short":
1714                         filename = strftime("%Y%m%d", localtime(time())) + " - " + info["name"]
1715                 elif config.recording.filename_composition.value == "long":
1716                         filename += " - " + info["name"] + " - " + info["description"]
1717                 else:
1718                         filename += " - " + info["name"] # standard
1719
1720                 if config.recording.ascii_filenames.value:
1721                         filename = ASCIItranslit.legacyEncode(filename)
1722
1723                 print "New timeshift filename: ", filename
1724                 return filename
1725
1726         # same as activateTimeshiftEnd, but pauses afterwards.
1727         def activateTimeshiftEndAndPause(self):
1728                 print "activateTimeshiftEndAndPause"
1729                 #state = self.seekstate
1730                 self.activateTimeshiftEnd(False)
1731
1732         def __seekableStatusChanged(self):
1733                 self["TimeshiftActivateActions"].setEnabled(not self.isSeekable() and self.timeshiftEnabled())
1734                 state = self.getSeek() is not None and self.timeshiftEnabled()
1735                 self["SeekActions"].setEnabled(state)
1736                 if not state:
1737                         self.setSeekState(self.SEEK_STATE_PLAY)
1738                 self.restartSubtitle()
1739
1740         def __serviceStarted(self):
1741                 self.pvrStateDialog.hide()
1742                 self.__seekableStatusChanged()
1743                 if self.ts_start_delay_timer.isActive():
1744                         self.ts_start_delay_timer.stop()
1745                 if int(config.usage.timeshift_start_delay.value):
1746                         self.ts_start_delay_timer.start(int(config.usage.timeshift_start_delay.value) * 1000, True)
1747
1748         def checkTimeshiftRunning(self, returnFunction):
1749                 if self.timeshiftEnabled() and config.usage.check_timeshift.value and self.timeshift_was_activated:
1750                         message = _("Stop timeshift?")
1751                         if not self.save_timeshift_file:
1752                                 choice = [(_("yes"), "stop"), (_("no"), "continue"), (_("Yes and save"), "save"), (_("Yes and save in movie dir"), "save_movie")]
1753                         else:
1754                                 choice = [(_("yes"), "stop"), (_("no"), "continue")]
1755                                 message += "\n" + _("Reminder, you have chosen to save timeshift file.")
1756                         self.session.openWithCallback(boundFunction(self.checkTimeshiftRunningCallback, returnFunction), MessageBox, message, simple = True, list = choice)
1757                 else:
1758                         returnFunction(True)
1759
1760         def checkTimeshiftRunningCallback(self, returnFunction, answer):
1761                 if answer:
1762                         if "movie" in answer:
1763                                 self.save_timeshift_in_movie_dir = True
1764                         if "save" in answer:
1765                                 self.save_timeshift_file = True
1766                                 ts = self.getTimeshift()
1767                                 if ts:
1768                                         ts.saveTimeshiftFile()
1769                                         del ts
1770                         if "continue" not in answer:
1771                                 self.saveTimeshiftFiles()
1772                 returnFunction(answer and answer != "continue")
1773
1774         # renames/moves timeshift files if requested
1775         def __serviceEnd(self):
1776                 self.saveTimeshiftFiles()
1777                 self.timeshift_was_activated = False
1778
1779         def saveTimeshiftFiles(self):
1780                 if self.save_timeshift_file and self.current_timeshift_filename and self.new_timeshift_filename:
1781                         if config.usage.timeshift_path.value and not self.save_timeshift_in_movie_dir:
1782                                 dirname = config.usage.timeshift_path.value
1783                         else:
1784                                 dirname = defaultMoviePath()
1785                         filename = getRecordingFilename(self.new_timeshift_filename, dirname) + ".ts"
1786
1787                         fileList = []
1788                         fileList.append((self.current_timeshift_filename, filename))
1789                         if fileExists(self.current_timeshift_filename + ".sc"):
1790                                 fileList.append((self.current_timeshift_filename + ".sc", filename + ".sc"))
1791                         if fileExists(self.current_timeshift_filename + ".cuts"):
1792                                 fileList.append((self.current_timeshift_filename + ".cuts", filename + ".cuts"))
1793
1794                         moveFiles(fileList)
1795                         self.save_timeshift_file = False
1796
1797 from Screens.PiPSetup import PiPSetup
1798
1799 class InfoBarExtensions:
1800         EXTENSION_SINGLE = 0
1801         EXTENSION_LIST = 1
1802
1803         def __init__(self):
1804                 self.list = []
1805
1806                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1807                         {
1808                                 "extensions": (self.showExtensionSelection, _("Show extensions...")),
1809                         }, 1) # lower priority
1810
1811         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1812                 self.list.append((type, extension, key))
1813
1814         def updateExtension(self, extension, key = None):
1815                 self.extensionsList.append(extension)
1816                 if key is not None:
1817                         if self.extensionKeys.has_key(key):
1818                                 key = None
1819
1820                 if key is None:
1821                         for x in self.availableKeys:
1822                                 if not self.extensionKeys.has_key(x):
1823                                         key = x
1824                                         break
1825
1826                 if key is not None:
1827                         self.extensionKeys[key] = len(self.extensionsList) - 1
1828
1829         def updateExtensions(self):
1830                 self.extensionsList = []
1831                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1832                 self.extensionKeys = {}
1833                 for x in self.list:
1834                         if x[0] == self.EXTENSION_SINGLE:
1835                                 self.updateExtension(x[1], x[2])
1836                         else:
1837                                 for y in x[1]():
1838                                         self.updateExtension(y[0], y[1])
1839
1840         def showExtensionSelection(self):
1841                 self.updateExtensions()
1842                 extensionsList = self.extensionsList[:]
1843                 keys = []
1844                 list = []
1845                 for x in self.availableKeys:
1846                         if self.extensionKeys.has_key(x):
1847                                 entry = self.extensionKeys[x]
1848                                 extension = self.extensionsList[entry]
1849                                 if extension[2]():
1850                                         name = str(extension[0]())
1851                                         list.append((extension[0](), extension))
1852                                         keys.append(x)
1853                                         extensionsList.remove(extension)
1854                                 else:
1855                                         extensionsList.remove(extension)
1856                 list.extend([(x[0](), x) for x in extensionsList])
1857
1858                 keys += [""] * len(extensionsList)
1859                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1860
1861         def extensionCallback(self, answer):
1862                 if answer is not None:
1863                         answer[1][1]()
1864
1865 from Tools.BoundFunction import boundFunction
1866 import inspect
1867
1868 # depends on InfoBarExtensions
1869
1870 class InfoBarPlugins:
1871         def __init__(self):
1872                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1873
1874         def getPluginName(self, name):
1875                 return name
1876
1877         def getPluginList(self):
1878                 l = []
1879                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1880                         args = inspect.getargspec(p.__call__)[0]
1881                         if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1882                                 l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1883                 l.sort(key = lambda e: e[2]) # sort by name
1884                 return l
1885
1886         def runPlugin(self, plugin):
1887                 if isinstance(self, InfoBarChannelSelection):
1888                         plugin(session = self.session, servicelist = self.servicelist)
1889                 else:
1890                         plugin(session = self.session)
1891
1892 from Components.Task import job_manager
1893 class InfoBarJobman:
1894         def __init__(self):
1895                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1896
1897         def getJobList(self):
1898                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1899
1900         def getJobName(self, job):
1901                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1902
1903         def showJobView(self, job):
1904                 from Screens.TaskView import JobView
1905                 job_manager.in_background = False
1906                 self.session.openWithCallback(self.JobViewCB, JobView, job)
1907
1908         def JobViewCB(self, in_background):
1909                 job_manager.in_background = in_background
1910
1911 # depends on InfoBarExtensions
1912 class InfoBarPiP:
1913         def __init__(self):
1914                 try:
1915                         self.session.pipshown
1916                 except:
1917                         self.session.pipshown = False
1918                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1919                         self["PiPActions"] = HelpableActionMap(self, "InfobarPiPActions",
1920                                 {
1921                                         "activatePiP": (self.activePiP, _("Activate PiP")),
1922                                 })
1923                         if (self.allowPiP):
1924                                 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1925                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1926                                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1927                                 self.addExtension((self.getTogglePipzapName, self.togglePipzap, lambda: True), "red")
1928                         else:
1929                                 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1930                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1931
1932         def pipShown(self):
1933                 return self.session.pipshown
1934
1935         def pipHandles0Action(self):
1936                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1937
1938         def getShowHideName(self):
1939                 if self.session.pipshown:
1940                         return _("Disable Picture in Picture")
1941                 else:
1942                         return _("Activate Picture in Picture")
1943
1944         def getSwapName(self):
1945                 return _("Swap services")
1946
1947         def getMoveName(self):
1948                 return _("Move Picture in Picture")
1949
1950         def getTogglePipzapName(self):
1951                 slist = self.servicelist
1952                 if slist and slist.dopipzap:
1953                         return _("Zap focus to main screen")
1954                 return _("Zap focus to Picture in Picture")
1955
1956         def togglePipzap(self):
1957                 if not self.session.pipshown:
1958                         self.showPiP()
1959                 slist = self.servicelist
1960                 if slist and self.session.pipshown:
1961                         slist.togglePipzap()
1962                         if slist.dopipzap:
1963                                 currentServicePath = slist.getCurrentServicePath()
1964                                 self.servicelist.setCurrentServicePath(self.session.pip.servicePath, doZap=False)
1965                                 self.session.pip.servicePath = currentServicePath
1966
1967         def showPiP(self):
1968                 if self.session.pipshown:
1969                         slist = self.servicelist
1970                         if slist and slist.dopipzap:
1971                                 self.togglePipzap()
1972                         if self.session.pipshown:
1973                                 del self.session.pip
1974                                 self.session.pipshown = False
1975                 else:
1976                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1977                         self.session.pip.show()
1978                         newservice = self.session.nav.getCurrentlyPlayingServiceReference() or self.servicelist.servicelist.getCurrent()
1979                         if self.session.pip.playService(newservice):
1980                                 self.session.pipshown = True
1981                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1982                         else:
1983                                 self.session.pipshown = False
1984                                 del self.session.pip
1985
1986         def activePiP(self):
1987                 if self.session.pipshown:
1988                         self.togglePipzap()
1989                 else:
1990                         self.showPiP()
1991
1992         def swapPiP(self):
1993                 swapservice = self.session.nav.getCurrentlyPlayingServiceOrGroup()
1994                 pipref = self.session.pip.getCurrentService()
1995                 if swapservice and pipref and pipref.toString() != swapservice.toString():
1996                         currentServicePath = self.servicelist.getCurrentServicePath()
1997                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath, doZap=False)
1998                         self.session.pip.playService(swapservice)
1999                         self.session.nav.playService(pipref, checkParentalControl=False, adjust=False)
2000                         self.session.pip.servicePath = currentServicePath
2001                         if self.servicelist.dopipzap:
2002                                 # This unfortunately won't work with subservices
2003                                 self.servicelist.setCurrentSelection(self.session.pip.getCurrentService())
2004
2005         def movePiP(self):
2006                 self.session.open(PiPSetup, pip = self.session.pip)
2007
2008         def pipDoHandle0Action(self):
2009                 use = config.usage.pip_zero_button.value
2010                 if "swap" == use:
2011                         self.swapPiP()
2012                 elif "swapstop" == use:
2013                         self.swapPiP()
2014                         self.showPiP()
2015                 elif "stop" == use:
2016                         self.showPiP()
2017
2018 from RecordTimer import parseEvent, RecordTimerEntry
2019
2020 class InfoBarInstantRecord:
2021         """Instant Record - handles the instantRecord action in order to
2022         start/stop instant records"""
2023         def __init__(self):
2024                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
2025                         {
2026                                 "instantRecord": (self.instantRecord, _("Instant recording...")),
2027                         })
2028                 if isStandardInfoBar(self):
2029                         self.recording = []
2030                 else:
2031                         from Screens.InfoBar import InfoBar
2032                         InfoBarInstance = InfoBar.instance
2033                         if InfoBarInstance:
2034                                 self.recording = InfoBarInstance.recording
2035
2036         def stopCurrentRecording(self, entry = -1):
2037                 if entry is not None and entry != -1:
2038                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
2039                         self.recording.remove(self.recording[entry])
2040
2041         def getProgramInfoAndEvent(self, info, name):
2042                 info["serviceref"] = self.session.nav.getCurrentlyPlayingServiceOrGroup()
2043
2044                 # try to get event info
2045                 event = None
2046                 try:
2047                         service = self.session.nav.getCurrentService()
2048                         epg = eEPGCache.getInstance()
2049                         event = epg.lookupEventTime(info["serviceref"], -1, 0)
2050                         if event is None:
2051                                 event = service.info().getEvent(0)
2052                 except:
2053                         pass
2054
2055                 info["event"] = event
2056                 info["name"]  = name
2057                 info["description"] = ""
2058                 info["eventid"] = None
2059
2060                 if event is not None:
2061                         curEvent = parseEvent(event)
2062                         info["name"] = curEvent[2]
2063                         info["description"] = curEvent[3]
2064                         info["eventid"] = curEvent[4]
2065                         info["end"] = curEvent[1]
2066
2067
2068         def startInstantRecording(self, limitEvent = False):
2069                 begin = int(time())
2070                 end = begin + 3600      # dummy
2071                 name = "instant record"
2072                 info = { }
2073
2074                 self.getProgramInfoAndEvent(info, name)
2075                 serviceref = info["serviceref"]
2076                 event = info["event"]
2077
2078                 if event is not None:
2079                         if limitEvent:
2080                                 end = info["end"]
2081                 else:
2082                         if limitEvent:
2083                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
2084
2085                 if isinstance(serviceref, eServiceReference):
2086                         serviceref = ServiceReference(serviceref)
2087
2088                 recording = RecordTimerEntry(serviceref, begin, end, info["name"], info["description"], info["eventid"], dirname = preferredInstantRecordPath())
2089                 recording.dontSave = True
2090
2091                 if event is None or limitEvent == False:
2092                         recording.autoincrease = True
2093                         recording.setAutoincreaseEnd()
2094
2095                 simulTimerList = self.session.nav.RecordTimer.record(recording)
2096
2097                 if simulTimerList is None:      # no conflict
2098                         recording.autoincrease = False
2099                         self.recording.append(recording)
2100                 else:
2101                         if len(simulTimerList) > 1: # with other recording
2102                                 name = simulTimerList[1].name
2103                                 name_date = ' '.join((name, strftime('%F %T', localtime(simulTimerList[1].begin))))
2104                                 print "[TIMER] conflicts with", name_date
2105                                 recording.autoincrease = True   # start with max available length, then increment
2106                                 if recording.setAutoincreaseEnd():
2107                                         self.session.nav.RecordTimer.record(recording)
2108                                         self.recording.append(recording)
2109                                         self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
2110                                 else:
2111                                         self.session.open(MessageBox, _("Could not record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
2112                         else:
2113                                 self.session.open(MessageBox, _("Could not record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
2114                         recording.autoincrease = False
2115
2116         def isInstantRecordRunning(self):
2117                 print "self.recording:", self.recording
2118                 if self.recording:
2119                         for x in self.recording:
2120                                 if x.isRunning():
2121                                         return True
2122                 return False
2123
2124         def recordQuestionCallback(self, answer):
2125                 print "pre:\n", self.recording
2126
2127                 if answer is None or answer[1] == "no":
2128                         return
2129                 list = []
2130                 recording = self.recording[:]
2131                 for x in recording:
2132                         if not x in self.session.nav.RecordTimer.timer_list:
2133                                 self.recording.remove(x)
2134                         elif x.dontSave and x.isRunning():
2135                                 list.append((x, False))
2136
2137                 if answer[1] == "changeduration":
2138                         if len(self.recording) == 1:
2139                                 self.changeDuration(0)
2140                         else:
2141                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
2142                 elif answer[1] == "changeendtime":
2143                         if len(self.recording) == 1:
2144                                 self.setEndtime(0)
2145                         else:
2146                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
2147                 elif answer[1] == "timer":
2148                         import TimerEdit
2149                         self.session.open(TimerEdit.TimerEditList)
2150                 elif answer[1] == "stop":
2151                         self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
2152                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
2153                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
2154                         if answer[1] == "manualduration":
2155                                 self.changeDuration(len(self.recording)-1)
2156                         elif answer[1] == "manualendtime":
2157                                 self.setEndtime(len(self.recording)-1)
2158                 elif "timeshift" in answer[1]:
2159                         ts = self.getTimeshift()
2160                         if ts:
2161                                 ts.saveTimeshiftFile()
2162                                 self.save_timeshift_file = True
2163                                 if "movie" in answer[1]:
2164                                         self.save_timeshift_in_movie_dir = True
2165                 print "after:\n", self.recording
2166
2167         def setEndtime(self, entry):
2168                 if entry is not None and entry >= 0:
2169                         self.selectedEntry = entry
2170                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
2171                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
2172                         dlg.setTitle(_("Please change recording endtime"))
2173
2174         def TimeDateInputClosed(self, ret):
2175                 if len(ret) > 1:
2176                         if ret[0]:
2177                                 print "stopping recording at", strftime("%F %T", localtime(ret[1]))
2178                                 if self.recording[self.selectedEntry].end != ret[1]:
2179                                         self.recording[self.selectedEntry].autoincrease = False
2180                                 self.recording[self.selectedEntry].end = ret[1]
2181                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
2182
2183         def changeDuration(self, entry):
2184                 if entry is not None and entry >= 0:
2185                         self.selectedEntry = entry
2186                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
2187
2188         def inputCallback(self, value):
2189                 if value is not None:
2190                         print "stopping recording after", int(value), "minutes."
2191                         entry = self.recording[self.selectedEntry]
2192                         if int(value) != 0:
2193                                 entry.autoincrease = False
2194                         entry.end = int(time()) + 60 * int(value)
2195                         self.session.nav.RecordTimer.timeChanged(entry)
2196
2197         def isTimerRecordRunning(self):
2198                 identical = timers = 0
2199                 for timer in self.session.nav.RecordTimer.timer_list:
2200                         if timer.isRunning() and not timer.justplay:
2201                                 timers += 1
2202                                 if self.recording:
2203                                         for x in self.recording:
2204                                                 if x.isRunning() and x == timer:
2205                                                         identical += 1
2206                 return timers > identical
2207
2208         def instantRecord(self):
2209                 pirr = preferredInstantRecordPath()
2210                 if not findSafeRecordPath(pirr) and not findSafeRecordPath(defaultMoviePath()):
2211                         if not pirr:
2212                                 pirr = ""
2213                         self.session.open(MessageBox, _("Missing ") + "\n" + pirr +
2214                                                  "\n" + _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
2215                         return
2216
2217                 if isStandardInfoBar(self):
2218                         common = ((_("Add recording (stop after current event)"), "event"),
2219                                 (_("Add recording (indefinitely)"), "indefinitely"),
2220                                 (_("Add recording (enter recording duration)"), "manualduration"),
2221                                 (_("Add recording (enter recording endtime)"), "manualendtime"),)
2222                 else:
2223                         common = ()
2224                 if self.isInstantRecordRunning():
2225                         title =_("A recording is currently running.\nWhat do you want to do?")
2226                         list = ((_("Stop recording"), "stop"),) + common + \
2227                                 ((_("Change recording (duration)"), "changeduration"),
2228                                 (_("Change recording (endtime)"), "changeendtime"),)
2229                         if self.isTimerRecordRunning():
2230                                 list += ((_("Stop timer recording"), "timer"),)
2231                         list += ((_("Do nothing"), "no"),)
2232                 else:
2233                         title=_("Start recording?")
2234                         list = common
2235                         if self.isTimerRecordRunning():
2236                                 list += ((_("Stop timer recording"), "timer"),)
2237                         if isStandardInfoBar(self):
2238                                 list += ((_("Do not record"), "no"),)
2239                 if isStandardInfoBar(self) and self.timeshiftEnabled():
2240                         list = list + ((_("Save timeshift file"), "timeshift"),
2241                                 (_("Save timeshift file in movie directory"), "timeshift_movie"))
2242                 if list:
2243                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=title, list=list)
2244                 else:
2245                         return 0
2246
2247 from Tools.ISO639 import LanguageCodes
2248
2249 class InfoBarAudioSelection:
2250         def __init__(self):
2251                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
2252                         {
2253                                 "audioSelection": (self.audioSelection, _("Audio options...")),
2254                         })
2255
2256         def audioSelection(self):
2257                 from Screens.AudioSelection import AudioSelection
2258                 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
2259
2260         def audioSelected(self, ret=None):
2261                 print "[infobar::audioSelected]", ret
2262
2263 class InfoBarSubserviceSelection:
2264         def __init__(self):
2265                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
2266                         {
2267                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
2268                         })
2269
2270                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
2271                         {
2272                                 "nextSubservice": (self.nextSubservice, _("Switch to next sub service")),
2273                                 "prevSubservice": (self.prevSubservice, _("Switch to previous sub service"))
2274                         }, -1)
2275                 self["SubserviceQuickzapAction"].setEnabled(False)
2276
2277                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2278                         {
2279                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
2280                         })
2281                 self.onClose.append(self.__removeNotifications)
2282
2283                 self.bsel = None
2284
2285         def __removeNotifications(self):
2286                 self.session.nav.event.remove(self.checkSubservicesAvail)
2287
2288         def checkSubservicesAvail(self):
2289                 service = self.session.nav.getCurrentService()
2290                 subservices = service and service.subServices()
2291                 if not subservices or subservices.getNumberOfSubservices() == 0:
2292                         self["SubserviceQuickzapAction"].setEnabled(False)
2293
2294         def nextSubservice(self):
2295                 self.changeSubservice(+1)
2296
2297         def prevSubservice(self):
2298                 self.changeSubservice(-1)
2299
2300         def changeSubservice(self, direction):
2301                 service = self.session.nav.getCurrentService()
2302                 subservices = service and service.subServices()
2303                 n = subservices and subservices.getNumberOfSubservices()
2304                 if n and n > 0:
2305                         selection = -1
2306                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
2307                         idx = 0
2308                         while idx < n:
2309                                 if subservices.getSubservice(idx).toString() == ref.toString():
2310                                         selection = idx
2311                                         break
2312                                 idx += 1
2313                         if selection != -1:
2314                                 selection += direction
2315                                 if selection >= n:
2316                                         selection=0
2317                                 elif selection < 0:
2318                                         selection=n-1
2319                                 newservice = subservices.getSubservice(selection)
2320                                 if newservice.valid():
2321                                         del subservices
2322                                         del service
2323                                         self.session.nav.playService(newservice, False)
2324
2325         def subserviceSelection(self):
2326                 service = self.session.nav.getCurrentService()
2327                 subservices = service and service.subServices()
2328                 self.bouquets = self.servicelist.getBouquetList()
2329                 n = subservices and subservices.getNumberOfSubservices()
2330                 selection = 0
2331                 if n and n > 0:
2332                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
2333                         tlist = []
2334                         idx = 0
2335                         while idx < n:
2336                                 i = subservices.getSubservice(idx)
2337                                 if i.toString() == ref.toString():
2338                                         selection = idx
2339                                 tlist.append((i.getName(), i))
2340                                 idx += 1
2341
2342                         if self.bouquets and len(self.bouquets):
2343                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
2344                                 if config.usage.multibouquet.value:
2345                                         tlist = [(_("Quick zap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
2346                                 else:
2347                                         tlist = [(_("Quick zap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
2348                                 selection += 3
2349                         else:
2350                                 tlist = [(_("Quick zap"), "quickzap", service.subServices()), ("--", "")] + tlist
2351                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
2352                                 selection += 2
2353
2354                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a sub service..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
2355
2356         def subserviceSelected(self, service):
2357                 del self.bouquets
2358                 if not service is None:
2359                         if isinstance(service[1], str):
2360                                 if service[1] == "quickzap":
2361                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
2362                                         self.session.open(SubservicesQuickzap, service[2])
2363                         else:
2364                                 self["SubserviceQuickzapAction"].setEnabled(True)
2365                                 self.session.nav.playService(service[1], False)
2366
2367         def addSubserviceToBouquetCallback(self, service):
2368                 if len(service) > 1 and isinstance(service[1], eServiceReference):
2369                         self.selectedSubservice = service
2370                         if self.bouquets is None:
2371                                 cnt = 0
2372                         else:
2373                                 cnt = len(self.bouquets)
2374                         if cnt > 1: # show bouquet list
2375                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
2376                         elif cnt == 1: # add to only one existing bouquet
2377                                 self.addSubserviceToBouquet(self.bouquets[0][1])
2378                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
2379
2380         def bouquetSelClosed(self, confirmed):
2381                 self.bsel = None
2382                 del self.selectedSubservice
2383                 if confirmed:
2384                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
2385
2386         def addSubserviceToBouquet(self, dest):
2387                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
2388                 if self.bsel:
2389                         self.bsel.close(True)
2390                 else:
2391                         del self.selectedSubservice
2392
2393 class InfoBarRedButton:
2394         def __init__(self):
2395                 self["RedButtonActions"] = HelpableActionMap(self, "InfobarRedButtonActions",
2396                         {
2397                                 "activateRedButton": (self.activateRedButton, _("Red button...")),
2398                         })
2399                 self.onHBBTVActivation = [ ]
2400                 self.onRedButtonActivation = [ ]
2401
2402         def activateRedButton(self):
2403                 service = self.session.nav.getCurrentService()
2404                 info = service and service.info()
2405                 if info and info.getInfoString(iServiceInformation.sHBBTVUrl) != "":
2406                         for x in self.onHBBTVActivation:
2407                                 x()
2408                 elif False: # TODO: other red button services
2409                         for x in self.onRedButtonActivation:
2410                                 x()
2411
2412 class InfoBarTimerButton:
2413         def __init__(self):
2414                 self["TimerButtonActions"] = HelpableActionMap(self, "InfobarTimerButtonActions",
2415                         {
2416                                 "timerSelection": (self.timerSelection, _("Timer selection...")),
2417                         })
2418
2419         def timerSelection(self):
2420                 from Screens.TimerEdit import TimerEditList
2421                 self.session.open(TimerEditList)
2422
2423 class InfoBarVmodeButton:
2424         def __init__(self):
2425                 self["VmodeButtonActions"] = HelpableActionMap(self, "InfobarVmodeButtonActions",
2426                         {
2427                                 "vmodeSelection": (self.vmodeSelection, _("Letterbox zoom")),
2428                         })
2429
2430         def vmodeSelection(self):
2431                 self.session.open(VideoMode)
2432
2433 class VideoMode(Screen):
2434         def __init__(self,session):
2435                 Screen.__init__(self, session)
2436                 self["videomode"] = Label()
2437
2438                 self["actions"] = NumberActionMap( [ "InfobarVmodeButtonActions" ],
2439                         {
2440                                 "vmodeSelection": self.selectVMode
2441                         })
2442
2443                 self.Timer = eTimer()
2444                 self.Timer.callback.append(self.quit)
2445                 self.selectVMode()
2446
2447         def selectVMode(self):
2448                 policy = config.av.policy_43
2449                 if self.isWideScreen():
2450                         policy = config.av.policy_169
2451                 idx = policy.choices.index(policy.value)
2452                 idx = (idx + 1) % len(policy.choices)
2453                 policy.value = policy.choices[idx]
2454                 self["videomode"].setText(policy.value)
2455                 self.Timer.start(1000, True)
2456
2457         def isWideScreen(self):
2458                 from Components.Converter.ServiceInfo import WIDESCREEN
2459                 service = self.session.nav.getCurrentService()
2460                 info = service and service.info()
2461                 return info.getInfo(iServiceInformation.sAspect) in WIDESCREEN
2462
2463         def quit(self):
2464                 self.Timer.stop()
2465                 self.close()
2466
2467 class InfoBarAdditionalInfo:
2468         def __init__(self):
2469
2470                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
2471                 self["TimeshiftPossible"] = self["RecordingPossible"]
2472                 self["ExtensionsAvailable"] = Boolean(fixed=1)
2473                 # TODO: these properties should be queried from the input device keymap
2474                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=0)
2475                 self["ShowAudioOnYellow"] = Boolean(fixed=0)
2476                 self["ShowRecordOnRed"] = Boolean(fixed=0)
2477
2478 class InfoBarNotifications:
2479         def __init__(self):
2480                 self.onExecBegin.append(self.checkNotifications)
2481                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
2482                 self.onClose.append(self.__removeNotification)
2483
2484         def __removeNotification(self):
2485                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
2486
2487         def checkNotificationsIfExecing(self):
2488                 if self.execing:
2489                         self.checkNotifications()
2490
2491         def checkNotifications(self):
2492                 notifications = Notifications.notifications
2493                 if notifications:
2494                         n = notifications[0]
2495
2496                         del notifications[0]
2497                         cb = n[0]
2498
2499                         if n[3].has_key("onSessionOpenCallback"):
2500                                 n[3]["onSessionOpenCallback"]()
2501                                 del n[3]["onSessionOpenCallback"]
2502
2503                         if cb:
2504                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
2505                         elif not Notifications.current_notifications and n[4] == "ZapError":
2506                                 if n[3].has_key("timeout"):
2507                                         del n[3]["timeout"]
2508                                 n[3]["enable_input"] = False
2509                                 dlg = self.session.instantiateDialog(n[1], *n[2], **n[3])
2510                                 self.hide()
2511                                 dlg.show()
2512                                 self.notificationDialog = dlg
2513                                 eActionMap.getInstance().bindAction('', -maxint - 1, self.keypressNotification)
2514                         else:
2515                                 dlg = self.session.open(n[1], *n[2], **n[3])
2516
2517                         # remember that this notification is currently active
2518                         d = (n[4], dlg)
2519                         Notifications.current_notifications.append(d)
2520                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
2521
2522         def closeNotificationInstantiateDialog(self):
2523                 if hasattr(self, "notificationDialog"):
2524                         self.session.deleteDialog(self.notificationDialog)
2525                         del self.notificationDialog
2526                         eActionMap.getInstance().unbindAction('', self.keypressNotification)
2527
2528         def keypressNotification(self, key, flag):
2529                 if flag:
2530                         self.closeNotificationInstantiateDialog()
2531
2532         def __notificationClosed(self, d):
2533                 Notifications.current_notifications.remove(d)
2534
2535 class InfoBarServiceNotifications:
2536         def __init__(self):
2537                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2538                         {
2539                                 iPlayableService.evEnd: self.serviceHasEnded
2540                         })
2541
2542         def serviceHasEnded(self):
2543                 print "service end!"
2544
2545                 try:
2546                         self.setSeekState(self.SEEK_STATE_PLAY)
2547                 except:
2548                         pass
2549
2550 class InfoBarCueSheetSupport:
2551         CUT_TYPE_IN = 0
2552         CUT_TYPE_OUT = 1
2553         CUT_TYPE_MARK = 2
2554         CUT_TYPE_LAST = 3
2555
2556         ENABLE_RESUME_SUPPORT = False
2557
2558         def __init__(self, actionmap = "InfobarCueSheetActions"):
2559                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
2560                         {
2561                                 "jumpPreviousMark": (self.jumpPreviousMark, _("Jump to previous marked position")),
2562                                 "jumpNextMark": (self.jumpNextMark, _("Jump to next marked position")),
2563                                 "toggleMark": (self.toggleMark, _("Toggle a cut mark at the current position"))
2564                         }, prio=1)
2565
2566                 self.cut_list = [ ]
2567                 self.is_closing = False
2568                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2569                         {
2570                                 iPlayableService.evStart: self.__serviceStarted,
2571                                 iPlayableService.evCuesheetChanged: self.downloadCuesheet,
2572                         })
2573
2574         def __serviceStarted(self):
2575                 if self.is_closing:
2576                         return
2577                 print "new service started! trying to download cuts!"
2578                 self.downloadCuesheet()
2579
2580                 if self.ENABLE_RESUME_SUPPORT:
2581                         for (pts, what) in self.cut_list:
2582                                 if what == self.CUT_TYPE_LAST:
2583                                         last = pts
2584                                         break
2585                         else:
2586                                 last = getResumePoint(self.session)
2587                         if last is None:
2588                                 return
2589                         # only resume if at least 10 seconds ahead, or <10 seconds before the end.
2590                         seekable = self.__getSeekable()
2591                         if seekable is None:
2592                                 return # Should not happen?
2593                         length = seekable.getLength() or (None,0)
2594                         print "seekable.getLength() returns:", length
2595                         # Hmm, this implies we don't resume if the length is unknown...
2596                         if (last > 900000) and (not length[1]  or (last < length[1] - 900000)):
2597                                 self.resume_point = last
2598                                 l = last / 90000
2599                                 if config.usage.on_movie_start.value == "ask" or not length[1]:
2600                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?") + "\n" + (_("Resume position at %s") % ("%d:%02d:%02d" % (l/3600, l%3600/60, l%60))), timeout=10)
2601                                 elif config.usage.on_movie_start.value == "resume":
2602 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2603 # TRANSLATORS: at the start of a movie, when the user has selected
2604 # TRANSLATORS: "Resume from last position" as start behavior.
2605 # TRANSLATORS: The purpose is to notify the user that the movie starts
2606 # TRANSLATORS: in the middle somewhere and not from the beginning.
2607 # TRANSLATORS: (Some translators seem to have interpreted it as a
2608 # TRANSLATORS: question or a choice, but it is a statement.)
2609                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2610
2611         def playLastCB(self, answer):
2612                 if answer == True:
2613                         self.doSeek(self.resume_point)
2614                 self.hideAfterResume()
2615
2616         def hideAfterResume(self):
2617                 if isinstance(self, InfoBarShowHide):
2618                         self.hide()
2619
2620         def __getSeekable(self):
2621                 service = self.session.nav.getCurrentService()
2622                 if service is None:
2623                         return None
2624                 return service.seek()
2625
2626         def cueGetCurrentPosition(self):
2627                 seek = self.__getSeekable()
2628                 if seek is None:
2629                         return None
2630                 r = seek.getPlayPosition()
2631                 if r[0]:
2632                         return None
2633                 return long(r[1])
2634
2635         def cueGetEndCutPosition(self):
2636                 ret = False
2637                 isin = True
2638                 for cp in self.cut_list:
2639                         if cp[1] == self.CUT_TYPE_OUT:
2640                                 if isin:
2641                                         isin = False
2642                                         ret = cp[0]
2643                         elif cp[1] == self.CUT_TYPE_IN:
2644                                 isin = True
2645                 return ret
2646
2647         def jumpPreviousNextMark(self, cmp, start=False):
2648                 current_pos = self.cueGetCurrentPosition()
2649                 if current_pos is None:
2650                         return False
2651                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2652                 if mark is not None:
2653                         pts = mark[0]
2654                 else:
2655                         return False
2656
2657                 self.doSeek(pts)
2658                 return True
2659
2660         def jumpPreviousMark(self):
2661                 # we add 5 seconds, so if the play position is <5s after
2662                 # the mark, the mark before will be used
2663                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2664
2665         def jumpNextMark(self):
2666                 if not self.jumpPreviousNextMark(lambda x: x-90000):
2667                         self.doSeek(-1)
2668
2669         def getNearestCutPoint(self, pts, cmp=abs, start=False):
2670                 # can be optimized
2671                 beforecut = True
2672                 nearest = None
2673                 bestdiff = -1
2674                 instate = True
2675                 if start:
2676                         bestdiff = cmp(0 - pts)
2677                         if bestdiff >= 0:
2678                                 nearest = [0, False]
2679                 for cp in self.cut_list:
2680                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2681                                 beforecut = False
2682                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
2683                                         diff = cmp(cp[0] - pts)
2684                                         if start and diff >= 0:
2685                                                 nearest = cp
2686                                                 bestdiff = diff
2687                                         else:
2688                                                 nearest = None
2689                                                 bestdiff = -1
2690                         if cp[1] == self.CUT_TYPE_IN:
2691                                 instate = True
2692                         elif cp[1] == self.CUT_TYPE_OUT:
2693                                 instate = False
2694                         elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2695                                 diff = cmp(cp[0] - pts)
2696                                 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2697                                         nearest = cp
2698                                         bestdiff = diff
2699                 return nearest
2700
2701         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2702                 current_pos = self.cueGetCurrentPosition()
2703                 if current_pos is None:
2704                         print "not seekable"
2705                         return
2706
2707                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2708
2709                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2710                         if onlyreturn:
2711                                 return nearest_cutpoint
2712                         if not onlyadd:
2713                                 self.removeMark(nearest_cutpoint)
2714                 elif not onlyremove and not onlyreturn:
2715                         self.addMark((current_pos, self.CUT_TYPE_MARK))
2716
2717                 if onlyreturn:
2718                         return None
2719
2720         def addMark(self, point):
2721                 insort(self.cut_list, point)
2722                 self.uploadCuesheet()
2723                 self.showAfterCuesheetOperation()
2724
2725         def removeMark(self, point):
2726                 self.cut_list.remove(point)
2727                 self.uploadCuesheet()
2728                 self.showAfterCuesheetOperation()
2729
2730         def showAfterCuesheetOperation(self):
2731                 if isinstance(self, InfoBarShowHide):
2732                         self.doShow()
2733
2734         def __getCuesheet(self):
2735                 service = self.session.nav.getCurrentService()
2736                 if service is None:
2737                         return None
2738                 return service.cueSheet()
2739
2740         def uploadCuesheet(self):
2741                 cue = self.__getCuesheet()
2742
2743                 if cue is None:
2744                         print "upload failed, no cuesheet interface"
2745                         return
2746                 cue.setCutList(self.cut_list)
2747
2748         def downloadCuesheet(self):
2749                 cue = self.__getCuesheet()
2750
2751                 if cue is None:
2752                         print "download failed, no cuesheet interface"
2753                         self.cut_list = [ ]
2754                 else:
2755                         self.cut_list = cue.getCutList()
2756
2757 class InfoBarSummary(Screen):
2758         skin = """
2759         <screen position="0,0" size="132,64">
2760                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2761                         <convert type="ClockToText">WithSeconds</convert>
2762                 </widget>
2763                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2764                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2765                         <convert type="ConditionalShowHide">Blink</convert>
2766                 </widget>
2767                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2768                         <convert type="ServiceName">Name</convert>
2769                 </widget>
2770                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2771                         <convert type="EventTime">Progress</convert>
2772                 </widget>
2773         </screen>"""
2774
2775 # for picon:  (path="piconlcd" will use LCD picons)
2776 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2777 #                       <convert type="ServiceName">Reference</convert>
2778 #               </widget>
2779
2780 class InfoBarSummarySupport:
2781         def __init__(self):
2782                 pass
2783
2784         def createSummary(self):
2785                 return InfoBarSummary
2786
2787 class InfoBarMoviePlayerSummary(Screen):
2788         skin = """
2789         <screen position="0,0" size="132,64">
2790                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2791                         <convert type="ClockToText">WithSeconds</convert>
2792                 </widget>
2793                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2794                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2795                         <convert type="ConditionalShowHide">Blink</convert>
2796                 </widget>
2797                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2798                         <convert type="ServiceName">Name</convert>
2799                 </widget>
2800                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2801                         <convert type="ServicePosition">Position</convert>
2802                 </widget>
2803         </screen>"""
2804
2805 class InfoBarMoviePlayerSummarySupport:
2806         def __init__(self):
2807                 pass
2808
2809         def createSummary(self):
2810                 return InfoBarMoviePlayerSummary
2811
2812 class InfoBarTeletextPlugin:
2813         def __init__(self):
2814                 self.teletext_plugin = None
2815
2816                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2817                         self.teletext_plugin = p
2818
2819                 if self.teletext_plugin is not None:
2820                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2821                                 {
2822                                         "startTeletext": (self.startTeletext, _("View teletext..."))
2823                                 })
2824                 else:
2825                         print "no teletext plugin found!"
2826
2827         def startTeletext(self):
2828                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2829
2830 class InfoBarSubtitleSupport(object):
2831         def __init__(self):
2832                 object.__init__(self)
2833                 self["SubtitleSelectionAction"] = HelpableActionMap(self, "InfobarSubtitleSelectionActions",
2834                         {
2835                                 "subtitleSelection": (self.subtitleSelection, _("Subtitle selection...")),
2836                         })
2837
2838                 self.selected_subtitle = None
2839
2840                 if isStandardInfoBar(self):
2841                         self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2842                 else:
2843                         from Screens.InfoBar import InfoBar
2844                         self.subtitle_window = InfoBar.instance.subtitle_window
2845
2846                 self.subtitle_window.hide()
2847
2848                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2849                         {
2850                                 iPlayableService.evStart: self.__serviceChanged,
2851                                 iPlayableService.evEnd: self.__serviceChanged,
2852                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2853                         })
2854
2855         def getCurrentServiceSubtitle(self):
2856                 service = self.session.nav.getCurrentService()
2857                 return service and service.subtitle()
2858
2859         def subtitleSelection(self):
2860                 subtitle = self.getCurrentServiceSubtitle()
2861                 subtitlelist = subtitle and subtitle.getSubtitleList()
2862                 if self.selected_subtitle or subtitlelist and len(subtitlelist)>0:
2863                         from Screens.AudioSelection import SubtitleSelection
2864                         self.session.open(SubtitleSelection, self)
2865                 else:
2866                         return 0
2867
2868         def __serviceChanged(self):
2869                 if self.selected_subtitle:
2870                         self.selected_subtitle = None
2871                         self.subtitle_window.hide()
2872
2873         def __updatedInfo(self):
2874                 if not self.selected_subtitle:
2875                         subtitle = self.getCurrentServiceSubtitle()
2876                         cachedsubtitle = subtitle.getCachedSubtitle()
2877                         if cachedsubtitle:
2878                                 self.enableSubtitle(cachedsubtitle)
2879
2880         def enableSubtitle(self, selectedSubtitle):
2881                 subtitle = self.getCurrentServiceSubtitle()
2882                 self.selected_subtitle = selectedSubtitle
2883                 if subtitle and self.selected_subtitle:
2884                         subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2885                         self.subtitle_window.show()
2886                 else:
2887                         if subtitle:
2888                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2889                         self.subtitle_window.hide()
2890
2891         def restartSubtitle(self):
2892                 if self.selected_subtitle:
2893                         self.enableSubtitle(self.selected_subtitle)
2894
2895 class InfoBarServiceErrorPopupSupport:
2896         def __init__(self):
2897                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2898                         {
2899                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2900                                 iPlayableService.evTunedIn: self.__serviceStarted,
2901                                 iPlayableService.evStart: self.__serviceStarted
2902                         })
2903                 self.__serviceStarted()
2904
2905         def __serviceStarted(self):
2906                 self.closeNotificationInstantiateDialog()
2907                 self.last_error = None
2908                 Notifications.RemovePopup(id = "ZapError")
2909
2910         def __tuneFailed(self):
2911                 if not config.usage.hide_zap_errors.value:
2912                         service = self.session.nav.getCurrentService()
2913                         info = service and service.info()
2914                         error = info and info.getInfo(iServiceInformation.sDVBState)
2915
2916                         if error == self.last_error:
2917                                 error = None
2918                         else:
2919                                 self.last_error = error
2920
2921                         error = {
2922                                 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2923                                 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2924                                 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2925                                 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2926                                 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2927                                 eDVBServicePMTHandler.eventNewProgramInfo: None,
2928                                 eDVBServicePMTHandler.eventTuned: None,
2929                                 eDVBServicePMTHandler.eventSOF: None,
2930                                 eDVBServicePMTHandler.eventEOF: None,
2931                                 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2932                         }.get(error) #this returns None when the key not exist in the dict
2933
2934                         if error: