do not let the user leave the parental control setup when setup protection
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector
2
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.BlinkingPixmap import BlinkingPixmapConditional
6 from Components.Harddisk import harddiskmanager
7 from Components.Input import Input
8 from Components.Label import *
9 from Components.Pixmap import Pixmap, PixmapConditional
10 from Components.PluginComponent import plugins
11 from Components.ProgressBar import *
12 from Components.ServiceEventTracker import ServiceEventTracker
13 from Components.Sources.CurrentService import CurrentService
14 from Components.Sources.EventInfo import EventInfo
15 from Components.Sources.RadioText import RadioText
16 from Components.Sources.FrontendStatus import FrontendStatus
17 from Components.Sources.Boolean import Boolean
18 from Components.Sources.Clock import Clock
19 from Components.TimerList import TimerEntryComponent
20 from Components.config import config, ConfigBoolean
21
22 from EpgSelection import EPGSelection
23 from Plugins.Plugin import PluginDescriptor
24
25 from Screen import Screen
26 from Screens.ChoiceBox import ChoiceBox
27 from Screens.Dish import Dish
28 from Screens.EventView import EventViewEPGSelect, EventViewSimple
29 from Screens.InputBox import InputBox
30 from Screens.MessageBox import MessageBox
31 from Screens.MinuteInput import MinuteInput
32 from Screens.TimerSelection import TimerSelection
33 from Screens.PictureInPicture import PictureInPicture
34 from Screens.SubtitleDisplay import SubtitleDisplay
35 from Screens.SleepTimerEdit import SleepTimerEdit
36 from ServiceReference import ServiceReference
37
38 from Tools import Notifications
39 from Tools.Directories import SCOPE_HDD, resolveFilename
40
41 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
42         iPlayableService, eServiceReference, eDVBResourceManager, iFrontendInformation, eEPGCache
43
44 from time import time
45 from os import stat as os_stat
46 from bisect import insort
47
48 # hack alert!
49 from Menu import MainMenu, mdom
50
51 class InfoBarDish:
52         def __init__(self):
53                 self.dishDialog = self.session.instantiateDialog(Dish)
54                 self.onLayoutFinish.append(self.dishDialog.show)
55
56 class InfoBarShowHide:
57         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
58         fancy animations. """
59         STATE_HIDDEN = 0
60         STATE_HIDING = 1
61         STATE_SHOWING = 2
62         STATE_SHOWN = 3
63         
64         def __init__(self):
65                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
66                         {
67                                 "toggleShow": self.toggleShow,
68                                 "hide": self.hide,
69                         })
70
71                 self.__state = self.STATE_SHOWN
72                 self.__locked = 0
73                 
74                 self.onExecBegin.append(self.show)
75                 
76                 self.hideTimer = eTimer()
77                 self.hideTimer.timeout.get().append(self.doTimerHide)
78                 self.hideTimer.start(5000, True)
79                 
80                 self.onShow.append(self.__onShow)
81                 self.onHide.append(self.__onHide)
82
83         def __onShow(self):
84                 self.__state = self.STATE_SHOWN
85                 self.startHideTimer()
86         
87         def startHideTimer(self):
88                 if self.__state == self.STATE_SHOWN and not self.__locked:
89                         idx = config.usage.infobar_timeout.index
90                         if idx:
91                                 self.hideTimer.start(idx*1000, True)
92
93         def __onHide(self):
94                 self.__state = self.STATE_HIDDEN
95
96         def doShow(self):
97                 self.show()
98                 self.startHideTimer()
99
100         def doTimerHide(self):
101                 self.hideTimer.stop()
102                 if self.__state == self.STATE_SHOWN:
103                         self.hide()
104
105         def toggleShow(self):
106                 if self.__state == self.STATE_SHOWN:
107                         self.hide()
108                         self.hideTimer.stop()
109                 elif self.__state == self.STATE_HIDDEN:
110                         self.show()
111
112         def lockShow(self):
113                 self.__locked = self.__locked + 1
114                 if self.execing:
115                         self.show()
116                         self.hideTimer.stop()
117         
118         def unlockShow(self):
119                 self.__locked = self.__locked - 1
120                 if self.execing:
121                         self.startHideTimer()
122
123 #       def startShow(self):
124 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
125 #               self.__state = self.STATE_SHOWN
126 #       
127 #       def startHide(self):
128 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
129 #               self.__state = self.STATE_HIDDEN
130
131 class NumberZap(Screen):
132         def quit(self):
133                 self.Timer.stop()
134                 self.close(0)
135
136         def keyOK(self):
137                 self.Timer.stop()
138                 self.close(int(self["number"].getText()))
139
140         def keyNumberGlobal(self, number):
141                 self.Timer.start(3000, True)            #reset timer
142                 self.field = self.field + str(number)
143                 self["number"].setText(self.field)
144                 if len(self.field) >= 4:
145                         self.keyOK()
146
147         def __init__(self, session, number):
148                 Screen.__init__(self, session)
149                 self.field = str(number)
150
151                 self["channel"] = Label(_("Channel:"))
152
153                 self["number"] = Label(self.field)
154
155                 self["actions"] = NumberActionMap( [ "SetupActions" ], 
156                         {
157                                 "cancel": self.quit,
158                                 "ok": self.keyOK,
159                                 "1": self.keyNumberGlobal,
160                                 "2": self.keyNumberGlobal,
161                                 "3": self.keyNumberGlobal,
162                                 "4": self.keyNumberGlobal,
163                                 "5": self.keyNumberGlobal,
164                                 "6": self.keyNumberGlobal,
165                                 "7": self.keyNumberGlobal,
166                                 "8": self.keyNumberGlobal,
167                                 "9": self.keyNumberGlobal,
168                                 "0": self.keyNumberGlobal
169                         })
170
171                 self.Timer = eTimer()
172                 self.Timer.timeout.get().append(self.keyOK)
173                 self.Timer.start(3000, True)
174
175 class InfoBarNumberZap:
176         """ Handles an initial number for NumberZapping """
177         def __init__(self):
178                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
179                         {
180                                 "1": self.keyNumberGlobal,
181                                 "2": self.keyNumberGlobal,
182                                 "3": self.keyNumberGlobal,
183                                 "4": self.keyNumberGlobal,
184                                 "5": self.keyNumberGlobal,
185                                 "6": self.keyNumberGlobal,
186                                 "7": self.keyNumberGlobal,
187                                 "8": self.keyNumberGlobal,
188                                 "9": self.keyNumberGlobal,
189                                 "0": self.keyNumberGlobal,
190                         })
191
192         def keyNumberGlobal(self, number):
193 #               print "You pressed number " + str(number)
194                 if number == 0:
195                         self.servicelist.recallPrevService()
196                         if config.usage.show_infobar_on_zap.value:
197                                 self.doShow()
198                 else:
199                         self.session.openWithCallback(self.numberEntered, NumberZap, number)
200
201         def numberEntered(self, retval):
202 #               print self.servicelist
203                 if retval > 0:
204                         self.zapToNumber(retval)
205
206         def searchNumberHelper(self, serviceHandler, num, bouquet):
207                 servicelist = serviceHandler.list(bouquet)
208                 if not servicelist is None:
209                         while num:
210                                 serviceIterator = servicelist.getNext()
211                                 if not serviceIterator.valid(): #check end of list
212                                         break
213                                 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
214                                 if playable:
215                                         num -= 1;
216                         if not num: #found service with searched number ?
217                                 return serviceIterator, 0
218                 return None, num
219
220         def zapToNumber(self, number):
221                 bouquet = self.servicelist.bouquet_root
222                 service = None
223                 serviceHandler = eServiceCenter.getInstance()
224                 if not config.usage.multibouquet.value:
225                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
226                 else:
227                         bouquetlist = serviceHandler.list(bouquet)
228                         if not bouquetlist is None:
229                                 while number:
230                                         bouquet = bouquetlist.getNext()
231                                         if not bouquet.valid(): #check end of list
232                                                 break
233                                         if bouquet.flags & eServiceReference.isDirectory:
234                                                 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
235                 if not service is None:
236                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
237                                 self.servicelist.clearPath()
238                                 if self.servicelist.bouquet_root != bouquet:
239                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
240                                 self.servicelist.enterPath(bouquet)
241                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
242                         self.servicelist.zap()
243
244 config.misc.initialchannelselection = ConfigBoolean(default = True)
245
246 class InfoBarChannelSelection:
247         """ ChannelSelection - handles the channelSelection dialog and the initial 
248         channelChange actions which open the channelSelection dialog """
249         def __init__(self):
250                 #instantiate forever
251                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
252                 
253                 if config.misc.initialchannelselection.value:
254                         self.onShown.append(self.firstRun)
255
256                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
257                         {
258                                 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
259                                 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
260                                 "zapUp": (self.zapUp, _("previous channel")),
261                                 "zapDown": (self.zapDown, _("next channel")),
262                                 "historyBack": (self.historyBack, _("previous channel in history")),
263                                 "historyNext": (self.historyNext, _("next channel in history")),
264                                 "openServiceList": (self.openServiceList, _("open servicelist")),
265                         })
266
267         def showTvChannelList(self, zap=False):
268                 self.servicelist.setModeTv()
269                 if zap:
270                         self.servicelist.zap()
271                 self.session.execDialog(self.servicelist)
272
273         def showRadioChannelList(self, zap=False):
274                 self.servicelist.setModeRadio()
275                 if zap:
276                         self.servicelist.zap()
277                 self.session.execDialog(self.servicelist)
278
279         def firstRun(self):
280                 self.onShown.remove(self.firstRun)
281                 config.misc.initialchannelselection.value = False
282                 config.misc.initialchannelselection.save()
283                 self.switchChannelDown()
284
285         def historyBack(self):
286                 self.servicelist.historyBack()
287
288         def historyNext(self):
289                 self.servicelist.historyNext()
290
291         def switchChannelUp(self):
292                 self.servicelist.moveUp()
293                 self.session.execDialog(self.servicelist)
294
295         def switchChannelDown(self):
296                 self.servicelist.moveDown()
297                 self.session.execDialog(self.servicelist)
298         
299         def openServiceList(self):
300                 self.session.execDialog(self.servicelist)
301
302         def zapUp(self):
303                 if self.servicelist.inBouquet():
304                         prev = self.servicelist.getCurrentSelection()
305                         if prev:
306                                 prev = prev.toString()
307                                 while True:
308                                         if config.usage.quickzap_bouquet_change.value:
309                                                 if self.servicelist.atBegin():
310                                                         self.servicelist.prevBouquet()
311                                         self.servicelist.moveUp()
312                                         cur = self.servicelist.getCurrentSelection()
313                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
314                                                 break
315                 else:
316                         self.servicelist.moveUp()
317                 self.servicelist.zap()
318                 if config.usage.show_infobar_on_zap.value:
319                         self.doShow()
320
321         def zapDown(self):
322                 if self.servicelist.inBouquet():
323                         prev = self.servicelist.getCurrentSelection()
324                         if prev:
325                                 prev = prev.toString()
326                                 while True:
327                                         if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
328                                                 self.servicelist.nextBouquet()
329                                         else:
330                                                 self.servicelist.moveDown()
331                                         cur = self.servicelist.getCurrentSelection()
332                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
333                                                 break
334                 else:
335                         self.servicelist.moveDown()
336                 self.servicelist.zap()
337                 if config.usage.show_infobar_on_zap.value:
338                         self.doShow()
339
340 class InfoBarMenu:
341         """ Handles a menu action, to open the (main) menu """
342         def __init__(self):
343                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions", 
344                         {
345                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
346                         })
347                 self.session.infobar = None
348
349         def mainMenu(self):
350                 print "loading mainmenu XML..."
351                 menu = mdom.childNodes[0]
352                 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
353
354                 self.session.infobar = self
355                 # so we can access the currently active infobar from screens opened from within the mainmenu
356                 # at the moment used from the SubserviceSelection
357
358                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu, menu.childNodes)
359
360         def mainMenuClosed(self, *val):
361                 self.session.infobar = None
362
363 class InfoBarSimpleEventView:
364         """ Opens the Eventview for now/next """
365         def __init__(self):
366                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
367                         {
368                                 "showEventInfo": (self.openEventView, _("show event details")),
369                         })
370
371         def openEventView(self):
372                 self.epglist = [ ]
373                 service = self.session.nav.getCurrentService()
374                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
375                 info = service.info()
376                 ptr=info.getEvent(0)
377                 if ptr:
378                         self.epglist.append(ptr)
379                 ptr=info.getEvent(1)
380                 if ptr:
381                         self.epglist.append(ptr)
382                 if len(self.epglist) > 0:
383                         self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
384
385         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
386                 if len(self.epglist) > 1:
387                         tmp = self.epglist[0]
388                         self.epglist[0]=self.epglist[1]
389                         self.epglist[1]=tmp
390                         setEvent(self.epglist[0])
391
392 class InfoBarEPG:
393         """ EPG - Opens an EPG list when the showEPGList action fires """
394         def __init__(self):
395                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
396                         {
397                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
398                         })
399
400                 self.is_now_next = False
401                 self.dlg_stack = [ ]
402                 self.bouquetSel = None
403                 self.eventView = None
404                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
405                         {
406                                 "showEventInfo": (self.openEventView, _("show EPG...")),
407                         })
408
409         def zapToService(self, service):
410                 if not service is None:
411                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
412                                 self.servicelist.clearPath()
413                                 if self.servicelist.bouquet_root != self.epg_bouquet:
414                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
415                                 self.servicelist.enterPath(self.epg_bouquet)
416                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
417                         self.servicelist.zap()
418
419         def getBouquetServices(self, bouquet):
420                 services = [ ]
421                 servicelist = eServiceCenter.getInstance().list(bouquet)
422                 if not servicelist is None:
423                         while True:
424                                 service = servicelist.getNext()
425                                 if not service.valid(): #check if end of list
426                                         break
427                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
428                                         continue
429                                 services.append(ServiceReference(service))
430                 return services
431
432         def openBouquetEPG(self, bouquet, withCallback=True):
433                 services = self.getBouquetServices(bouquet)
434                 if len(services):
435                         self.epg_bouquet = bouquet
436                         if withCallback:
437                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
438                         else:
439                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
440
441         def changeBouquetCB(self, direction, epg):
442                 if self.bouquetSel:
443                         if direction > 0:
444                                 self.bouquetSel.down()
445                         else:
446                                 self.bouquetSel.up()
447                         bouquet = self.bouquetSel.getCurrent()
448                         services = self.getBouquetServices(bouquet)
449                         if len(services):
450                                 self.epg_bouquet = bouquet
451                                 epg.setServices(services)
452
453         def closed(self, ret=False):
454                 closedScreen = self.dlg_stack.pop()
455                 if self.bouquetSel and closedScreen == self.bouquetSel:
456                         self.bouquetSel = None
457                 elif self.eventView and closedScreen == self.eventView:
458                         self.eventView = None
459                 if ret:
460                         dlgs=len(self.dlg_stack)
461                         if dlgs > 0:
462                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
463
464         def openMultiServiceEPG(self, withCallback=True):
465                 bouquets = self.servicelist.getBouquetList()
466                 if bouquets is None:
467                         cnt = 0
468                 else:
469                         cnt = len(bouquets)
470                 if cnt > 1: # show bouquet list
471                         if withCallback:
472                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
473                                 self.dlg_stack.append(self.bouquetSel)
474                         else:
475                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
476                 elif cnt == 1: 
477                         self.openBouquetEPG(bouquets[0][1], withCallback)
478
479         def openSingleServiceEPG(self):
480                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
481                 self.session.open(EPGSelection, ref)
482
483         def openSimilarList(self, eventid, refstr):
484                 self.session.open(EPGSelection, refstr, None, eventid)
485
486         def getNowNext(self):
487                 self.epglist = [ ]
488                 service = self.session.nav.getCurrentService()
489                 info = service and service.info()
490                 ptr = info and info.getEvent(0)
491                 if ptr:
492                         self.epglist.append(ptr)
493                 ptr = info and info.getEvent(1)
494                 if ptr:
495                         self.epglist.append(ptr)
496
497         def __evEventInfoChanged(self):
498                 if self.is_now_next and len(self.dlg_stack) == 1:
499                         self.getNowNext()
500                         assert self.eventView
501                         if len(self.epglist):
502                                 self.eventView.setEvent(self.epglist[0])
503
504         def openEventView(self):
505                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
506                 self.getNowNext()
507                 if len(self.epglist) == 0:
508                         self.is_now_next = False
509                         epg = eEPGCache.getInstance()
510                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
511                         if ptr:
512                                 self.epglist.append(ptr)
513                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
514                                 if ptr:
515                                         self.epglist.append(ptr)
516                 else:
517                         self.is_now_next = True
518                 if len(self.epglist) > 0:
519                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
520                         self.dlg_stack.append(self.eventView)
521                 else:
522                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
523                         self.openMultiServiceEPG(False)
524
525         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
526                 if len(self.epglist) > 1:
527                         tmp = self.epglist[0]
528                         self.epglist[0]=self.epglist[1]
529                         self.epglist[1]=tmp
530                         setEvent(self.epglist[0])
531
532 class InfoBarTuner:
533         """provides a snr/agc/ber display"""
534         def __init__(self):
535                 self["FrontendStatus"] = FrontendStatus(service_source = self.session.nav.getCurrentService)
536
537 class InfoBarEvent:
538         """provides a current/next event info display"""
539         def __init__(self):
540                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.NOW)
541                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.NEXT)
542
543 class InfoBarRadioText:
544         """provides radio (RDS) text info display"""
545         def __init__(self):
546                 self["RadioText"] = RadioText(self.session.nav)
547
548 class InfoBarServiceName:
549         def __init__(self):
550                 self["CurrentService"] = CurrentService(self.session.nav)
551
552 class InfoBarSeek:
553         """handles actions like seeking, pause"""
554         
555         # ispause, isff, issm
556         SEEK_STATE_PLAY = (0, 0, 0, ">")
557         SEEK_STATE_PAUSE = (1, 0, 0, "||")
558         SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
559         SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
560         SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
561         SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
562         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
563         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
564         
565         SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
566         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
567         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
568         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
569         
570         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
571         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
572         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
573         
574         def __init__(self):
575                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
576                         {
577                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
578                                 iPlayableService.evStart: self.__serviceStarted,
579                                 
580                                 iPlayableService.evEOF: self.__evEOF,
581                                 iPlayableService.evSOF: self.__evSOF,
582                         })
583
584                 class InfoBarSeekActionMap(HelpableActionMap):
585                         def __init__(self, screen, *args, **kwargs):
586                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
587                                 self.screen = screen
588                                 
589                         def action(self, contexts, action):
590                                 if action[:5] == "seek:":
591                                         time = int(action[5:])
592                                         self.screen.seekRelative(time * 90000)
593                                         return 1
594                                 else:
595                                         return HelpableActionMap.action(self, contexts, action)
596
597                 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
598                         {
599                                 "playpauseService": (self.playpauseService, _("pause")),
600                                 "pauseService": (self.pauseService, _("pause")),
601                                 "unPauseService": (self.unPauseService, _("continue")),
602                                 
603                                 "seekFwd": (self.seekFwd, _("skip forward")),
604                                 "seekFwdDown": self.seekFwdDown,
605                                 "seekFwdUp": self.seekFwdUp,
606                                 "seekBack": (self.seekBack, _("skip backward")),
607                                 "seekBackDown": self.seekBackDown,
608                                 "seekBackUp": self.seekBackUp,
609                         }, prio=-1)
610                         # give them a little more priority to win over color buttons
611
612                 self.seekstate = self.SEEK_STATE_PLAY
613                 self.onClose.append(self.delTimer)
614                 
615                 self.fwdtimer = False
616                 self.fwdKeyTimer = eTimer()
617                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
618
619                 self.rwdtimer = False
620                 self.rwdKeyTimer = eTimer()
621                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
622                 
623                 self.onPlayStateChanged = [ ]
624                 
625                 self.lockedBecauseOfSkipping = False
626         
627         def up(self):
628                 pass
629         
630         def down(self):
631                 pass
632         
633         def delTimer(self):
634                 del self.fwdKeyTimer
635                 del self.rwdKeyTimer
636         
637         def getSeek(self):
638                 service = self.session.nav.getCurrentService()
639                 if service is None:
640                         return None
641
642                 seek = service.seek()
643
644                 if seek is None or not seek.isCurrentlySeekable():
645                         return None
646                 
647                 return seek
648         
649         def isSeekable(self):
650                 if self.getSeek() is None:
651                         return False
652                 return True
653
654         def __seekableStatusChanged(self):
655                 print "seekable status changed!"
656                 if not self.isSeekable():
657                         self["SeekActions"].setEnabled(False)
658                         print "not seekable, return to play"
659                         self.setSeekState(self.SEEK_STATE_PLAY)
660                 else:
661                         self["SeekActions"].setEnabled(True)
662                         print "seekable"
663
664         def __serviceStarted(self):
665                 self.seekstate = self.SEEK_STATE_PLAY
666
667         def setSeekState(self, state):
668                 service = self.session.nav.getCurrentService()
669                 
670                 if service is None:
671                         return False
672                 
673                 if not self.isSeekable():
674                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
675                                 state = self.SEEK_STATE_PLAY
676                 
677                 pauseable = service.pause()
678
679                 if pauseable is None:
680                         print "not pauseable."
681                         state = self.SEEK_STATE_PLAY
682                 
683                 oldstate = self.seekstate
684                 self.seekstate = state
685                 
686                 for i in range(3):
687                         if oldstate[i] != self.seekstate[i]:
688                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
689
690                 for c in self.onPlayStateChanged:
691                         c(self.seekstate)
692                 
693                 self.checkSkipShowHideLock()
694
695                 return True
696         
697         def playpauseService(self):
698                 if self.seekstate != self.SEEK_STATE_PLAY:
699                         self.unPauseService()
700                 else:
701                         self.pauseService()
702
703         def pauseService(self):
704                 if self.seekstate == self.SEEK_STATE_PAUSE:
705                         print "pause, but in fact unpause"
706                         self.unPauseService()
707                 else:
708                         if self.seekstate == self.SEEK_STATE_PLAY:
709                                 print "yes, playing."
710                         else:
711                                 print "no", self.seekstate
712                         print "pause"
713                         self.setSeekState(self.SEEK_STATE_PAUSE);
714                 
715         def unPauseService(self):
716                 print "unpause"
717                 if self.seekstate == self.SEEK_STATE_PLAY:
718                         return 0
719                 self.setSeekState(self.SEEK_STATE_PLAY)
720         
721         def doSeek(self, seektime):
722                 print "doseek", seektime
723                 service = self.session.nav.getCurrentService()
724                 if service is None:
725                         return
726                 
727                 seekable = self.getSeek()
728                 if seekable is None:
729                         return
730                 
731                 seekable.seekTo(90 * seektime)
732
733         def seekFwdDown(self):
734                 print "start fwd timer"
735                 self.fwdtimer = True
736                 self.fwdKeyTimer.start(1000)
737
738         def seekBackDown(self):
739                 print "start rewind timer"
740                 self.rwdtimer = True
741                 self.rwdKeyTimer.start(1000)
742
743         def seekFwdUp(self):
744                 print "seekFwdUp"
745                 if self.fwdtimer:
746                         self.fwdKeyTimer.stop()
747                         self.fwdtimer = False
748                         self.seekFwd()
749
750         def seekFwd(self):
751                 lookup = {
752                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
753                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
754                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
755                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
756                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
757                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
758                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
759                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
760                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
761                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
762                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
763                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
764                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
765                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
766                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
767                         }
768                 self.setSeekState(lookup[self.seekstate])
769         
770         def seekBackUp(self):
771                 print "seekBackUp"
772                 if self.rwdtimer:
773                         self.rwdKeyTimer.stop()
774                         self.rwdtimer = False
775                         self.seekBack()
776                 
777         def seekBack(self):
778                 lookup = {
779                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
780                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
781                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
782                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
783                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
784                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
785                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
786                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
787                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
788                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
789                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
790                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
791                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
792                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
793                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
794                         }
795                 self.setSeekState(lookup[self.seekstate])
796                 
797                 if self.seekstate == self.SEEK_STATE_PAUSE:
798                         seekable = self.getSeek()
799                         if seekable is not None:
800                                 seekable.seekRelative(-1, 3)
801
802         def fwdTimerFire(self):
803                 print "Display seek fwd"
804                 self.fwdKeyTimer.stop()
805                 self.fwdtimer = False
806                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
807                 
808         def fwdSeekTo(self, minutes):
809                 print "Seek", minutes, "minutes forward"
810                 if minutes != 0:
811                         seekable = self.getSeek()
812                         if seekable is not None:
813                                 seekable.seekRelative(1, minutes * 60 * 90000)
814         
815         def rwdTimerFire(self):
816                 print "rwdTimerFire"
817                 self.rwdKeyTimer.stop()
818                 self.rwdtimer = False
819                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
820         
821         def rwdSeekTo(self, minutes):
822                 print "rwdSeekTo"
823                 self.fwdSeekTo(0 - minutes)
824         
825         def checkSkipShowHideLock(self):
826                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
827                 
828                 if config.usage.show_infobar_on_zap.value:
829                         if self.lockedBecauseOfSkipping and not wantlock:
830                                 self.unlockShow()
831                                 self.lockedBecauseOfSkipping = False
832                 
833                         if wantlock and not self.lockedBecauseOfSkipping:
834                                 self.lockShow()
835                                 self.lockedBecauseOfSkipping = True
836
837         def __evEOF(self):
838                 if self.seekstate != self.SEEK_STATE_PLAY:
839                         self.setSeekState(self.SEEK_STATE_PAUSE)
840                         # HACK
841                         #self.getSeek().seekRelative(1, -90000)
842                         self.setSeekState(self.SEEK_STATE_PLAY)
843                 else:
844                         self.setSeekState(self.SEEK_STATE_PAUSE)
845         
846         def __evSOF(self):
847                 self.setSeekState(self.SEEK_STATE_PLAY)
848                 self.doSeek(0)
849
850         def seekRelative(self, diff):
851                 seekable = self.getSeek()
852                 if seekable is not None:
853                         seekable.seekRelative(1, diff)
854
855         def seekAbsolute(self, abs):
856                 seekable = self.getSeek()
857                 if seekable is not None:
858                         seekable.seekTo(abs)
859
860 from Screens.PVRState import PVRState, TimeshiftState
861
862 class InfoBarPVRState:
863         def __init__(self, screen=PVRState):
864                 self.onPlayStateChanged.append(self.__playStateChanged)
865                 self.pvrStateDialog = self.session.instantiateDialog(screen)
866                 self.onShow.append(self._mayShow)
867                 self.onHide.append(self.pvrStateDialog.hide)
868
869         def _mayShow(self):
870                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
871                         self.pvrStateDialog.show()
872
873         def __playStateChanged(self, state):
874                 playstateString = state[3]
875                 self.pvrStateDialog["state"].setText(playstateString)
876                 self._mayShow()
877
878 class InfoBarTimeshiftState(InfoBarPVRState):
879         def __init__(self):
880                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
881
882         def _mayShow(self):
883                 if self.execing and self.timeshift_enabled:
884                         self.pvrStateDialog.show()
885
886 class InfoBarShowMovies:
887
888         # i don't really like this class. 
889         # it calls a not further specified "movie list" on up/down/movieList,
890         # so this is not more than an action map
891         def __init__(self):
892                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
893                         {
894                                 "movieList": (self.showMovies, "movie list"),
895                                 "up": (self.showMovies, "movie list"),
896                                 "down": (self.showMovies, "movie list")
897                         })
898
899 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
900
901 # Hrmf.
902 #
903 # Timeshift works the following way:
904 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
905 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
906 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
907 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
908 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
909 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
910 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
911 #
912
913 # in other words:
914 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
915 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
916 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
917 # - the user can now PVR around
918 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
919 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
920 # after!
921 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
922 # - if the user rewinds, or press pause, timeshift will be activated again
923
924 # note that a timeshift can be enabled ("recording") and
925 # activated (currently time-shifting).
926
927 class InfoBarTimeshift:
928         def __init__(self):
929                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
930                         {
931                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
932                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
933                         }, prio=1)
934                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
935                         {
936                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
937                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
938                         }, prio=-1) # priority over record
939
940                 self.timeshift_enabled = 0
941                 self.timeshift_state = 0
942                 self.ts_pause_timer = eTimer()
943                 self.ts_pause_timer.timeout.get().append(self.pauseService)
944
945                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
946                         {
947                                 iPlayableService.evStart: self.__serviceStarted,
948                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
949                         })
950         
951         def getTimeshift(self):
952                 service = self.session.nav.getCurrentService()
953                 return service and service.timeshift()
954
955         def startTimeshift(self):
956                 print "enable timeshift"
957                 ts = self.getTimeshift()
958                 if ts is None:
959                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
960                         print "no ts interface"
961                         return 0;
962                 
963                 if self.timeshift_enabled:
964                         print "hu, timeshift already enabled?"
965                 else:
966                         if not ts.startTimeshift():
967                                 self.timeshift_enabled = 1
968
969                                 # we remove the "relative time" for now.
970                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
971                                         
972                                 # PAUSE.
973                                 self.setSeekState(self.SEEK_STATE_PAUSE)
974                                 
975                                 # enable the "TimeshiftEnableActions", which will override
976                                 # the startTimeshift actions
977                                 self.__seekableStatusChanged()
978                         else:
979                                 print "timeshift failed"
980
981         def stopTimeshift(self):
982                 if not self.timeshift_enabled:
983                         return 0
984                 print "disable timeshift"
985                 ts = self.getTimeshift()
986                 if ts is None:
987                         return 0
988                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
989
990         def stopTimeshiftConfirmed(self, confirmed):
991                 if not confirmed:
992                         return
993
994                 ts = self.getTimeshift()
995                 if ts is None:
996                         return
997
998                 ts.stopTimeshift()
999                 self.timeshift_enabled = 0
1000
1001                 # disable actions
1002                 self.__seekableStatusChanged()
1003         
1004         # activates timeshift, and seeks to (almost) the end
1005         def activateTimeshiftEnd(self):
1006                 ts = self.getTimeshift()
1007                 
1008                 if ts is None:
1009                         return
1010                 
1011                 if ts.isTimeshiftActive():
1012                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1013                         self.pauseService()
1014                 else:
1015                         self.setSeekState(self.SEEK_STATE_PLAY)
1016                         ts.activateTimeshift()
1017                         self.seekRelative(0)
1018         
1019         # same as activateTimeshiftEnd, but pauses afterwards.
1020         def activateTimeshiftEndAndPause(self):
1021                 state = self.seekstate
1022                 self.activateTimeshiftEnd()
1023                 
1024                 # well, this is "andPause", but it could be pressed from pause,
1025                 # when pausing on the (fake-)"live" picture, so an un-pause
1026                 # is perfectly ok.
1027                 
1028                 print "now, pauseService"
1029                 if state == self.SEEK_STATE_PLAY:
1030                         print "is PLAYING, start pause timer"
1031                         self.ts_pause_timer.start(200, 1)
1032                 else:
1033                         print "unpause"
1034                         self.unPauseService()
1035         
1036         def __seekableStatusChanged(self):
1037                 enabled = False
1038                 
1039                 print "self.isSeekable", self.isSeekable()
1040                 print "self.timeshift_enabled", self.timeshift_enabled
1041                 
1042                 # when this service is not seekable, but timeshift
1043                 # is enabled, this means we can activate
1044                 # the timeshift
1045                 if not self.isSeekable() and self.timeshift_enabled:
1046                         enabled = True
1047
1048                 print "timeshift activate:", enabled
1049                 self["TimeshiftActivateActions"].setEnabled(enabled)
1050
1051         def __serviceStarted(self):
1052                 self.timeshift_enabled = False
1053                 self.__seekableStatusChanged()
1054
1055 from Screens.PiPSetup import PiPSetup
1056
1057 class InfoBarExtensions:
1058         EXTENSION_SINGLE = 0
1059         EXTENSION_LIST = 1
1060         
1061         def __init__(self):
1062                 self.list = []
1063                 
1064                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1065                         {
1066                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1067                         })
1068
1069         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1070                 self.list.append((type, extension, key))
1071                 
1072         def updateExtension(self, extension, key = None):
1073                 self.extensionsList.append(extension)
1074                 if key is not None:
1075                         if self.extensionKeys.has_key(key):
1076                                 key = None
1077                 
1078                 if key is None:
1079                         for x in self.availableKeys:
1080                                 if not self.extensionKeys.has_key(x):
1081                                         key = x
1082                                         break
1083
1084                 if key is not None:
1085                         self.extensionKeys[key] = len(self.extensionsList) - 1
1086                         
1087         def updateExtensions(self):
1088                 self.extensionsList = []
1089                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1090                 self.extensionKeys = {}
1091                 for x in self.list:
1092                         if x[0] == self.EXTENSION_SINGLE:
1093                                 self.updateExtension(x[1], x[2])
1094                         else:
1095                                 for y in x[1]():
1096                                         self.updateExtension(y[0], y[1])
1097
1098
1099         def showExtensionSelection(self):
1100                 self.updateExtensions()
1101                 extensionsList = self.extensionsList[:]
1102                 keys = []
1103                 list = []
1104                 for x in self.availableKeys:
1105                         if self.extensionKeys.has_key(x):
1106                                 entry = self.extensionKeys[x]
1107                                 extension = self.extensionsList[entry]
1108                                 if extension[2]():
1109                                         name = str(extension[0]())
1110                                         list.append((extension[0](), extension))
1111                                         keys.append(x)
1112                                         extensionsList.remove(extension)
1113                                 else:
1114                                         extensionsList.remove(extension)
1115                 for x in extensionsList:
1116                         list.append((x[0](), x))
1117                 keys += [""] * len(extensionsList)
1118                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys)
1119
1120         def extensionCallback(self, answer):
1121                 if answer is not None:
1122                         answer[1][1]()
1123
1124 from Tools.BoundFunction import boundFunction
1125
1126 # depends on InfoBarExtensions
1127 from Components.PluginComponent import plugins
1128
1129 class InfoBarPlugins:
1130         def __init__(self):
1131                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1132                 
1133         def getPluginName(self, name):
1134                 return name
1135                 
1136         def getPluginList(self):
1137                 list = []
1138                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1139                         list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
1140                 return list
1141
1142         def runPlugin(self, plugin):
1143                 plugin(session = self.session)
1144
1145 # depends on InfoBarExtensions
1146 class InfoBarSleepTimer:
1147         def __init__(self):
1148                 self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, self.available), "1")      
1149                 
1150         def available(self):
1151                 return True
1152
1153         def getSleepTimerName(self):
1154                 return _("Sleep Timer")
1155
1156         def showSleepTimerSetup(self):
1157                 self.session.open(SleepTimerEdit)
1158
1159 # depends on InfoBarExtensions
1160 class InfoBarPiP:
1161         def __init__(self):
1162                 self.session.pipshown = False
1163
1164                 self.addExtension((self.getShowHideName, self.showPiP, self.available), "blue")
1165                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1166                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1167         
1168         def available(self):
1169                 return True
1170         
1171         def pipShown(self):
1172                 return self.session.pipshown
1173         
1174         def getShowHideName(self):
1175                 if self.session.pipshown:
1176                         return _("Disable Picture in Picture")
1177                 else:
1178                         return _("Activate Picture in Picture")
1179                 
1180         def getSwapName(self):
1181                 return _("Swap Services")
1182                 
1183         def getMoveName(self):
1184                 return _("Move Picture in Picture")
1185         
1186         def showPiP(self):
1187                 if self.session.pipshown:
1188                         del self.session.pip
1189                         self.session.pipshown = False
1190                 else:
1191                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1192                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1193                         if self.session.pip.playService(newservice):
1194                                 self.session.pipshown = True
1195                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1196                         else:
1197                                 self.session.pipshown = False
1198                                 del self.session.pip
1199                         self.session.nav.playService(newservice)
1200         
1201         def swapPiP(self):
1202                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1203                 if self.session.pip.servicePath:
1204                         servicepath = self.servicelist.getCurrentServicePath()
1205                         ref=servicepath[len(servicepath)-1]
1206                         pipref=self.session.pip.getCurrentService()
1207                         self.session.pip.playService(swapservice)
1208                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1209                         if pipref.toString() != ref.toString(): # is a subservice ?
1210                                 self.session.nav.stopService() # stop portal
1211                                 self.session.nav.playService(pipref) # start subservice
1212                         self.session.pip.servicePath=servicepath
1213         
1214         def movePiP(self):
1215                 self.session.open(PiPSetup, pip = self.session.pip)
1216
1217 from RecordTimer import parseEvent
1218
1219 class InfoBarInstantRecord:
1220         """Instant Record - handles the instantRecord action in order to 
1221         start/stop instant records"""
1222         def __init__(self):
1223                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1224                         {
1225                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1226                         })
1227                 self.recording = []
1228                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1229                 self["BlinkingPoint"].hide()
1230                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1231
1232         def stopCurrentRecording(self, entry = -1):     
1233                 if entry is not None and entry != -1:
1234                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1235                         self.recording.remove(self.recording[entry])
1236
1237         def startInstantRecording(self, limitEvent = False):
1238                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1239                 
1240                 # try to get event info
1241                 event = None
1242                 try:
1243                         service = self.session.nav.getCurrentService()
1244                         epg = eEPGCache.getInstance()
1245                         event = epg.lookupEventTime(serviceref, -1, 0)
1246                         if event is None:
1247                                 info = service.info()
1248                                 ev = info.getEvent(0)
1249                                 event = ev
1250                 except:
1251                         pass
1252
1253                 begin = time()
1254                 end = time() + 3600 * 10
1255                 name = "instant record"
1256                 description = ""
1257                 eventid = None
1258                 
1259                 if event is not None:
1260                         curEvent = parseEvent(event)
1261                         name = curEvent[2]
1262                         description = curEvent[3]
1263                         eventid = curEvent[4]
1264                         if limitEvent:
1265                                 end = curEvent[1]
1266                 else:
1267                         if limitEvent:
1268                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1269                                 
1270                 data = (begin, end, name, description, eventid)
1271                 
1272                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1273                 recording.dontSave = True
1274                 self.recording.append(recording)
1275                 
1276                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1277                 
1278         def isInstantRecordRunning(self):
1279                 print "self.recording:", self.recording
1280                 if len(self.recording) > 0:
1281                         for x in self.recording:
1282                                 if x.isRunning():
1283                                         return True
1284                 return False
1285
1286         def recordQuestionCallback(self, answer):
1287                 print "pre:\n", self.recording
1288                 
1289                 if answer is None or answer[1] == "no":
1290                         return
1291                 list = []
1292                 recording = self.recording[:]
1293                 for x in recording:
1294                         if not x in self.session.nav.RecordTimer.timer_list:
1295                                 self.recording.remove(x)
1296                         elif x.dontSave and x.isRunning():
1297                                 list.append(TimerEntryComponent(x, False))              
1298
1299                 if answer[1] == "changeduration":
1300                         if len(self.recording) == 1:
1301                                 self.changeDuration(0)
1302                         else:
1303                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1304                 elif answer[1] == "stop":
1305                         if len(self.recording) == 1:
1306                                 self.stopCurrentRecording(0)
1307                         else:
1308                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1309                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1310                         limitEvent = False
1311                         if answer[1] == "event":
1312                                 limitEvent = True
1313                         if answer[1] == "manualduration":
1314                                 self.selectedEntry = len(self.recording)
1315                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1316                         self.startInstantRecording(limitEvent = limitEvent)
1317                         
1318                 print "after:\n", self.recording
1319
1320         def changeDuration(self, entry):
1321                 if entry is not None:
1322                         self.selectedEntry = entry
1323                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1324
1325         def inputCallback(self, value):
1326                 if value is not None:
1327                         print "stopping recording after", int(value), "minutes."
1328                         self.recording[self.selectedEntry].end = time() + 60 * int(value)
1329                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1330
1331         def instantRecord(self):
1332                 try:
1333                         stat = os_stat(resolveFilename(SCOPE_HDD))
1334                 except:
1335                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1336                         return
1337
1338                 if self.isInstantRecordRunning():
1339                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1340                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1341                                 list=[(_("stop recording"), "stop"), \
1342                                 (_("change recording (duration)"), "changeduration"), \
1343                                 (_("add recording (indefinitely)"), "indefinitely"), \
1344                                 (_("add recording (stop after current event)"), "event"), \
1345                                 (_("add recording (enter recording duration)"), "manualduration"), \
1346                                 (_("do nothing"), "no")])
1347                 else:
1348                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1349                                 title=_("Start recording?"), \
1350                                 list=[(_("add recording (indefinitely)"), "indefinitely"), \
1351                                 (_("add recording (stop after current event)"), "event"), \
1352                                 (_("add recording (enter recording duration)"), "manualduration"), \
1353                                 (_("don't record"), "no")])
1354
1355 from Tools.ISO639 import LanguageCodes
1356
1357 class InfoBarAudioSelection:
1358         def __init__(self):
1359                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1360                         {
1361                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1362                         })
1363
1364         def audioSelection(self):
1365                 service = self.session.nav.getCurrentService()
1366                 audio = service and service.audioTracks()
1367                 self.audioTracks = audio
1368                 n = audio and audio.getNumberOfTracks() or 0
1369                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1370                 tlist = []
1371                 print "tlist:", tlist
1372                 if n > 0:
1373                         self.audioChannel = service.audioChannel()
1374
1375                         for x in range(n):
1376                                 i = audio.getTrackInfo(x)
1377                                 language = i.getLanguage()
1378                                 description = i.getDescription()
1379         
1380                                 if LanguageCodes.has_key(language):
1381                                         language = LanguageCodes[language][0]
1382         
1383                                 if len(description):
1384                                         description += " (" + language + ")"
1385                                 else:
1386                                         description = language
1387         
1388                                 tlist.append((description, x))
1389                         
1390                         selectedAudio = tlist[0][1]
1391                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1392
1393                         selection = 2
1394                         for x in tlist:
1395                                 if x[1] != selectedAudio:
1396                                         selection += 1
1397                                 else:
1398                                         break
1399
1400                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1401                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1402                 else:
1403                         del self.audioTracks
1404
1405         def audioSelected(self, audio):
1406                 if audio is not None:
1407                         if isinstance(audio[1], str):
1408                                 if audio[1] == "mode":
1409                                         keys = ["red", "green", "yellow"]
1410                                         selection = self.audioChannel.getCurrentChannel()
1411                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1412                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1413                         else:
1414                                 del self.audioChannel
1415                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1416                                         self.audioTracks.selectTrack(audio[1])
1417                 else:
1418                         del self.audioChannel
1419                 del self.audioTracks
1420
1421         def modeSelected(self, mode):
1422                 if mode is not None:
1423                         self.audioChannel.selectChannel(mode[1])
1424                 del self.audioChannel
1425
1426 class InfoBarSubserviceSelection:
1427         def __init__(self):
1428                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1429                         {
1430                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1431                         })
1432
1433                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1434                         {
1435                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1436                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1437                         }, -1)
1438                 self["SubserviceQuickzapAction"].setEnabled(False)
1439
1440                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1441
1442                 self.bsel = None
1443
1444         def checkSubservicesAvail(self, ev):
1445                 if ev == iPlayableService.evUpdatedEventInfo:
1446                         service = self.session.nav.getCurrentService()
1447                         subservices = service and service.subServices()
1448                         if not subservices or subservices.getNumberOfSubservices() == 0:
1449                                 self["SubserviceQuickzapAction"].setEnabled(False)
1450
1451         def nextSubservice(self):
1452                 self.changeSubservice(+1)
1453
1454         def prevSubservice(self):
1455                 self.changeSubservice(-1)
1456
1457         def changeSubservice(self, direction):
1458                 service = self.session.nav.getCurrentService()
1459                 subservices = service and service.subServices()
1460                 n = subservices and subservices.getNumberOfSubservices()
1461                 if n and n > 0:
1462                         selection = -1
1463                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1464                         for x in range(n):
1465                                 if subservices.getSubservice(x).toString() == ref.toString():
1466                                         selection = x
1467                         if selection != -1:
1468                                 selection += direction
1469                                 if selection >= n:
1470                                         selection=0
1471                                 elif selection < 0:
1472                                         selection=n-1
1473                                 newservice = subservices.getSubservice(selection)
1474                                 if newservice.valid():
1475                                         del subservices
1476                                         del service
1477                                         if config.usage.show_infobar_on_zap.value:
1478                                                 self.doShow()
1479                                         self.session.nav.playService(newservice)
1480
1481         def subserviceSelection(self):
1482                 service = self.session.nav.getCurrentService()
1483                 subservices = service and service.subServices()
1484                 self.bouquets = self.servicelist.getBouquetList()
1485                 n = subservices and subservices.getNumberOfSubservices()
1486                 selection = 0
1487                 if n and n > 0:
1488                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1489                         tlist = []
1490                         for x in range(n):
1491                                 i = subservices.getSubservice(x)
1492                                 if i.toString() == ref.toString():
1493                                         selection = x
1494                                 tlist.append((i.getName(), i))
1495
1496                         if self.bouquets and len(self.bouquets):
1497                                 keys = ["red", "green", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1498                                 if config.usage.multibouquet.value:
1499                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1500                                 else:
1501                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1502                                 selection += 3
1503                         else:
1504                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1505                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1506                                 selection += 2
1507
1508                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
1509
1510         def subserviceSelected(self, service):
1511                 del self.bouquets
1512                 if not service is None:
1513                         if isinstance(service[1], str):
1514                                 if service[1] == "quickzap":
1515                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1516                                         self.session.open(SubservicesQuickzap, service[2])
1517                         else:
1518                                 self["SubserviceQuickzapAction"].setEnabled(True)
1519                                 if config.usage.show_infobar_on_zap.value:
1520                                         self.doShow()
1521                                 self.session.nav.playService(service[1])
1522
1523         def addSubserviceToBouquetCallback(self, service):
1524                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1525                         self.selectedSubservice = service
1526                         if self.bouquets is None:
1527                                 cnt = 0
1528                         else:
1529                                 cnt = len(self.bouquets)
1530                         if cnt > 1: # show bouquet list
1531                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1532                         elif cnt == 1: # add to only one existing bouquet
1533                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1534                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1535
1536         def bouquetSelClosed(self, confirmed):
1537                 self.bsel = None
1538                 del self.selectedSubservice
1539                 if confirmed:
1540                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1541
1542         def addSubserviceToBouquet(self, dest):
1543                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1544                 if self.bsel:
1545                         self.bsel.close(True)
1546                 else:
1547                         del self.selectedSubservice
1548
1549 class InfoBarAdditionalInfo:
1550         def __init__(self):
1551                 self["NimA"] = Pixmap()
1552                 self["NimB"] = Pixmap()
1553                 self["NimA_Active"] = Pixmap()
1554                 self["NimB_Active"] = Pixmap()
1555
1556                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1557                 self["TimeshiftPossible"] = self["RecordingPossible"]
1558                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1559
1560                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1561                 res_mgr = eDVBResourceManager.getInstance()
1562                 if res_mgr:
1563                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1564
1565         def tunerUseMaskChanged(self, mask):
1566                 if mask&1:
1567                         self["NimA_Active"].show()
1568                 else:
1569                         self["NimA_Active"].hide()
1570                 if mask&2:
1571                         self["NimB_Active"].show()
1572                 else:
1573                         self["NimB_Active"].hide()
1574
1575         def checkTunerState(self, service):
1576                 info = service.frontendInfo()
1577                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1578                 if feNumber is None:
1579                         self["NimA"].hide()
1580                         self["NimB"].hide()
1581                 elif feNumber == 0:
1582                         self["NimB"].hide()
1583                         self["NimA"].show()
1584                 elif feNumber == 1:
1585                         self["NimA"].hide()
1586                         self["NimB"].show()
1587
1588         def gotServiceEvent(self, ev):
1589                 service = self.session.nav.getCurrentService()
1590                 if ev == iPlayableService.evStart:
1591                         self.checkTunerState(service)
1592
1593 class InfoBarNotifications:
1594         def __init__(self):
1595                 self.onExecBegin.append(self.checkNotifications)
1596                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1597                 self.onClose.append(self.__removeNotification)
1598         
1599         def __removeNotification(self):
1600                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1601         
1602         def checkNotificationsIfExecing(self):
1603                 if self.execing:
1604                         self.checkNotifications()
1605
1606         def checkNotifications(self):
1607                 if len(Notifications.notifications):
1608                         n = Notifications.notifications[0]
1609                         
1610                         Notifications.notifications = Notifications.notifications[1:]
1611                         cb = n[0]
1612                         if cb is not None:
1613                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1614                         else:
1615                                 dlg = self.session.open(n[1], *n[2], **n[3])
1616                         
1617                         # remember that this notification is currently active
1618                         d = (n[4], dlg)
1619                         Notifications.current_notifications.append(d)
1620                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1621
1622         def __notificationClosed(self, d):
1623                 Notifications.current_notifications.remove(d)
1624
1625 class InfoBarServiceNotifications:
1626         def __init__(self):
1627                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1628                         {
1629                                 iPlayableService.evEnd: self.serviceHasEnded
1630                         })
1631
1632         def serviceHasEnded(self):
1633                 print "service end!"
1634
1635                 try:
1636                         self.setSeekState(self.SEEK_STATE_PLAY)
1637                 except:
1638                         pass
1639
1640 class InfoBarCueSheetSupport:
1641         CUT_TYPE_IN = 0
1642         CUT_TYPE_OUT = 1
1643         CUT_TYPE_MARK = 2
1644         CUT_TYPE_LAST = 3
1645         
1646         ENABLE_RESUME_SUPPORT = False
1647         
1648         def __init__(self):
1649                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1650                         {
1651                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1652                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1653                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1654                         }, prio=1) 
1655                 
1656                 self.cut_list = [ ]
1657                 self.is_closing = False
1658                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1659                         {
1660                                 iPlayableService.evStart: self.__serviceStarted,
1661                         })
1662
1663         def __serviceStarted(self):
1664                 if self.is_closing:
1665                         return
1666                 print "new service started! trying to download cuts!"
1667                 self.downloadCuesheet()
1668                 
1669                 if self.ENABLE_RESUME_SUPPORT:
1670                         last = None
1671                         
1672                         for (pts, what) in self.cut_list:
1673                                 if what == self.CUT_TYPE_LAST:
1674                                         last = pts
1675                         
1676                         if last is not None:
1677                                 self.resume_point = last
1678                                 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1679
1680         def playLastCB(self, answer):
1681                 if answer == True:
1682                         seekable = self.__getSeekable()
1683                         if seekable is not None:
1684                                 seekable.seekTo(self.resume_point)
1685
1686         def __getSeekable(self):
1687                 service = self.session.nav.getCurrentService()
1688                 if service is None:
1689                         return None
1690                 return service.seek()
1691
1692         def cueGetCurrentPosition(self):
1693                 seek = self.__getSeekable()
1694                 if seek is None:
1695                         return None
1696                 r = seek.getPlayPosition()
1697                 if r[0]:
1698                         return None
1699                 return long(r[1])
1700
1701         def jumpPreviousNextMark(self, cmp, alternative=None):
1702                 current_pos = self.cueGetCurrentPosition()
1703                 if current_pos is None:
1704                         return
1705                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1706                 if mark is not None:
1707                         pts = mark[0]
1708                 elif alternative is not None:
1709                         pts = alternative
1710                 else:
1711                         return
1712
1713                 seekable = self.__getSeekable()
1714                 if seekable is not None:
1715                         seekable.seekTo(pts)
1716
1717         def jumpPreviousMark(self):
1718                 # we add 2 seconds, so if the play position is <2s after
1719                 # the mark, the mark before will be used
1720                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1721
1722         def jumpNextMark(self):
1723                 self.jumpPreviousNextMark(lambda x: x)
1724
1725         def getNearestCutPoint(self, pts, cmp=abs):
1726                 # can be optimized
1727                 nearest = None
1728                 for cp in self.cut_list:
1729                         diff = cmp(cp[0] - pts)
1730                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1731                                 nearest = cp
1732                 return nearest
1733
1734         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1735                 current_pos = self.cueGetCurrentPosition()
1736                 if current_pos is None:
1737                         print "not seekable"
1738                         return
1739                 
1740                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1741                 
1742                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1743                         if onlyreturn:
1744                                 return nearest_cutpoint
1745                         if not onlyadd:
1746                                 self.removeMark(nearest_cutpoint)
1747                 elif not onlyremove and not onlyreturn:
1748                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1749                 
1750                 if onlyreturn:
1751                         return None
1752
1753         def addMark(self, point):
1754                 insort(self.cut_list, point)
1755                 self.uploadCuesheet()
1756
1757         def removeMark(self, point):
1758                 self.cut_list.remove(point)
1759                 self.uploadCuesheet()
1760
1761         def __getCuesheet(self):
1762                 service = self.session.nav.getCurrentService()
1763                 if service is None:
1764                         return None
1765                 return service.cueSheet()
1766
1767         def uploadCuesheet(self):
1768                 cue = self.__getCuesheet()
1769
1770                 if cue is None:
1771                         print "upload failed, no cuesheet interface"
1772                         return
1773                 cue.setCutList(self.cut_list)
1774
1775         def downloadCuesheet(self):
1776                 cue = self.__getCuesheet()
1777
1778                 if cue is None:
1779                         print "upload failed, no cuesheet interface"
1780                         return
1781                 self.cut_list = cue.getCutList()
1782
1783 class InfoBarSummary(Screen):
1784         skin = """
1785         <screen position="0,0" size="132,64">
1786                 <widget source="CurrentTime" render="Label" position="56,46" size="82,18" font="Regular;16" >
1787                         <convert type="ClockToText">WithSeconds</convert>
1788                 </widget>
1789                 <widget source="CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
1790                         <convert type="ServiceName">Name</convert>
1791                 </widget>
1792         </screen>"""
1793
1794         def __init__(self, session, parent):
1795                 Screen.__init__(self, session)
1796                 self["CurrentService"] = CurrentService(self.session.nav)
1797                 self["CurrentTime"] = Clock()
1798
1799 class InfoBarSummarySupport:
1800         def __init__(self):
1801                 pass
1802         
1803         def createSummary(self):
1804                 return InfoBarSummary
1805
1806 class InfoBarTeletextPlugin:
1807         def __init__(self):
1808                 self.teletext_plugin = None
1809                 
1810                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1811                         self.teletext_plugin = p
1812                 
1813                 if self.teletext_plugin is not None:
1814                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1815                                 {
1816                                         "startTeletext": (self.startTeletext, _("View teletext..."))
1817                                 })
1818                 else:
1819                         print "no teletext plugin found!"
1820
1821         def startTeletext(self):
1822                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1823
1824 class InfoBarSubtitleSupport(object):
1825         def __init__(self):
1826                 object.__init__(self)
1827                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1828                 self.__subtitles_enabled = False
1829
1830                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1831                         {
1832                                 iPlayableService.evEnd: self.__serviceStopped,
1833                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
1834                         })
1835                 self.cached_subtitle_checked = False
1836
1837         def __serviceStopped(self):
1838                 self.subtitle_window.hide()
1839                 self.__subtitles_enabled = False
1840                 self.cached_subtitle_checked = False
1841
1842         def __updatedInfo(self):
1843                 if not self.cached_subtitle_checked:
1844                         subtitle = self.getCurrentServiceSubtitle()
1845                         self.cached_subtitle_checked = True
1846                         if subtitle:
1847                                 self.__selected_subtitle = subtitle.getCachedSubtitle()
1848                         if self.__selected_subtitle:
1849                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1850                                 self.subtitle_window.show()
1851                                 self.__subtitles_enabled = True
1852
1853         def getCurrentServiceSubtitle(self):
1854                 service = self.session.nav.getCurrentService()
1855                 return service and service.subtitle()
1856         
1857         def setSubtitlesEnable(self, enable=True):
1858                 subtitle = self.getCurrentServiceSubtitle()
1859                 if enable and self.__selected_subtitle is not None:
1860                         if subtitle and not self.__subtitles_enabled:
1861                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1862                                 self.subtitle_window.show()
1863                                 self.__subtitles_enabled = True
1864                 else:
1865                         if subtitle:
1866                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1867                         self.__subtitles_enabled = False
1868                         self.subtitle_window.hide()
1869
1870         def setSelectedSubtitle(self, subtitle):
1871                 self.__selected_subtitle = subtitle
1872
1873         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
1874         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
1875
1876 class InfoBarServiceErrorPopupSupport:
1877         def __init__(self):
1878                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1879                         {
1880                                 iPlayableService.evTuneFailed: self.__tuneFailed,
1881                                 iPlayableService.evStart: self.__serviceStarted
1882                         })
1883                 self.__serviceStarted()
1884
1885         def __serviceStarted(self):
1886                 self.last_error = None
1887                 Notifications.RemovePopup(id = "ZapError")
1888
1889         def __tuneFailed(self):
1890                 service = self.session.nav.getCurrentService()
1891                 info = service and service.info()
1892                 error = info and info.getInfo(iServiceInformation.sDVBState)
1893                 
1894                 if error == self.last_error:
1895                         error = None
1896                 else:
1897                         self.last_error = error
1898
1899                 errors = {
1900                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
1901                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
1902                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
1903                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
1904                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
1905                         eDVBServicePMTHandler.eventNewProgramInfo: None,
1906                         eDVBServicePMTHandler.eventTuned: None,
1907                         eDVBServicePMTHandler.eventSOF: None,
1908                         eDVBServicePMTHandler.eventEOF: None
1909                 }
1910
1911                 if error not in errors:
1912                         error = None
1913
1914                 error = error is not None and errors[error]
1915
1916                 if error is not None:
1917                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
1918                 else:
1919                         Notifications.RemovePopup(id = "ZapError")