enigma2 20130114 (master) -> 20130216 (master)
[enigma2.git] / usr / lib / enigma2 / 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.PluginComponent import plugins
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.Sources.Boolean import Boolean
11 from Components.Sources.HbbtvApplication import HbbtvApplication
12 from Components.config import config, ConfigBoolean, ConfigClock
13 from Components.SystemInfo import SystemInfo
14 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath, defaultStorageDevice
15 from Components.ResourceManager import resourcemanager
16 from EpgSelection import EPGSelection
17 from Plugins.Plugin import PluginDescriptor
18
19 from Screen import Screen
20 from Screens.ChoiceBox import ChoiceBox
21 from Screens.Dish import Dish
22 from Screens.EventView import EventViewEPGSelect, EventViewSimple
23 from Screens.InputBox import InputBox
24 from Screens.MessageBox import MessageBox
25 from Screens.MinuteInput import MinuteInput
26 from Screens.TimerSelection import TimerSelection
27 from Screens.PictureInPicture import PictureInPicture
28 from Screens.SubtitleDisplay import SubtitleDisplay
29 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
30 from Screens.TimeDateInput import TimeDateInput
31 from Screens.UnhandledKey import UnhandledKey
32 from ServiceReference import ServiceReference
33
34 from Tools import Notifications
35 from Tools.Directories import fileExists
36
37 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
38         iPlayableService, eServiceReference, eEPGCache, eActionMap
39
40 from time import time, localtime, strftime
41 from bisect import insort
42
43 from RecordTimer import RecordTimerEntry
44 import Screens.Standby
45
46 # hack alert!
47 from Menu import MainMenu, mdom
48
49 class InfoBarDish:
50         def __init__(self):
51                 self.dishDialog = self.session.instantiateDialog(Dish)
52
53 class InfoBarUnhandledKey:
54         def __init__(self):
55                 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
56                 self.hideUnhandledKeySymbolTimer = eTimer()
57                 self.hideUnhandledKeySymbolTimer.callback.append(self.unhandledKeyDialog.hide)
58                 self.checkUnusedTimer = eTimer()
59                 self.checkUnusedTimer.callback.append(self.checkUnused)
60                 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
61                 eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
62                 eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
63                 self.flags = (1<<1);
64                 self.uflags = 0;
65
66         #this function is called on every keypress!
67         def actionA(self, key, flag):
68                 if flag != 4:
69                         if self.flags & (1<<1):
70                                 self.flags = self.uflags = 0
71                         self.flags |= (1<<flag)
72                         if flag == 1: # break
73                                 self.checkUnusedTimer.start(0, True)
74                 return 0
75
76         #this function is only called when no other action has handled this key
77         def actionB(self, key, flag):
78                 if flag != 4:
79                         self.uflags |= (1<<flag)
80
81         def checkUnused(self):
82                 if self.flags == self.uflags:
83                         self.unhandledKeyDialog.show()
84                         self.hideUnhandledKeySymbolTimer.start(2000, True)
85
86 class InfoBarAutoSleepTimer:
87         def __init__(self):
88                 self.inactivityTimer = eTimer()
89                 self.inactivityTimer.callback.append(self.inactive)
90                 self.keypress(None, 1)
91                 eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.keypress) #highest prio
92                 if not config.usage.inactivity_shutdown_initialized.value:
93                         from Components.UsageConfig import inactivity_shutdown_choices
94                         list = []
95                         for item in inactivity_shutdown_choices:
96                                 list.append((item[1], item[0]))
97                         Notifications.AddNotificationWithCallback(
98                                 self._initialAutoSleepValueSet,
99                                 ChoiceBox,
100                                 list = list,
101                                 selection=3,
102                                 title=_("Please specify the amount of time the device has to be inactive (no key pressed) before shutting down automatically"),
103                                 titlebartext=_("Inactivity shutdown"))
104
105         def _initialAutoSleepValueSet(self, answer):
106                 config.usage.inactivity_shutdown_initialized.value = True
107                 config.usage.inactivity_shutdown_initialized.save()
108                 if answer != None:
109                         config.usage.inactivity_shutdown.value = answer[1]
110
111         #this function is called on every keypress!
112         def keypress(self, key, flag):
113                 if flag == 1: # break code
114                         hours = config.usage.inactivity_shutdown.value
115                         if hours != "never":
116                                 self.inactivityTimer.startLongTimer(int(hours)*60*60)
117                         else:
118                                 self.inactivityTimer.stop()
119                 return 0
120
121         def inactive(self):
122                 print "[InfoBarAutoSleepTimer].inactive"
123                 if Screens.Standby.inStandby == None:
124                         self.session.openWithCallback(self.shutdown, MessageBox, _("The device will shutdown due to inactivity.\nDo you want to abort the shutdown?"), MessageBox.TYPE_YESNO, timeout=120, default=False, title=_("Inactivity shutdown"))
125                 else:
126                         self.shutdown(False)
127                         return
128
129         def shutdown(self, aborted):
130                 print "[InfoBarAutoSleepTimer].shutdown"
131                 if aborted or Screens.Standby.inTryQuitMainloop:
132                         print "aborted"
133                         self.keypress(None, 1) #restart the timer
134                         return
135
136                 if Screens.Standby.inStandby != None:
137                         print "RecordTimer.TryQuitMainloop"
138                         RecordTimerEntry.TryQuitMainloop(True)
139                 else:
140                         print "Screens.Standby.TryQuitMainloop"
141                         self.session.open(Screens.Standby.TryQuitMainloop, 1)
142
143 class InfoBarShowHide:
144         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
145         fancy animations. """
146         STATE_HIDDEN = 0
147         STATE_HIDING = 1
148         STATE_SHOWING = 2
149         STATE_SHOWN = 3
150
151         def __init__(self):
152                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
153                         {
154                                 "toggleShow": self.toggleShow,
155                                 "hide": self.hide,
156                         }, 1) # lower prio to make it possible to override ok and cancel..
157
158                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
159                         {
160                                 iPlayableService.evStart: self.serviceStarted,
161                         })
162
163                 self.__state = self.STATE_SHOWN
164                 self.__locked = 0
165
166                 self.hideTimer = eTimer()
167                 self.hideTimer.callback.append(self.doTimerHide)
168                 self.hideTimer.start(5000, True)
169
170                 self.onShow.append(self.__onShow)
171                 self.onHide.append(self.__onHide)
172
173         def serviceStarted(self):
174                 if self.execing:
175                         if config.usage.show_infobar_on_zap.value:
176                                 self.doShow()
177
178         def __onShow(self):
179                 self.__state = self.STATE_SHOWN
180                 self.startHideTimer()
181
182         def startHideTimer(self):
183                 if self.__state == self.STATE_SHOWN and not self.__locked:
184                         idx = config.usage.infobar_timeout.index
185                         if idx:
186                                 self.hideTimer.start(idx*1000, True)
187
188         def __onHide(self):
189                 self.__state = self.STATE_HIDDEN
190
191         def doShow(self):
192                 self.show()
193                 self.startHideTimer()
194
195         def doTimerHide(self):
196                 self.hideTimer.stop()
197                 if self.__state == self.STATE_SHOWN:
198                         self.hide()
199
200         def toggleShow(self):
201                 if self.__state == self.STATE_SHOWN:
202                         self.hide()
203                         self.hideTimer.stop()
204                 elif self.__state == self.STATE_HIDDEN:
205                         self.show()
206
207         def lockShow(self):
208                 self.__locked = self.__locked + 1
209                 if self.execing:
210                         self.show()
211                         self.hideTimer.stop()
212
213         def unlockShow(self):
214                 self.__locked = self.__locked - 1
215                 if self.execing:
216                         self.startHideTimer()
217
218 #       def startShow(self):
219 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
220 #               self.__state = self.STATE_SHOWN
221 #
222 #       def startHide(self):
223 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
224 #               self.__state = self.STATE_HIDDEN
225
226 class NumberZap(Screen):
227         def quit(self):
228                 self.Timer.stop()
229                 self.close(0)
230
231         def keyOK(self):
232                 self.Timer.stop()
233                 self.close(int(self["number"].getText()))
234
235         def keyNumberGlobal(self, number):
236                 self.Timer.start(3000, True)            #reset timer
237                 self.field = self.field + str(number)
238                 self["number"].setText(self.field)
239                 if len(self.field) >= 4:
240                         self.keyOK()
241
242         def __init__(self, session, number):
243                 Screen.__init__(self, session)
244                 self.field = str(number)
245
246                 self["channel"] = Label(_("Channel:"))
247
248                 self["number"] = Label(self.field)
249
250                 self["actions"] = NumberActionMap( [ "SetupActions" ],
251                         {
252                                 "cancel": self.quit,
253                                 "ok": self.keyOK,
254                                 "1": self.keyNumberGlobal,
255                                 "2": self.keyNumberGlobal,
256                                 "3": self.keyNumberGlobal,
257                                 "4": self.keyNumberGlobal,
258                                 "5": self.keyNumberGlobal,
259                                 "6": self.keyNumberGlobal,
260                                 "7": self.keyNumberGlobal,
261                                 "8": self.keyNumberGlobal,
262                                 "9": self.keyNumberGlobal,
263                                 "0": self.keyNumberGlobal
264                         })
265
266                 self.Timer = eTimer()
267                 self.Timer.callback.append(self.keyOK)
268                 self.Timer.start(3000, True)
269
270 class InfoBarNumberZap:
271         """ Handles an initial number for NumberZapping """
272         def __init__(self):
273                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
274                         {
275                                 "1": self.keyNumberGlobal,
276                                 "2": self.keyNumberGlobal,
277                                 "3": self.keyNumberGlobal,
278                                 "4": self.keyNumberGlobal,
279                                 "5": self.keyNumberGlobal,
280                                 "6": self.keyNumberGlobal,
281                                 "7": self.keyNumberGlobal,
282                                 "8": self.keyNumberGlobal,
283                                 "9": self.keyNumberGlobal,
284                                 "0": self.keyNumberGlobal,
285                         })
286
287         def keyNumberGlobal(self, number):
288 #               print "You pressed number " + str(number)
289                 if number == 0:
290                         if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
291                                 self.pipDoHandle0Action()
292                         else:
293                                 self.servicelist.recallPrevService()
294                 else:
295                         if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
296                                 self.session.openWithCallback(self.numberEntered, NumberZap, number)
297
298         def numberEntered(self, retval):
299 #               print self.servicelist
300                 if retval > 0:
301                         self.zapToNumber(retval)
302
303         def searchNumberHelper(self, serviceHandler, num, bouquet):
304                 servicelist = serviceHandler.list(bouquet)
305                 if not servicelist is None:
306                         while num:
307                                 serviceIterator = servicelist.getNext()
308                                 if not serviceIterator.valid(): #check end of list
309                                         break
310                                 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
311                                 if playable:
312                                         num -= 1;
313                         if not num: #found service with searched number ?
314                                 return serviceIterator, 0
315                 return None, num
316
317         def zapToNumber(self, number):
318                 bouquet = self.servicelist.bouquet_root
319                 service = None
320                 serviceHandler = eServiceCenter.getInstance()
321                 if not config.usage.multibouquet.value:
322                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
323                 else:
324                         bouquetlist = serviceHandler.list(bouquet)
325                         if not bouquetlist is None:
326                                 while number:
327                                         bouquet = bouquetlist.getNext()
328                                         if not bouquet.valid(): #check end of list
329                                                 break
330                                         if bouquet.flags & eServiceReference.isDirectory:
331                                                 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
332                 if not service is None:
333                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
334                                 self.servicelist.clearPath()
335                                 if self.servicelist.bouquet_root != bouquet:
336                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
337                                 self.servicelist.enterPath(bouquet)
338                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
339                         self.servicelist.zap()
340
341 config.misc.initialchannelselection = ConfigBoolean(default = True)
342
343 class InfoBarChannelSelection:
344         """ ChannelSelection - handles the channelSelection dialog and the initial
345         channelChange actions which open the channelSelection dialog """
346         def __init__(self):
347                 #instantiate forever
348                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
349                 self.servicelist.onRootChanged.append(self.__onServiceListRootChanged)
350
351                 if config.misc.initialchannelselection.value:
352                         self.onShown.append(self.firstRun)
353
354                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
355                         {
356                                 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
357                                 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
358                                 "zapUp": (self.zapUp, _("previous channel")),
359                                 "zapDown": (self.zapDown, _("next channel")),
360                                 "historyBack": (self.historyBack, _("previous channel in history")),
361                                 "historyNext": (self.historyNext, _("next channel in history")),
362                                 "openServiceList": (self.openServiceList, _("open servicelist")),
363                         })
364
365                 self.onServiceListRootChanged = []
366
367         def __onServiceListRootChanged(self, ref):
368                 for fnc in self.onServiceListRootChanged:
369                         fnc(ref)
370
371         def showTvChannelList(self, zap=False):
372                 self.servicelist.setModeTv()
373                 if zap:
374                         self.servicelist.zap()
375                 self.session.execDialog(self.servicelist)
376
377         def showRadioChannelList(self, zap=False):
378                 self.servicelist.setModeRadio()
379                 if zap:
380                         self.servicelist.zap()
381                 self.session.execDialog(self.servicelist)
382
383         def firstRun(self):
384                 self.onShown.remove(self.firstRun)
385                 config.misc.initialchannelselection.value = False
386                 config.misc.initialchannelselection.save()
387                 self.switchChannelDown()
388
389         def historyBack(self):
390                 self.servicelist.historyBack()
391
392         def historyNext(self):
393                 self.servicelist.historyNext()
394
395         def switchChannelUp(self):
396                 self.servicelist.moveUp()
397                 self.session.execDialog(self.servicelist)
398
399         def switchChannelDown(self):
400                 self.servicelist.moveDown()
401                 self.session.execDialog(self.servicelist)
402
403         def openServiceList(self):
404                 self.session.execDialog(self.servicelist)
405
406         def zapUp(self):
407                 if self.servicelist.inBouquet():
408                         prev = self.servicelist.getCurrentSelection()
409                         if prev:
410                                 prev = prev.toString()
411                                 while True:
412                                         if config.usage.quickzap_bouquet_change.value:
413                                                 if self.servicelist.atBegin():
414                                                         self.servicelist.prevBouquet()
415                                         self.servicelist.moveUp()
416                                         cur = self.servicelist.getCurrentSelection()
417                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
418                                                 break
419                 else:
420                         self.servicelist.moveUp()
421                 self.servicelist.zap()
422
423         def zapDown(self):
424                 if self.servicelist.inBouquet():
425                         prev = self.servicelist.getCurrentSelection()
426                         if prev:
427                                 prev = prev.toString()
428                                 while True:
429                                         if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
430                                                 self.servicelist.nextBouquet()
431                                         else:
432                                                 self.servicelist.moveDown()
433                                         cur = self.servicelist.getCurrentSelection()
434                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
435                                                 break
436                 else:
437                         self.servicelist.moveDown()
438                 self.servicelist.zap()
439
440 class InfoBarMenu:
441         """ Handles a menu action, to open the (main) menu """
442         def __init__(self):
443                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
444                         {
445                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
446                         })
447                 self.session.infobar = None
448
449         def mainMenu(self):
450                 print "loading mainmenu XML..."
451                 menu = mdom.getroot()
452                 assert menu.tag == "menu", "root element in menu must be 'menu'!"
453
454                 self.session.infobar = self
455                 # so we can access the currently active infobar from screens opened from within the mainmenu
456                 # at the moment used from the SubserviceSelection
457
458                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
459
460         def mainMenuClosed(self, *val):
461                 self.session.infobar = None
462
463 #yet used in MoviePlayer.. not normal Infobar ... look at InfoBarEPG!
464 class InfoBarSimpleEventView:
465         """ Opens the Eventview for now/next """
466         def __init__(self):
467                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
468                         {
469                                 "showEventInfo": (self.openEventView, _("show event details")),
470                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
471                         })
472
473         def showEventInfoWhenNotVisible(self):
474                 if self.shown:
475                         self.openEventView()
476                 else:
477                         self.toggleShow()
478                         return 1
479
480         def openEventView(self):
481                 epglist = [ ]
482                 self.epglist = epglist
483                 service = self.session.nav.getCurrentService()
484                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
485                 info = service.info()
486                 ptr=info.getEvent(0)
487                 if ptr:
488                         epglist.append(ptr)
489                 ptr=info.getEvent(1)
490                 if ptr:
491                         epglist.append(ptr)
492                 if epglist:
493                         self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
494
495         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
496                 epglist = self.epglist
497                 if len(epglist) > 1:
498                         tmp = epglist[0]
499                         epglist[0] = epglist[1]
500                         epglist[1] = tmp
501                         setEvent(epglist[0])
502
503 class SimpleServicelist:
504         def __init__(self, services):
505                 self.services = services
506                 self.length = len(services)
507                 self.current = 0
508
509         def selectService(self, service):
510                 if not self.length:
511                         self.current = -1
512                         return False
513                 else:
514                         self.current = 0
515                         while self.services[self.current].ref != service:
516                                 self.current += 1
517                                 if self.current >= self.length:
518                                         return False
519                 return True
520
521         def nextService(self):
522                 if not self.length:
523                         return
524                 if self.current+1 < self.length:
525                         self.current += 1
526                 else:
527                         self.current = 0
528
529         def prevService(self):
530                 if not self.length:
531                         return
532                 if self.current-1 > -1:
533                         self.current -= 1
534                 else:
535                         self.current = self.length - 1
536
537         def currentService(self):
538                 if not self.length or self.current >= self.length:
539                         return None
540                 return self.services[self.current]
541
542 class InfoBarEPG:
543         """ EPG - Opens an EPG list when the showEPGList action fires """
544         def __init__(self):
545                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
546                         {
547                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
548                         })
549
550                 self.is_now_next = False
551                 self.dlg_stack = [ ]
552                 self.bouquetSel = None
553                 self.eventView = None
554                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
555                         {
556                                 "showEventInfo": (self.openEventView, _("show EPG...")),
557                                 "showEventInfoPlugin": (self.showEventInfoPlugins, _("list of EPG views...")),
558                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
559                         })
560
561         def showEventInfoWhenNotVisible(self):
562                 if self.shown:
563                         self.openEventView()
564                 else:
565                         self.toggleShow()
566                         return 1
567
568         def zapToService(self, service):
569                 if not service is None:
570                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
571                                 self.servicelist.clearPath()
572                                 if self.servicelist.bouquet_root != self.epg_bouquet:
573                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
574                                 self.servicelist.enterPath(self.epg_bouquet)
575                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
576                         self.servicelist.zap()
577
578         def getBouquetServices(self, bouquet):
579                 services = [ ]
580                 servicelist = eServiceCenter.getInstance().list(bouquet)
581                 if not servicelist is None:
582                         while True:
583                                 service = servicelist.getNext()
584                                 if not service.valid(): #check if end of list
585                                         break
586                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
587                                         continue
588                                 services.append(ServiceReference(service))
589                 return services
590
591         def openBouquetEPG(self, bouquet, withCallback=True):
592                 services = self.getBouquetServices(bouquet)
593                 if services:
594                         self.epg_bouquet = bouquet
595                         if withCallback:
596                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
597                         else:
598                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
599
600         def changeBouquetCB(self, direction, epg):
601                 if self.bouquetSel:
602                         if direction > 0:
603                                 self.bouquetSel.down()
604                         else:
605                                 self.bouquetSel.up()
606                         bouquet = self.bouquetSel.getCurrent()
607                         services = self.getBouquetServices(bouquet)
608                         if services:
609                                 self.epg_bouquet = bouquet
610                                 epg.setServices(services)
611
612         def closed(self, ret=False):
613                 closedScreen = self.dlg_stack.pop()
614                 if closedScreen is self.bouquetSel:
615                         self.bouquetSel = None
616                 elif closedScreen is self.eventView:
617                         self.eventView = None
618                         self.is_now_next = False
619                 if ret:
620                         dlgs=len(self.dlg_stack)
621                         if dlgs > 0:
622                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
623
624         def openMultiServiceEPG(self, withCallback=True):
625                 bouquets = self.servicelist.getBouquetList()
626                 if bouquets is None:
627                         cnt = 0
628                 else:
629                         cnt = len(bouquets)
630                 if config.usage.multiepg_ask_bouquet.value:
631                         self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
632                 else:
633                         self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
634
635         def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
636                 if cnt > 1: # show bouquet list
637                         if withCallback:
638                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
639                                 self.dlg_stack.append(self.bouquetSel)
640                         else:
641                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
642                 elif cnt == 1:
643                         self.openBouquetEPG(bouquets[0][1], withCallback)
644
645         def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
646                 root = self.servicelist.getRoot()
647                 rootstr = root.toCompareString()
648                 current = 0
649                 for bouquet in bouquets:
650                         if bouquet[1].toCompareString() == rootstr:
651                                 break
652                         current += 1
653                 if current >= cnt:
654                         current = 0
655                 if cnt > 1: # create bouquet list for bouq+/-
656                         self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
657                 if cnt >= 1:
658                         self.openBouquetEPG(root, withCallback)
659
660         def changeServiceCB(self, direction, epg):
661                 if self.serviceSel:
662                         if direction > 0:
663                                 self.serviceSel.nextService()
664                         else:
665                                 self.serviceSel.prevService()
666                         epg.setService(self.serviceSel.currentService())
667
668         def SingleServiceEPGClosed(self, ret=False):
669                 self.serviceSel = None
670
671         def openSingleServiceEPG(self):
672                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
673                 if ref:
674                         if self.servicelist.getMutableList() is not None: # bouquet in channellist
675                                 current_path = self.servicelist.getRoot()
676                                 services = self.getBouquetServices(current_path)
677                                 self.serviceSel = SimpleServicelist(services)
678                                 if self.serviceSel.selectService(ref):
679                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
680                                 else:
681                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
682                         else:
683                                 self.session.open(EPGSelection, ref)
684
685         def showEventInfoPlugins(self):
686                 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
687
688                 if list:
689                         list.append((_("show single service EPG..."), self.openSingleServiceEPG))
690                         list.append((_("Multi EPG"), self.openMultiServiceEPG))
691                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
692                 else:
693                         self.openSingleServiceEPG()
694
695         def runPlugin(self, plugin):
696                 plugin(session = self.session, servicelist = self.servicelist)
697
698         def EventInfoPluginChosen(self, answer):
699                 if answer is not None:
700                         answer[1]()
701
702         def openSimilarList(self, eventid, refstr):
703                 self.session.open(EPGSelection, refstr, None, eventid)
704
705         def getNowNext(self):
706                 epglist = [ ]
707                 service = self.session.nav.getCurrentService()
708                 info = service and service.info()
709                 ptr = info and info.getEvent(0)
710                 if ptr:
711                         epglist.append(ptr)
712                 ptr = info and info.getEvent(1)
713                 if ptr:
714                         epglist.append(ptr)
715                 self.epglist = epglist
716
717         def __evEventInfoChanged(self):
718                 if self.is_now_next:
719                         self.getNowNext()
720                         if self.epglist:
721                                 self.eventView.setEvent(self.epglist[0])
722
723         def openEventView(self):
724                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
725                 self.getNowNext()
726                 epglist = self.epglist
727                 if not epglist:
728                         epg = eEPGCache.getInstance()
729                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
730                         if ptr:
731                                 epglist.append(ptr)
732                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
733                                 if ptr:
734                                         epglist.append(ptr)
735                 else:
736                         self.is_now_next = True
737                 if epglist:
738                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
739                         self.dlg_stack.append(self.eventView)
740                 else:
741                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
742                         self.openMultiServiceEPG(False)
743
744         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
745                 epglist = self.epglist
746                 if len(epglist) > 1:
747                         tmp = epglist[0]
748                         epglist[0]=epglist[1]
749                         epglist[1]=tmp
750                         setEvent(epglist[0])
751
752 class InfoBarRdsDecoder:
753         """provides RDS and Rass support/display"""
754         def __init__(self):
755                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
756                 self.rass_interactive = None
757
758                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
759                         {
760                                 iPlayableService.evEnd: self.__serviceStopped,
761                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
762                         })
763
764                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
765                 {
766                         "startRassInteractive": self.startRassInteractive
767                 },-1)
768
769                 self["RdsActions"].setEnabled(False)
770
771                 self.onLayoutFinish.append(self.rds_display.show)
772                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
773
774         def RassInteractivePossibilityChanged(self, state):
775                 self["RdsActions"].setEnabled(state)
776
777         def RassSlidePicChanged(self):
778                 if not self.rass_interactive:
779                         service = self.session.nav.getCurrentService()
780                         decoder = service and service.rdsDecoder()
781                         if decoder:
782                                 decoder.showRassSlidePicture()
783
784         def __serviceStopped(self):
785                 if self.rass_interactive is not None:
786                         rass_interactive = self.rass_interactive
787                         self.rass_interactive = None
788                         rass_interactive.close()
789
790         def startRassInteractive(self):
791                 self.rds_display.hide()
792                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
793
794         def RassInteractiveClosed(self, *val):
795                 if self.rass_interactive is not None:
796                         self.rass_interactive = None
797                         self.RassSlidePicChanged()
798                 self.rds_display.show()
799
800 class InfoBarSeek:
801         """handles actions like seeking, pause"""
802
803         SEEK_STATE_PLAY = (0, 0, 0, ">")
804         SEEK_STATE_PAUSE = (1, 0, 0, "||")
805         SEEK_STATE_EOF = (1, 0, 0, "END")
806         SEEK_STATE_STOP = (0, 0, 0, "STOP")
807
808         def __init__(self, actionmap = "InfobarSeekActions"):
809                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
810                         {
811                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
812                                 iPlayableService.evStart: self.__serviceStarted,
813
814                                 iPlayableService.evEOF: self.__evEOF,
815                                 iPlayableService.evSOF: self.__evSOF
816                         })
817                 self.fast_winding_hint_message_showed = False
818
819                 class InfoBarSeekActionMap(HelpableActionMap):
820                         def __init__(self, screen, *args, **kwargs):
821                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
822                                 self.screen = screen
823
824                         def action(self, contexts, action):
825                                 print "action:", action
826                                 if action[:5] == "seek:":
827                                         time = int(action[5:])
828                                         self.screen.doSeekRelative(time * 90000)
829                                         return 1
830                                 elif action[:8] == "seekdef:":
831                                         key = int(action[8:])
832                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
833                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
834                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
835                                         self.screen.doSeekRelative(time * 90000)
836                                         return 1
837                                 else:
838                                         return HelpableActionMap.action(self, contexts, action)
839
840                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
841                         {
842                                 "playpauseService": self.playpauseService,
843                                 "pauseService": (self.pauseService, _("pause")),
844                                 "unPauseService": (self.unPauseService, _("continue")),
845
846                                 "seekFwd": (self.seekFwd, _("skip forward")),
847                                 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
848                                 "seekBack": (self.seekBack, _("skip backward")),
849                                 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
850                         }, prio=-1)
851                         # give them a little more priority to win over color buttons
852
853                 self["SeekActions"].setEnabled(False)
854
855                 self.seekstate = self.SEEK_STATE_STOP
856                 self.lastseekstate = self.SEEK_STATE_STOP
857
858                 self.onPlayStateChanged = [ ]
859
860                 self.lockedBecauseOfSkipping = False
861
862                 self.__seekableStatusChanged()
863
864         def makeStateForward(self, n):
865                 return (0, n, 0, ">> %dx" % n)
866
867         def makeStateBackward(self, n):
868                 return (0, -n, 0, "<< %dx" % n)
869
870         def makeStateSlowMotion(self, n):
871                 return (0, 0, n, "/%d" % n)
872
873         def isStateForward(self, state):
874                 return state[1] > 1
875
876         def isStateBackward(self, state):
877                 return state[1] < 0
878
879         def isStateSlowMotion(self, state):
880                 return state[1] == 0 and state[2] > 1
881
882         def getHigher(self, n, lst):
883                 for x in lst:
884                         if x > n:
885                                 return x
886                 return False
887
888         def getLower(self, n, lst):
889                 lst = lst[:]
890                 lst.reverse()
891                 for x in lst:
892                         if x < n:
893                                 return x
894                 return False
895
896         def showAfterSeek(self):
897                 if isinstance(self, InfoBarShowHide):
898                         self.doShow()
899
900         def up(self):
901                 pass
902
903         def down(self):
904                 pass
905
906         def getSeek(self):
907                 service = self.session.nav.getCurrentService()
908                 if service is None:
909                         return None
910
911                 seek = service.seek()
912
913                 if seek is None or not seek.isCurrentlySeekable():
914                         return None
915
916                 return seek
917
918         def isSeekable(self):
919                 import Screens.InfoBar
920
921                 if self.getSeek() is None or (isinstance(self, Screens.InfoBar.InfoBar) and not self.timeshift_enabled):
922                         return False
923                 return True
924
925         def __seekableStatusChanged(self):
926 #               print "seekable status changed!"
927                 if not self.isSeekable():
928                         self["SeekActions"].setEnabled(False)
929 #                       print "not seekable, return to play"
930                         self.setSeekState(self.SEEK_STATE_PLAY)
931                 else:
932                         self["SeekActions"].setEnabled(True)
933 #                       print "seekable"
934
935         def __serviceStarted(self):
936                 self.fast_winding_hint_message_showed = False
937                 self.setSeekState(self.SEEK_STATE_PLAY, True)
938                 self.__seekableStatusChanged()
939
940         def setSeekState(self, state, onlyGUI = False):
941                 service = self.session.nav.getCurrentService()
942                 ret = 0
943
944                 if service is None:
945                         return False
946
947                 if not onlyGUI and state != self.SEEK_STATE_STOP:
948                         if not self.isSeekable():
949                                 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
950                                         state = self.SEEK_STATE_PLAY
951
952                         pauseable = service.pause()
953
954                         if pauseable is None:
955                                 print "not pauseable."
956                                 state = self.SEEK_STATE_PLAY
957
958                         if pauseable is not None:
959                                 if state[0]:
960                                         ret = pauseable.pause()
961                                         print "resolved to PAUSE", ret
962                                 elif state[1]:
963                                         ret = pauseable.setFastForward(state[1])
964                                         print "resolved to FAST FORWARD", ret
965                                 elif state[2]:
966                                         ret = pauseable.setSlowMotion(state[2])
967                                         print "resolved to SLOW MOTION", ret
968                                 else:
969                                         ret = pauseable.unpause()
970                                         print "resolved to PLAY", ret
971
972                 if ret == 0:
973                         self.seekstate = state
974                         for c in self.onPlayStateChanged:
975                                 c(self.seekstate)
976
977                 self.checkSkipShowHideLock()
978
979                 return (ret == 0)
980
981         def playpauseService(self):
982                 if self.seekstate != self.SEEK_STATE_PLAY:
983                         self.unPauseService()
984                 else:
985                         self.pauseService()
986
987         def pauseService(self):
988                 if self.seekstate == self.SEEK_STATE_PAUSE:
989                         if config.seek.on_pause.value == "play":
990                                 self.unPauseService()
991                         elif config.seek.on_pause.value == "step":
992                                 self.doSeekRelative(1)
993                         elif config.seek.on_pause.value == "last":
994                                 self.setSeekState(self.lastseekstate)
995                                 self.lastseekstate = self.SEEK_STATE_PLAY
996                 else:
997                         if self.seekstate != self.SEEK_STATE_EOF:
998                                 self.lastseekstate = self.seekstate
999                         self.setSeekState(self.SEEK_STATE_PAUSE);
1000
1001         def unPauseService(self):
1002                 print "unpause"
1003                 if self.seekstate == self.SEEK_STATE_PLAY:
1004                         return 0
1005                 self.setSeekState(self.SEEK_STATE_PLAY)
1006
1007         def doSeek(self, pts):
1008                 seekable = self.getSeek()
1009                 if seekable is None:
1010                         return
1011                 seekable.seekTo(pts)
1012
1013         def doSeekRelative(self, pts):
1014                 seekable = self.getSeek()
1015                 if seekable is None:
1016                         return
1017                 prevstate = self.seekstate
1018
1019                 if self.seekstate == self.SEEK_STATE_EOF:
1020                         if prevstate == self.SEEK_STATE_PAUSE:
1021                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1022                         else:
1023                                 self.setSeekState(self.SEEK_STATE_PLAY)
1024                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
1025                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
1026                         self.showAfterSeek()
1027
1028         def seekFwd(self):
1029                 seek = self.getSeek()
1030                 if seek and not (seek.isCurrentlySeekable() & 2):
1031                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1032                                 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)
1033                                 self.fast_winding_hint_message_showed = True
1034                                 return
1035                         return 0 # trade as unhandled action
1036                 if self.seekstate == self.SEEK_STATE_PLAY:
1037                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1038                 elif self.seekstate == self.SEEK_STATE_PAUSE:
1039                         if len(config.seek.speeds_slowmotion.value):
1040                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
1041                         else:
1042                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1043                 elif self.seekstate == self.SEEK_STATE_EOF:
1044                         pass
1045                 elif self.isStateForward(self.seekstate):
1046                         speed = self.seekstate[1]
1047                         if self.seekstate[2]:
1048                                 speed /= self.seekstate[2]
1049                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
1050                         self.setSeekState(self.makeStateForward(speed))
1051                 elif self.isStateBackward(self.seekstate):
1052                         speed = -self.seekstate[1]
1053                         if self.seekstate[2]:
1054                                 speed /= self.seekstate[2]
1055                         speed = self.getLower(speed, config.seek.speeds_backward.value)
1056                         if speed:
1057                                 self.setSeekState(self.makeStateBackward(speed))
1058                         else:
1059                                 self.setSeekState(self.SEEK_STATE_PLAY)
1060                 elif self.isStateSlowMotion(self.seekstate):
1061                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
1062                         self.setSeekState(self.makeStateSlowMotion(speed))
1063
1064         def seekBack(self):
1065                 seek = self.getSeek()
1066                 if seek and not (seek.isCurrentlySeekable() & 2):
1067                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1068                                 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)
1069                                 self.fast_winding_hint_message_showed = True
1070                                 return
1071                         return 0 # trade as unhandled action
1072                 seekstate = self.seekstate
1073                 if seekstate == self.SEEK_STATE_PLAY:
1074                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1075                 elif seekstate == self.SEEK_STATE_EOF:
1076                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1077                         self.doSeekRelative(-6)
1078                 elif seekstate == self.SEEK_STATE_PAUSE:
1079                         self.doSeekRelative(-1)
1080                 elif self.isStateForward(seekstate):
1081                         speed = seekstate[1]
1082                         if seekstate[2]:
1083                                 speed /= seekstate[2]
1084                         speed = self.getLower(speed, config.seek.speeds_forward.value)
1085                         if speed:
1086                                 self.setSeekState(self.makeStateForward(speed))
1087                         else:
1088                                 self.setSeekState(self.SEEK_STATE_PLAY)
1089                 elif self.isStateBackward(seekstate):
1090                         speed = -seekstate[1]
1091                         if seekstate[2]:
1092                                 speed /= seekstate[2]
1093                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1094                         self.setSeekState(self.makeStateBackward(speed))
1095                 elif self.isStateSlowMotion(seekstate):
1096                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1097                         if speed:
1098                                 self.setSeekState(self.makeStateSlowMotion(speed))
1099                         else:
1100                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1101
1102         def seekFwdManual(self):
1103                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1104
1105         def fwdSeekTo(self, minutes):
1106                 print "Seek", minutes, "minutes forward"
1107                 self.doSeekRelative(minutes * 60 * 90000)
1108
1109         def seekBackManual(self):
1110                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1111
1112         def rwdSeekTo(self, minutes):
1113                 print "rwdSeekTo"
1114                 self.doSeekRelative(-minutes * 60 * 90000)
1115
1116         def checkSkipShowHideLock(self):
1117                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1118
1119                 if config.usage.show_infobar_on_skip.value:
1120                         if self.lockedBecauseOfSkipping and not wantlock:
1121                                 self.unlockShow()
1122                                 self.lockedBecauseOfSkipping = False
1123
1124                         if wantlock and not self.lockedBecauseOfSkipping:
1125                                 self.lockShow()
1126                                 self.lockedBecauseOfSkipping = True
1127
1128         def calcRemainingTime(self):
1129                 seekable = self.getSeek()
1130                 if seekable is not None:
1131                         len = seekable.getLength()
1132                         try:
1133                                 tmp = self.cueGetEndCutPosition()
1134                                 if tmp:
1135                                         len = [False, tmp]
1136                         except:
1137                                 pass
1138                         pos = seekable.getPlayPosition()
1139                         speednom = self.seekstate[1] or 1
1140                         speedden = self.seekstate[2] or 1
1141                         if not len[0] and not pos[0]:
1142                                 if len[1] <= pos[1]:
1143                                         return 0
1144                                 time = (len[1] - pos[1])*speedden/(90*speednom)
1145                                 return time
1146                 return False
1147
1148         def __evEOF(self):
1149                 if self.seekstate == self.SEEK_STATE_EOF:
1150                         return
1151
1152                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1153                 seekstate = self.seekstate
1154                 if self.seekstate != self.SEEK_STATE_PAUSE:
1155                         self.setSeekState(self.SEEK_STATE_EOF)
1156
1157                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1158                         seekable = self.getSeek()
1159                         if seekable is not None:
1160                                 seekable.seekTo(-1)
1161                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1162                         self.doEofInternal(True)
1163                 else:
1164                         self.doEofInternal(False)
1165
1166         def doEofInternal(self, playing):
1167                 pass            # Defined in subclasses
1168
1169         def __evSOF(self):
1170                 self.setSeekState(self.SEEK_STATE_PLAY)
1171                 self.doSeek(0)
1172
1173 from Screens.PVRState import PVRState, TimeshiftState
1174
1175 class InfoBarPVRState:
1176         def __init__(self, screen=PVRState, force_show = False):
1177                 self.onPlayStateChanged.append(self.__playStateChanged)
1178                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1179                 self.onShow.append(self._mayShow)
1180                 self.onHide.append(self.pvrStateDialog.hide)
1181                 self.force_show = force_show
1182
1183         def _mayShow(self):
1184                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1185                         self.pvrStateDialog.show()
1186
1187         def __playStateChanged(self, state):
1188                 playstateString = state[3]
1189                 self.pvrStateDialog["state"].setText(playstateString)
1190
1191                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1192                 # also hide if service stopped and returning into MovieList
1193                 if not config.usage.show_infobar_on_skip.value and self.seekstate in (self.SEEK_STATE_PLAY, self.SEEK_STATE_STOP) and not self.force_show:
1194                         self.pvrStateDialog.hide()
1195                 else:
1196                         self._mayShow()
1197
1198 class InfoBarTimeshiftState(InfoBarPVRState):
1199         def __init__(self):
1200                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1201                 self.__hideTimer = eTimer()
1202                 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1203
1204         def _mayShow(self):
1205                 if self.execing and self.timeshift_enabled:
1206                         self.pvrStateDialog.show()
1207                         if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1208                                 self.__hideTimer.start(5*1000, True)
1209
1210         def __hideTimeshiftState(self):
1211                 self.pvrStateDialog.hide()
1212
1213 class InfoBarShowMovies:
1214
1215         # i don't really like this class.
1216         # it calls a not further specified "movie list" on up/down/movieList,
1217         # so this is not more than an action map
1218         def __init__(self):
1219                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1220                         {
1221                                 "movieList": (self.showMovies, _("movie list")),
1222                                 "up": (self.showMovies, _("movie list")),
1223                                 "down": (self.showMovies, _("movie list"))
1224                         })
1225
1226 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1227
1228 # Hrmf.
1229 #
1230 # Timeshift works the following way:
1231 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1232 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1233 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1234 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1235 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1236 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1237 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1238 #
1239
1240 # in other words:
1241 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1242 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1243 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1244 # - the user can now PVR around
1245 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1246 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1247 # after!
1248 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1249 # - if the user rewinds, or press pause, timeshift will be activated again
1250
1251 # note that a timeshift can be enabled ("recording") and
1252 # activated (currently time-shifting).
1253
1254 class InfoBarTimeshift:
1255         def __init__(self):
1256                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1257                         {
1258                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1259                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1260                         }, prio=1)
1261                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1262                         {
1263                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1264                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1265                         }, prio=-1) # priority over record
1266
1267                 self.timeshift_enabled = 0
1268                 self.timeshift_state = 0
1269                 self.ts_rewind_timer = eTimer()
1270                 self.ts_rewind_timer.callback.append(self.rewindService)
1271
1272                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1273                         {
1274                                 iPlayableService.evStart: self.__serviceStarted,
1275                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1276                         })
1277
1278         def getTimeshift(self):
1279                 service = self.session.nav.getCurrentService()
1280                 return service and service.timeshift()
1281
1282         def startTimeshift(self):
1283                 print "enable timeshift"
1284                 ts = self.getTimeshift()
1285                 if ts is None:
1286                         if harddiskmanager.HDDCount() and not harddiskmanager.HDDEnabledCount():
1287                                 self.session.open(MessageBox, _("Timeshift not possible!") + "\n" \
1288                                         + _("Please make sure to set up your storage devices with the storage management in menu -> setup -> system -> storage devices."), MessageBox.TYPE_ERROR)
1289                         elif harddiskmanager.HDDEnabledCount() and defaultStorageDevice() == "<undefined>":
1290                                 self.session.open(MessageBox, _("Timeshift not possible!") + "\n" \
1291                                         + _("Please make sure to set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_ERROR)
1292                         elif harddiskmanager.HDDEnabledCount() and defaultStorageDevice() != "<undefined>":
1293                                 part = harddiskmanager.getDefaultStorageDevicebyUUID(defaultStorageDevice())
1294                                 if part is None:
1295                                         self.session.open(MessageBox, _("Timeshift not possible!") + "\n" \
1296                                                 + _("Please verify if your default storage device is attached or set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_ERROR)
1297                         else:
1298                                 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1299                                 print "no ts interface"
1300                         return 0
1301
1302                 if self.timeshift_enabled:
1303                         print "hu, timeshift already enabled?"
1304                 else:
1305                         if not ts.startTimeshift():
1306                                 self.timeshift_enabled = 1
1307
1308                                 # we remove the "relative time" for now.
1309                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1310
1311                                 # PAUSE.
1312                                 #self.setSeekState(self.SEEK_STATE_PAUSE)
1313                                 self.activateTimeshiftEnd(False)
1314
1315                                 # enable the "TimeshiftEnableActions", which will override
1316                                 # the startTimeshift actions
1317                                 self.__seekableStatusChanged()
1318                         else:
1319                                 print "timeshift failed"
1320
1321         def stopTimeshift(self):
1322                 if not self.timeshift_enabled:
1323                         return 0
1324                 print "disable timeshift"
1325                 ts = self.getTimeshift()
1326                 if ts is None:
1327                         return 0
1328                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1329
1330         def stopTimeshiftConfirmed(self, confirmed):
1331                 if not confirmed:
1332                         return
1333
1334                 ts = self.getTimeshift()
1335                 if ts is None:
1336                         return
1337
1338                 ts.stopTimeshift()
1339                 self.timeshift_enabled = 0
1340
1341                 # disable actions
1342                 self.__seekableStatusChanged()
1343
1344         # activates timeshift, and seeks to (almost) the end
1345         def activateTimeshiftEnd(self, back = True):
1346                 ts = self.getTimeshift()
1347                 print "activateTimeshiftEnd"
1348
1349                 if ts is None:
1350                         return
1351
1352                 if ts.isTimeshiftActive():
1353                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1354                         self.pauseService()
1355                 else:
1356                         print "play, ..."
1357                         ts.activateTimeshift() # activate timeshift will automatically pause
1358                         self.setSeekState(self.SEEK_STATE_PAUSE)
1359
1360                 if back:
1361                         self.ts_rewind_timer.start(200, 1)
1362
1363         def rewindService(self):
1364                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1365
1366         # same as activateTimeshiftEnd, but pauses afterwards.
1367         def activateTimeshiftEndAndPause(self):
1368                 print "activateTimeshiftEndAndPause"
1369                 #state = self.seekstate
1370                 self.activateTimeshiftEnd(False)
1371
1372         def __seekableStatusChanged(self):
1373                 enabled = False
1374
1375 #               print "self.isSeekable", self.isSeekable()
1376 #               print "self.timeshift_enabled", self.timeshift_enabled
1377
1378                 # when this service is not seekable, but timeshift
1379                 # is enabled, this means we can activate
1380                 # the timeshift
1381                 if not self.isSeekable() and self.timeshift_enabled:
1382                         enabled = True
1383
1384 #               print "timeshift activate:", enabled
1385                 self["TimeshiftActivateActions"].setEnabled(enabled)
1386
1387         def __serviceStarted(self):
1388                 self.timeshift_enabled = False
1389                 self.__seekableStatusChanged()
1390
1391 from Screens.PiPSetup import PiPSetup
1392
1393 class InfoBarExtensions:
1394         EXTENSION_SINGLE = 0
1395         EXTENSION_LIST = 1
1396
1397         def __init__(self):
1398                 self.list = []
1399
1400                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1401                         {
1402                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1403                         }, 1) # lower priority
1404
1405         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1406                 self.list.append((type, extension, key))
1407
1408         def updateExtension(self, extension, key = None):
1409                 self.extensionsList.append(extension)
1410                 if key is not None:
1411                         if self.extensionKeys.has_key(key):
1412                                 key = None
1413
1414                 if key is None:
1415                         for x in self.availableKeys:
1416                                 if not self.extensionKeys.has_key(x):
1417                                         key = x
1418                                         break
1419
1420                 if key is not None:
1421                         self.extensionKeys[key] = len(self.extensionsList) - 1
1422
1423         def updateExtensions(self):
1424                 self.extensionsList = []
1425                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue", "text" ]
1426                 self.extensionKeys = {}
1427                 for x in self.list:
1428                         if x[0] == self.EXTENSION_SINGLE:
1429                                 self.updateExtension(x[1], x[2])
1430                         else:
1431                                 for y in x[1]():
1432                                         self.updateExtension(y[0], y[1])
1433
1434
1435         def showExtensionSelection(self):
1436                 self.updateExtensions()
1437                 extensionsList = self.extensionsList[:]
1438                 keys = []
1439                 list = []
1440                 for x in self.availableKeys:
1441                         if self.extensionKeys.has_key(x):
1442                                 entry = self.extensionKeys[x]
1443                                 extension = self.extensionsList[entry]
1444                                 if extension[2]():
1445                                         name = str(extension[0]())
1446                                         list.append((extension[0](), extension))
1447                                         keys.append(x)
1448                                         extensionsList.remove(extension)
1449                                 else:
1450                                         extensionsList.remove(extension)
1451                 list.extend([(x[0](), x) for x in extensionsList])
1452
1453                 keys += [""] * len(extensionsList)
1454                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1455
1456         def extensionCallback(self, answer):
1457                 if answer is not None:
1458                         answer[1][1]()
1459
1460 from Tools.BoundFunction import boundFunction
1461 import inspect
1462
1463 # depends on InfoBarExtensions
1464
1465 class InfoBarPlugins:
1466         def __init__(self):
1467                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1468
1469         def getPluginName(self, name):
1470                 return name
1471
1472         def getPluginList(self):
1473                 l = []
1474                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1475                         args = inspect.getargspec(p.__call__)[0]
1476                         if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1477                                 l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1478                 l.sort(key = lambda e: e[2]) # sort by name
1479                 return l
1480
1481         def runPlugin(self, plugin):
1482                 if isinstance(self, InfoBarChannelSelection):
1483                         plugin(session = self.session, servicelist = self.servicelist)
1484                 else:
1485                         plugin(session = self.session)
1486
1487 from Components.Task import job_manager
1488 class InfoBarJobman:
1489         def __init__(self):
1490                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1491
1492         def getJobList(self):
1493                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1494
1495         def getJobName(self, job):
1496                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1497
1498         def showJobView(self, job):
1499                 from Screens.TaskView import JobView
1500                 job_manager.in_background = False
1501                 self.session.openWithCallback(self.JobViewCB, JobView, job)
1502
1503         def JobViewCB(self, in_background):
1504                 job_manager.in_background = in_background
1505
1506 # depends on InfoBarExtensions
1507 class InfoBarPiP:
1508         def __init__(self):
1509                 try:
1510                         self.session.pipshown
1511                 except:
1512                         self.session.pipshown = False
1513                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1514                         if (self.allowPiP):
1515                                 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1516                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1517                                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1518                         else:
1519                                 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1520                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1521
1522         def pipShown(self):
1523                 return self.session.pipshown
1524
1525         def pipHandles0Action(self):
1526                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1527
1528         def getShowHideName(self):
1529                 if self.session.pipshown:
1530                         return _("Disable Picture in Picture")
1531                 else:
1532                         return _("Activate Picture in Picture")
1533
1534         def getSwapName(self):
1535                 return _("Swap Services")
1536
1537         def getMoveName(self):
1538                 return _("Move Picture in Picture")
1539
1540         def showPiP(self):
1541                 if self.session.pipshown:
1542                         print "pip currently shown.... pointer:", self.session.pip
1543                         del self.session.pip
1544                         print 'hasattr(self.session,"pip")', hasattr(self.session,"pip")
1545                         self.session.pipshown = False
1546                 else:
1547                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1548                         self.session.pip.show()
1549                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1550                         if self.session.pip.playService(newservice):
1551                                 self.session.pipshown = True
1552                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1553                         else:
1554                                 self.session.pipshown = False
1555                                 del self.session.pip
1556                         self.session.nav.playService(newservice)
1557
1558         def swapPiP(self):
1559                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1560                 if self.session.pip.servicePath:
1561                         servicepath = self.servicelist.getCurrentServicePath()
1562                         ref=servicepath[len(servicepath)-1]
1563                         pipref=self.session.pip.getCurrentService()
1564                         self.session.pip.playService(swapservice)
1565                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1566                         if pipref.toString() != ref.toString(): # is a subservice ?
1567                                 self.session.nav.stopService() # stop portal
1568                                 self.session.nav.playService(pipref) # start subservice
1569                         self.session.pip.servicePath=servicepath
1570
1571         def movePiP(self):
1572                 self.session.open(PiPSetup, pip = self.session.pip)
1573
1574         def pipDoHandle0Action(self):
1575                 use = config.usage.pip_zero_button.value
1576                 if "swap" == use:
1577                         self.swapPiP()
1578                 elif "swapstop" == use:
1579                         self.swapPiP()
1580                         self.showPiP()
1581                 elif "stop" == use:
1582                         self.showPiP()
1583
1584 from RecordTimer import parseEvent
1585
1586 class InfoBarInstantRecord:
1587         """Instant Record - handles the instantRecord action in order to
1588         start/stop instant records"""
1589         def __init__(self):
1590                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1591                         {
1592                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1593                         })
1594
1595                 self.session.nav.RecordTimer.on_state_change.append(self.timerentryOnStateChange)
1596                 self.recording = []
1597
1598         def timerentryOnStateChange(self, timer):
1599                 # timer recording has been started, append to self.recording list
1600                 if hasattr(self, "recording") and timer.isRunning():
1601                         if not timer in self.recording: # only if timer is not in list already
1602                                 self.recording.append(timer)
1603
1604         def stopCurrentRecording(self, entry = -1):
1605                 if entry is not None and entry != -1:
1606                         t = self.recording[entry]
1607                         if t.repeated:  # do not delete repeated timer, ask user what to do
1608                                 choicelist = (
1609                                         (_("Stop current event but not coming events"), "stoponlycurrent"),
1610                                         (_("Stop current event and disable coming events"), "stopall")
1611                                 )
1612                                 self.session.openWithCallback(boundFunction(self.runningRepeatedTimerCallback, t), ChoiceBox, title=_("Repeating event currently recording... What do you want to do?"), list = choicelist)
1613                         else:
1614                                 self.session.nav.RecordTimer.removeEntry(t)
1615                                 self.recording.remove(t)
1616
1617         def runningRepeatedTimerCallback(self, t, result):
1618                 if result is not None:
1619                         if result[1] == "stoponlycurrent":
1620                                 t.enable()
1621                                 t.processRepeated(findRunningEvent = False)
1622                                 self.session.nav.RecordTimer.doActivate(t)
1623                         elif result[1] == "stopall":
1624                                 t.disable()
1625                         self.session.nav.RecordTimer.timeChanged(t)
1626                         self.recording.remove(t)
1627
1628
1629         def startInstantRecording(self, limitEvent = False):
1630                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1631
1632                 # try to get event info
1633                 event = None
1634                 try:
1635                         service = self.session.nav.getCurrentService()
1636                         epg = eEPGCache.getInstance()
1637                         event = epg.lookupEventTime(serviceref, -1, 0)
1638                         if event is None:
1639                                 info = service.info()
1640                                 ev = info.getEvent(0)
1641                                 event = ev
1642                 except:
1643                         pass
1644
1645                 begin = int(time())
1646                 end = begin + 3600      # dummy
1647                 name = "instant record"
1648                 description = ""
1649                 eventid = None
1650
1651                 if event is not None:
1652                         curEvent = parseEvent(event)
1653                         name = curEvent[2]
1654                         description = curEvent[3]
1655                         eventid = curEvent[4]
1656                         if limitEvent:
1657                                 end = curEvent[1]
1658                 else:
1659                         if limitEvent:
1660                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1661
1662                 if isinstance(serviceref, eServiceReference):
1663                         serviceref = ServiceReference(serviceref)
1664
1665                 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1666                 recording.dontSave = True
1667
1668                 if event is None or limitEvent == False:
1669                         recording.autoincrease = True
1670                         recording.setAutoincreaseEnd()
1671
1672                 self.recording.append(recording)
1673                 simulTimerList = self.session.nav.RecordTimer.record(recording)
1674
1675                 if simulTimerList is not None:
1676                         if len(simulTimerList) > 1: # with other recording
1677                                 name = simulTimerList[1].name
1678                                 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1679                                 print "[TIMER] conflicts with", name_date
1680                                 recording.autoincrease = True   # start with max available length, then increment
1681                                 if recording.setAutoincreaseEnd():
1682                                         self.session.nav.RecordTimer.record(recording)
1683                                         self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1684                                 else:
1685                                         self.recording.remove(recording)
1686                                         self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1687                         else:
1688                                 self.recording.remove(recording)
1689                                 self.session.open(MessageBox, _("Couldn't record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
1690                         recording.autoincrease = False
1691
1692         def isInstantRecordRunning(self):
1693                 print "self.recording:", self.recording
1694                 if self.recording:
1695                         for x in self.recording:
1696                                 if x.isRunning():
1697                                         return True
1698                 return False
1699
1700         def recordQuestionCallback(self, answer):
1701                 print "pre:\n", self.recording
1702
1703                 if answer is None or answer[1] == "no":
1704                         return
1705                 list = []
1706                 recording = self.recording[:]
1707                 for x in recording:
1708                         if not x in self.session.nav.RecordTimer.timer_list or not x.isRunning(): # check for isRunning because of repeated timer (there are still in the timerlist!)
1709                                 self.recording.remove(x)
1710                         elif x.isRunning():
1711                                 list.append((x, False))
1712
1713                 if answer[1] == "changeduration":
1714                         self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1715                 elif answer[1] == "changeendtime":
1716                         self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1717                 elif answer[1] == "stop":
1718                         self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1719                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1720                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1721                         if answer[1] == "manualduration":
1722                                 self.changeDuration(len(self.recording)-1)
1723                         elif answer[1] == "manualendtime":
1724                                 self.setEndtime(len(self.recording)-1)
1725                 print "after:\n", self.recording
1726
1727         def setEndtime(self, entry):
1728                 if entry is not None and entry >= 0:
1729                         self.selectedEntry = entry
1730                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1731                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1732                         dlg.setTitle(_("Please change recording endtime"))
1733
1734         def TimeDateInputClosed(self, ret):
1735                 if len(ret) > 1:
1736                         if ret[0]:
1737                                 localendtime = localtime(ret[1])
1738                                 print "stopping recording at", strftime("%c", localendtime)
1739                                 if self.recording[self.selectedEntry].end != ret[1]:
1740                                         self.recording[self.selectedEntry].autoincrease = False
1741                                 self.recording[self.selectedEntry].end = ret[1]
1742                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1743
1744         def changeDuration(self, entry):
1745                 if entry is not None and entry >= 0:
1746                         self.selectedEntry = entry
1747                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1748
1749         def inputCallback(self, value):
1750                 if value is not None:
1751                         print "stopping recording after", int(value), "minutes."
1752                         entry = self.recording[self.selectedEntry]
1753                         if int(value) != 0:
1754                                 entry.autoincrease = False
1755                         entry.end = int(time()) + 60 * int(value)
1756                         self.session.nav.RecordTimer.timeChanged(entry)
1757
1758         def instantRecord(self):
1759                 dir = preferredInstantRecordPath()
1760                 if not dir or not fileExists(dir, 'w'):
1761                         dir = defaultMoviePath()
1762                 if not harddiskmanager.inside_mountpoint(dir):
1763                         if harddiskmanager.HDDCount() and not harddiskmanager.HDDEnabledCount():
1764                                 self.session.open(MessageBox, _("Unconfigured storage devices found!") + "\n" \
1765                                         + _("Please make sure to set up your storage devices with the storage management in menu -> setup -> system -> storage devices."), MessageBox.TYPE_ERROR)
1766                                 return
1767                         elif harddiskmanager.HDDEnabledCount() and defaultStorageDevice() == "<undefined>":
1768                                 self.session.open(MessageBox, _("No default storage device found!") + "\n" \
1769                                         + _("Please make sure to set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_ERROR)
1770                                 return
1771                         elif harddiskmanager.HDDEnabledCount() and defaultStorageDevice() != "<undefined>":
1772                                 part = harddiskmanager.getDefaultStorageDevicebyUUID(defaultStorageDevice())
1773                                 if part is None:
1774                                         self.session.open(MessageBox, _("Default storage device is not available!") + "\n" \
1775                                                 + _("Please verify if your default storage device is attached or set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_ERROR)
1776                                         return
1777                         else:
1778                                 # XXX: this message is a little odd as we might be recording to a remote device
1779                                 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1780                                 return
1781
1782                 if self.isInstantRecordRunning():
1783                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1784                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1785                                 list=((_("stop recording"), "stop"), \
1786                                 (_("add recording (stop after current event)"), "event"), \
1787                                 (_("add recording (indefinitely)"), "indefinitely"), \
1788                                 (_("add recording (enter recording duration)"), "manualduration"), \
1789                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1790                                 (_("change recording (duration)"), "changeduration"), \
1791                                 (_("change recording (endtime)"), "changeendtime"), \
1792                                 (_("do nothing"), "no")))
1793                 else:
1794                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1795                                 title=_("Start recording?"), \
1796                                 list=((_("add recording (stop after current event)"), "event"), \
1797                                 (_("add recording (indefinitely)"), "indefinitely"), \
1798                                 (_("add recording (enter recording duration)"), "manualduration"), \
1799                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1800                                 (_("don't record"), "no")))
1801
1802 class InfoBarAudioSelection:
1803         def __init__(self):
1804                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1805                         {
1806                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1807                         })
1808
1809         def audioSelection(self):
1810                 from Screens.AudioSelection import AudioSelection
1811                 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self.session.infobar or self)
1812
1813         def audioSelected(self, ret=None):
1814                 print "[infobar::audioSelected]", ret
1815
1816 class InfoBarSubserviceSelection:
1817         def __init__(self):
1818                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1819                         {
1820                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1821                         })
1822
1823                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1824                         {
1825                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1826                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1827                         }, -1)
1828                 self["SubserviceQuickzapAction"].setEnabled(False)
1829
1830                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1831                         {
1832                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1833                         })
1834
1835                 self.bsel = None
1836
1837         def checkSubservicesAvail(self):
1838                 service = self.session.nav.getCurrentService()
1839                 subservices = service and service.subServices()
1840                 if not subservices or subservices.getNumberOfSubservices() == 0:
1841                         self["SubserviceQuickzapAction"].setEnabled(False)
1842
1843         def nextSubservice(self):
1844                 self.changeSubservice(+1)
1845
1846         def prevSubservice(self):
1847                 self.changeSubservice(-1)
1848
1849         def changeSubservice(self, direction):
1850                 service = self.session.nav.getCurrentService()
1851                 subservices = service and service.subServices()
1852                 n = subservices and subservices.getNumberOfSubservices()
1853                 if n and n > 0:
1854                         selection = -1
1855                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1856                         idx = 0
1857                         while idx < n:
1858                                 if subservices.getSubservice(idx).toString() == ref.toString():
1859                                         selection = idx
1860                                         break
1861                                 idx += 1
1862                         if selection != -1:
1863                                 selection += direction
1864                                 if selection >= n:
1865                                         selection=0
1866                                 elif selection < 0:
1867                                         selection=n-1
1868                                 newservice = subservices.getSubservice(selection)
1869                                 if newservice.valid():
1870                                         del subservices
1871                                         del service
1872                                         self.session.nav.playService(newservice, False)
1873
1874         def subserviceSelection(self):
1875                 service = self.session.nav.getCurrentService()
1876                 subservices = service and service.subServices()
1877                 self.bouquets = self.servicelist.getBouquetList()
1878                 n = subservices and subservices.getNumberOfSubservices()
1879                 selection = 0
1880                 if n and n > 0:
1881                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1882                         tlist = []
1883                         idx = 0
1884                         while idx < n:
1885                                 i = subservices.getSubservice(idx)
1886                                 if i.toString() == ref.toString():
1887                                         selection = idx
1888                                 tlist.append((i.getName(), i))
1889                                 idx += 1
1890
1891                         if self.bouquets and len(self.bouquets):
1892                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1893                                 if config.usage.multibouquet.value:
1894                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1895                                 else:
1896                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1897                                 selection += 3
1898                         else:
1899                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1900                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1901                                 selection += 2
1902
1903                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1904
1905         def subserviceSelected(self, service):
1906                 del self.bouquets
1907                 if not service is None:
1908                         if isinstance(service[1], str):
1909                                 if service[1] == "quickzap":
1910                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1911                                         self.session.open(SubservicesQuickzap, service[2])
1912                         else:
1913                                 self["SubserviceQuickzapAction"].setEnabled(True)
1914                                 self.session.nav.playService(service[1], False)
1915
1916         def addSubserviceToBouquetCallback(self, service):
1917                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1918                         self.selectedSubservice = service
1919                         if self.bouquets is None:
1920                                 cnt = 0
1921                         else:
1922                                 cnt = len(self.bouquets)
1923                         if cnt > 1: # show bouquet list
1924                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1925                         elif cnt == 1: # add to only one existing bouquet
1926                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1927                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1928
1929         def bouquetSelClosed(self, confirmed):
1930                 self.bsel = None
1931                 del self.selectedSubservice
1932                 if confirmed:
1933                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1934
1935         def addSubserviceToBouquet(self, dest):
1936                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1937                 if self.bsel:
1938                         self.bsel.close(True)
1939                 else:
1940                         del self.selectedSubservice
1941
1942 class InfoBarAdditionalInfo:
1943         def __init__(self):
1944                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1945                 self["TimeshiftPossible"] = self["RecordingPossible"]
1946                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value in (0, 2)))
1947                 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1948                 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1949                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1950                 self["PendingNotification"] = Boolean(fixed=0)
1951
1952 class InfoBarNotifications:
1953         def __init__(self):
1954                 self.onExecBegin.append(self.checkNotifications)
1955                 Notifications.notificationQueue.addedCB.append(self.checkNotificationsIfExecing)
1956                 self.onClose.append(self.__removeNotification)
1957                 if isinstance(self, InfoBarExtensions):
1958                         self.addExtension((self.getEntryText, self.showNotificationQueueViewer, lambda: True), key = "text")
1959
1960         def __removeNotification(self):
1961                 Notifications.notificationQueue.addedCB.remove(self.checkNotificationsIfExecing)
1962
1963         def checkNotificationsIfExecing(self):
1964                 if self.execing:
1965                         self.checkNotifications()
1966
1967         def checkNotifications(self):
1968                 Notifications.notificationQueue.popNotification(self)
1969
1970         def getEntryText(self):
1971                 numPending = len(Notifications.notificationQueue.getPending())
1972                 text = _("Notification Queue")
1973                 if numPending == 1:
1974                         text += " (1 new event)"
1975                 elif numPending > 1:
1976                         text += " (%i new events)" % numPending
1977                 return text
1978
1979         def showNotificationQueueViewer(self):
1980                 from Screens.NotificationQueueViewer import NotificationQueueViewer
1981                 self.session.open(NotificationQueueViewer)
1982
1983 class InfoBarServiceNotifications:
1984         def __init__(self):
1985                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1986                         {
1987                                 iPlayableService.evEnd: self.serviceHasEnded
1988                         })
1989
1990         def serviceHasEnded(self):
1991                 print "service end!"
1992                 try:
1993                         self.setSeekState(self.SEEK_STATE_STOP, True)
1994                 except:
1995                         pass
1996
1997 class InfoBarCueSheetSupport:
1998         CUT_TYPE_IN = 0
1999         CUT_TYPE_OUT = 1
2000         CUT_TYPE_MARK = 2
2001         CUT_TYPE_LAST = 3
2002
2003         ENABLE_RESUME_SUPPORT = False
2004
2005         def __init__(self, actionmap = "InfobarCueSheetActions"):
2006                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
2007                         {
2008                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
2009                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
2010                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
2011                         }, prio=1)
2012
2013                 self.cut_list = [ ]
2014                 self.is_closing = False
2015                 self.length = [0,0]
2016                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2017                         {
2018                                 iPlayableService.evStart: self.__serviceStarted,
2019                         })
2020
2021         def __serviceStarted(self):
2022                 if self.is_closing:
2023                         return
2024                 print "new service started! trying to download cuts!"
2025                 self.downloadCuesheet()
2026
2027                 if self.ENABLE_RESUME_SUPPORT:
2028                         last = None
2029
2030                         for (pts, what) in self.cut_list:
2031                                 if what == self.CUT_TYPE_LAST:
2032                                         last = pts
2033
2034                         if last is not None:
2035                                 self.resume_point = last
2036
2037                                 if (self.length[1] > 0) and abs(self.length[1] - last) < 4*90000: # if last playpos is within 4 seconds span to the length of this recording, assume it has been watched all the way to the end and don't resume
2038                                         return
2039                                 l = last / 90000
2040                                 if config.usage.on_movie_start.value == "ask":
2041                                         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, domain = "InfoBar")
2042                                 elif config.usage.on_movie_start.value == "resume":
2043 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2044 # TRANSLATORS: at the start of a movie, when the user has selected
2045 # TRANSLATORS: "Resume from last position" as start behavior.
2046 # TRANSLATORS: The purpose is to notify the user that the movie starts
2047 # TRANSLATORS: in the middle somewhere and not from the beginning.
2048 # TRANSLATORS: (Some translators seem to have interpreted it as a
2049 # TRANSLATORS: question or a choice, but it is a statement.)
2050                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO, domain = "InfoBar")
2051
2052         def playLastCB(self, answer):
2053                 if answer == True:
2054                         self.doSeek(self.resume_point)
2055                 self.hideAfterResume()
2056
2057         def hideAfterResume(self):
2058                 if isinstance(self, InfoBarShowHide):
2059                         self.hide()
2060
2061         def __getSeekable(self):
2062                 service = self.session.nav.getCurrentService()
2063                 if service is None:
2064                         return None
2065                 return service.seek()
2066
2067         def cueGetCurrentPosition(self):
2068                 seek = self.__getSeekable()
2069                 if seek is None:
2070                         return None
2071                 r = seek.getPlayPosition()
2072                 if r[0]:
2073                         return None
2074                 return long(r[1])
2075
2076         def cueGetEndCutPosition(self):
2077                 ret = False
2078                 isin = True
2079                 for cp in self.cut_list:
2080                         if cp[1] == self.CUT_TYPE_OUT:
2081                                 if isin:
2082                                         isin = False
2083                                         ret = cp[0]
2084                         elif cp[1] == self.CUT_TYPE_IN:
2085                                 isin = True
2086                 return ret
2087
2088         def jumpPreviousNextMark(self, cmp, start=False):
2089                 current_pos = self.cueGetCurrentPosition()
2090                 if current_pos is None:
2091                         return False
2092                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2093                 if mark is not None:
2094                         pts = mark[0]
2095                 else:
2096                         return False
2097                 self.doSeek(pts)
2098                 return True
2099
2100         def jumpPreviousMark(self):
2101                 # we add 5 seconds, so if the play position is <5s after
2102                 # the mark, the mark before will be used
2103                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2104
2105         def jumpNextMark(self):
2106                 if not self.jumpPreviousNextMark(lambda x: x-90000):
2107                         self.doSeek(-1)
2108
2109         def getNearestCutPoint(self, pts, cmp=abs, start=False):
2110                 # can be optimized
2111                 beforecut = True
2112                 nearest = None
2113                 bestdiff = -1
2114                 instate = True
2115                 if start:
2116                         bestdiff = cmp(0 - pts)
2117                         if bestdiff >= 0:
2118                                 nearest = [0, False]
2119                 for cp in self.cut_list:
2120                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2121                                 beforecut = False
2122                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
2123                                         diff = cmp(cp[0] - pts)
2124                                         if start and diff >= 0:
2125                                                 nearest = cp
2126                                                 bestdiff = diff
2127                                         else:
2128                                                 nearest = None
2129                                                 bestdiff = -1
2130                         if cp[1] == self.CUT_TYPE_IN:
2131                                 instate = True
2132                         elif cp[1] == self.CUT_TYPE_OUT:
2133                                 instate = False
2134                         elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2135                                 diff = cmp(cp[0] - pts)
2136                                 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2137                                         nearest = cp
2138                                         bestdiff = diff
2139                 return nearest
2140
2141         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2142                 current_pos = self.cueGetCurrentPosition()
2143                 if current_pos is None:
2144                         print "not seekable"
2145                         return
2146
2147                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2148
2149                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2150                         if onlyreturn:
2151                                 return nearest_cutpoint
2152                         if not onlyadd:
2153                                 self.removeMark(nearest_cutpoint)
2154                 elif not onlyremove and not onlyreturn:
2155                         self.addMark((current_pos, self.CUT_TYPE_MARK))
2156
2157                 if onlyreturn:
2158                         return None
2159
2160         def addMark(self, point):
2161                 insort(self.cut_list, point)
2162                 self.uploadCuesheet()
2163                 self.showAfterCuesheetOperation()
2164
2165         def removeMark(self, point):
2166                 self.cut_list.remove(point)
2167                 self.uploadCuesheet()
2168                 self.showAfterCuesheetOperation()
2169
2170         def showAfterCuesheetOperation(self):
2171                 if isinstance(self, InfoBarShowHide):
2172                         self.doShow()
2173
2174         def __getCuesheet(self):
2175                 service = self.session.nav.getCurrentService()
2176                 if service is None:
2177                         return None
2178                 if self.__getSeekable():
2179                         self.length = self.__getSeekable().getLength()
2180                 return service.cueSheet()
2181
2182         def uploadCuesheet(self):
2183                 cue = self.__getCuesheet()
2184
2185                 if cue is None:
2186                         print "upload failed, no cuesheet interface"
2187                         return
2188                 cue.setCutList(self.cut_list)
2189
2190         def downloadCuesheet(self):
2191                 cue = self.__getCuesheet()
2192
2193                 if cue is None:
2194                         print "download failed, no cuesheet interface"
2195                         self.cut_list = [ ]
2196                 else:
2197                         self.cut_list = cue.getCutList()
2198
2199 class InfoBarSummary(Screen):
2200         skin = """
2201         <screen position="0,0" size="132,64">
2202                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2203                         <convert type="ClockToText">WithSeconds</convert>
2204                 </widget>
2205                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2206                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2207                         <convert type="ConditionalShowHide">Blink</convert>
2208                 </widget>
2209                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2210                         <convert type="ServiceName">Name</convert>
2211                 </widget>
2212                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2213                         <convert type="EventTime">Progress</convert>
2214                 </widget>
2215         </screen>"""
2216
2217 # for picon:  (path="piconlcd" will use LCD picons)
2218 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2219 #                       <convert type="ServiceName">Reference</convert>
2220 #               </widget>
2221
2222 class InfoBarSummarySupport:
2223         def __init__(self):
2224                 pass
2225
2226         def createSummary(self):
2227                 return InfoBarSummary
2228
2229 class InfoBarMoviePlayerSummary(Screen):
2230         skin = """
2231         <screen position="0,0" size="132,64">
2232                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2233                         <convert type="ClockToText">WithSeconds</convert>
2234                 </widget>
2235                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2236                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2237                         <convert type="ConditionalShowHide">Blink</convert>
2238                 </widget>
2239                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2240                         <convert type="ServiceName">Name</convert>
2241                 </widget>
2242                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2243                         <convert type="ServicePosition">Position</convert>
2244                 </widget>
2245         </screen>"""
2246
2247 class InfoBarMoviePlayerSummarySupport:
2248         def __init__(self):
2249                 pass
2250
2251         def createSummary(self):
2252                 return InfoBarMoviePlayerSummary
2253
2254 class InfoBarTeletextPlugin:
2255         def __init__(self):
2256                 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2257                         {
2258                                 "startTeletext": (self.startTeletext, _("View teletext..."))
2259                         })
2260
2261         def startTeletext(self):
2262                 teletext_plugins = plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT)
2263                 l = len(teletext_plugins)
2264                 if l == 1:
2265                         teletext_plugins[0](session=self.session, service=self.session.nav.getCurrentService())
2266                 elif l > 1:
2267                         list = []
2268                         for p in teletext_plugins:
2269                                 list.append( (p.name, p) )
2270                         self.session.openWithCallback(self.onTextSelected, ChoiceBox, title=_("Please select a Text application"), list = list)
2271
2272         def onTextSelected(self, p):
2273                 p = p and p[1]
2274                 if p is not None:
2275                         p(session=self.session, service=self.session.nav.getCurrentService())
2276
2277 class InfobarHbbtvPlugin:
2278         def __init__(self):
2279                 if not self["ShowRecordOnRed"].boolean:
2280                         self["HbbtvActions"] = HelpableActionMap(self, "InfobarHbbtvActions",
2281                                 {
2282                                         "hbbtvAutostart" : (self.startHbbtv, _("Start HbbTV..."))
2283                                 }
2284                         )
2285                         self["HbbtvApplication"] = HbbtvApplication()
2286                 else:
2287                         self["HbbtvApplication"] = Boolean(fixed=0)
2288                         self["HbbtvApplication"].name = "" #is this a hack?
2289
2290         def startHbbtv(self):
2291                 hbbtv_plugin = None
2292                 for p in plugins.getPlugins(PluginDescriptor.WHERE_HBBTV):
2293                         hbbtv_plugin = p
2294                 if hbbtv_plugin is not None:
2295                         hbbtv_plugin(session=self.session)
2296
2297 class InfoBarSubtitleSupport(object):
2298         def __init__(self):
2299                 object.__init__(self)
2300                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2301                 self.__subtitles_enabled = False
2302
2303                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2304                         {
2305                                 iPlayableService.evEnd: self.__serviceStopped,
2306                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2307                         })
2308                 self.cached_subtitle_checked = False
2309                 self.__selected_subtitle = None
2310
2311         def __serviceStopped(self):
2312                 self.cached_subtitle_checked = False
2313                 if self.__subtitles_enabled:
2314                         self.subtitle_window.hide()
2315                         self.__subtitles_enabled = False
2316                         self.__selected_subtitle = None
2317
2318         def __updatedInfo(self):
2319                 if not self.cached_subtitle_checked:
2320                         self.cached_subtitle_checked = True
2321                         subtitle = self.getCurrentServiceSubtitle()
2322                         self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2323                         if self.__selected_subtitle:
2324                                 self.setSubtitlesEnable(True)
2325
2326         def getCurrentServiceSubtitle(self):
2327                 service = self.session.nav.getCurrentService()
2328                 return service and service.subtitle()
2329
2330         def setSubtitlesEnable(self, enable=True):
2331                 subtitle = self.getCurrentServiceSubtitle()
2332                 if enable:
2333                         if self.__selected_subtitle:
2334                                 if subtitle and not self.__subtitles_enabled:
2335                                         subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2336                                         self.subtitle_window.show()
2337                                         self.__subtitles_enabled = True
2338                 else:
2339                         if subtitle:
2340                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2341                         self.__selected_subtitle = False
2342                         self.__subtitles_enabled = False
2343                         self.subtitle_window.hide()
2344
2345         def setSelectedSubtitle(self, subtitle):
2346                 self.__selected_subtitle = subtitle
2347
2348         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2349         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2350
2351 class InfoBarServiceErrorPopupSupport:
2352         def __init__(self):
2353                 Notifications.notificationQueue.registerDomain("ZapError", _("ZapError"), Notifications.ICON_DEFAULT)
2354                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2355                         {
2356                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2357                                 iPlayableService.evStart: self.__serviceStarted
2358                         })
2359                 self.__serviceStarted()
2360
2361         def __serviceStarted(self):
2362                 self.last_error = None
2363                 Notifications.RemovePopup(id = "ZapError")
2364
2365         def __tuneFailed(self):
2366                 service = self.session.nav.getCurrentService()
2367                 info = service and service.info()
2368                 error = info and info.getInfo(iServiceInformation.sDVBState)
2369
2370                 if error == self.last_error:
2371                         error = None
2372                 else:
2373                         self.last_error = error
2374
2375                 error = {
2376                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2377                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2378                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2379                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2380                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2381                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2382                         eDVBServicePMTHandler.eventTuned: None,
2383                         eDVBServicePMTHandler.eventSOF: None,
2384                         eDVBServicePMTHandler.eventEOF: None,
2385                         eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2386                 }.get(error) #this returns None when the key not exist in the dict
2387
2388                 if error is not None:
2389                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError", domain = "ZapError")
2390                 else:
2391                         Notifications.RemovePopup(id = "ZapError")