some python import cleanups
[vuplus_dvbapp] / 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
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 = self.servicelist.appendDVBTypes(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                                 "pauseService": (self.pauseService, _("pause")),
600                                 "unPauseService": (self.unPauseService, _("continue")),
601                                 
602                                 "seekFwd": (self.seekFwd, _("skip forward")),
603                                 "seekFwdDown": self.seekFwdDown,
604                                 "seekFwdUp": self.seekFwdUp,
605                                 "seekBack": (self.seekBack, _("skip backward")),
606                                 "seekBackDown": self.seekBackDown,
607                                 "seekBackUp": self.seekBackUp,
608                         }, prio=-1)
609                         # give them a little more priority to win over color buttons
610
611                 self.seekstate = self.SEEK_STATE_PLAY
612                 self.onClose.append(self.delTimer)
613                 
614                 self.fwdtimer = False
615                 self.fwdKeyTimer = eTimer()
616                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
617
618                 self.rwdtimer = False
619                 self.rwdKeyTimer = eTimer()
620                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
621                 
622                 self.onPlayStateChanged = [ ]
623                 
624                 self.lockedBecauseOfSkipping = False
625         
626         def up(self):
627                 pass
628         
629         def down(self):
630                 pass
631         
632         def delTimer(self):
633                 del self.fwdKeyTimer
634                 del self.rwdKeyTimer
635         
636         def getSeek(self):
637                 service = self.session.nav.getCurrentService()
638                 if service is None:
639                         return None
640
641                 seek = service.seek()
642
643                 if seek is None or not seek.isCurrentlySeekable():
644                         return None
645                 
646                 return seek
647         
648         def isSeekable(self):
649                 if self.getSeek() is None:
650                         return False
651                 return True
652
653         def __seekableStatusChanged(self):
654                 print "seekable status changed!"
655                 if not self.isSeekable():
656                         self["SeekActions"].setEnabled(False)
657                         print "not seekable, return to play"
658                         self.setSeekState(self.SEEK_STATE_PLAY)
659                 else:
660                         self["SeekActions"].setEnabled(True)
661                         print "seekable"
662
663         def __serviceStarted(self):
664                 self.seekstate = self.SEEK_STATE_PLAY
665
666         def setSeekState(self, state):
667                 service = self.session.nav.getCurrentService()
668                 
669                 if service is None:
670                         return False
671                 
672                 if not self.isSeekable():
673                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
674                                 state = self.SEEK_STATE_PLAY
675                 
676                 pauseable = service.pause()
677
678                 if pauseable is None:
679                         print "not pauseable."
680                         state = self.SEEK_STATE_PLAY
681                 
682                 oldstate = self.seekstate
683                 self.seekstate = state
684                 
685                 for i in range(3):
686                         if oldstate[i] != self.seekstate[i]:
687                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
688
689                 for c in self.onPlayStateChanged:
690                         c(self.seekstate)
691                 
692                 self.checkSkipShowHideLock()
693
694                 return True
695
696         def pauseService(self):
697                 if self.seekstate == self.SEEK_STATE_PAUSE:
698                         print "pause, but in fact unpause"
699                         self.unPauseService()
700                 else:
701                         if self.seekstate == self.SEEK_STATE_PLAY:
702                                 print "yes, playing."
703                         else:
704                                 print "no", self.seekstate
705                         print "pause"
706                         self.setSeekState(self.SEEK_STATE_PAUSE);
707                 
708         def unPauseService(self):
709                 print "unpause"
710                 if self.seekstate == self.SEEK_STATE_PLAY:
711                         return 0
712                 self.setSeekState(self.SEEK_STATE_PLAY)
713         
714         def doSeek(self, seektime):
715                 print "doseek", seektime
716                 service = self.session.nav.getCurrentService()
717                 if service is None:
718                         return
719                 
720                 seekable = self.getSeek()
721                 if seekable is None:
722                         return
723                 
724                 seekable.seekTo(90 * seektime)
725
726         def seekFwdDown(self):
727                 print "start fwd timer"
728                 self.fwdtimer = True
729                 self.fwdKeyTimer.start(1000)
730
731         def seekBackDown(self):
732                 print "start rewind timer"
733                 self.rwdtimer = True
734                 self.rwdKeyTimer.start(1000)
735
736         def seekFwdUp(self):
737                 print "seekFwdUp"
738                 if self.fwdtimer:
739                         self.fwdKeyTimer.stop()
740                         self.fwdtimer = False
741                         self.seekFwd()
742
743         def seekFwd(self):
744                 lookup = {
745                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
746                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
747                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
748                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
749                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
750                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
751                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
752                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
753                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
754                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
755                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
756                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
757                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
758                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
759                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
760                         }
761                 self.setSeekState(lookup[self.seekstate])
762         
763         def seekBackUp(self):
764                 print "seekBackUp"
765                 if self.rwdtimer:
766                         self.rwdKeyTimer.stop()
767                         self.rwdtimer = False
768                         self.seekBack()
769                 
770         def seekBack(self):
771                 lookup = {
772                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
773                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
774                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
775                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
776                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
777                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
778                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
779                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
780                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
781                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
782                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
783                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
784                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
785                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
786                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
787                         }
788                 self.setSeekState(lookup[self.seekstate])
789                 
790                 if self.seekstate == self.SEEK_STATE_PAUSE:
791                         seekable = self.getSeek()
792                         if seekable is not None:
793                                 seekable.seekRelative(-1, 3)
794
795         def fwdTimerFire(self):
796                 print "Display seek fwd"
797                 self.fwdKeyTimer.stop()
798                 self.fwdtimer = False
799                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
800                 
801         def fwdSeekTo(self, minutes):
802                 print "Seek", minutes, "minutes forward"
803                 if minutes != 0:
804                         seekable = self.getSeek()
805                         if seekable is not None:
806                                 seekable.seekRelative(1, minutes * 60 * 90000)
807         
808         def rwdTimerFire(self):
809                 print "rwdTimerFire"
810                 self.rwdKeyTimer.stop()
811                 self.rwdtimer = False
812                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
813         
814         def rwdSeekTo(self, minutes):
815                 print "rwdSeekTo"
816                 self.fwdSeekTo(0 - minutes)
817         
818         def checkSkipShowHideLock(self):
819                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
820                 
821                 if config.usage.show_infobar_on_zap.value:
822                         if self.lockedBecauseOfSkipping and not wantlock:
823                                 self.unlockShow()
824                                 self.lockedBecauseOfSkipping = False
825                 
826                         if wantlock and not self.lockedBecauseOfSkipping:
827                                 self.lockShow()
828                                 self.lockedBecauseOfSkipping = True
829
830         def __evEOF(self):
831                 if self.seekstate != self.SEEK_STATE_PLAY:
832                         self.setSeekState(self.SEEK_STATE_PAUSE)
833                         # HACK
834                         #self.getSeek().seekRelative(1, -90000)
835                         self.setSeekState(self.SEEK_STATE_PLAY)
836                 else:
837                         self.setSeekState(self.SEEK_STATE_PAUSE)
838         
839         def __evSOF(self):
840                 self.setSeekState(self.SEEK_STATE_PLAY)
841                 self.doSeek(0)
842
843         def seekRelative(self, diff):
844                 seekable = self.getSeek()
845                 if seekable is not None:
846                         seekable.seekRelative(1, diff)
847
848         def seekAbsolute(self, abs):
849                 seekable = self.getSeek()
850                 if seekable is not None:
851                         seekable.seekTo(abs)
852
853 from Screens.PVRState import PVRState, TimeshiftState
854
855 class InfoBarPVRState:
856         def __init__(self, screen=PVRState):
857                 self.onPlayStateChanged.append(self.__playStateChanged)
858                 self.pvrStateDialog = self.session.instantiateDialog(screen)
859                 self.onShow.append(self.__mayShow)
860                 self.onHide.append(self.pvrStateDialog.hide)
861         
862         def __mayShow(self):
863                 if self.seekstate != self.SEEK_STATE_PLAY and self.execing:
864                         self.pvrStateDialog.show()
865
866         def __playStateChanged(self, state):
867                 playstateString = state[3]
868                 self.pvrStateDialog["state"].setText(playstateString)
869                 self.__mayShow()
870
871 class InfoBarTimeshiftState(InfoBarPVRState):
872         def __init__(self):
873                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
874
875 class InfoBarShowMovies:
876
877         # i don't really like this class. 
878         # it calls a not further specified "movie list" on up/down/movieList,
879         # so this is not more than an action map
880         def __init__(self):
881                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
882                         {
883                                 "movieList": (self.showMovies, "movie list"),
884                                 "up": (self.showMovies, "movie list"),
885                                 "down": (self.showMovies, "movie list")
886                         })
887
888 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
889
890 # Hrmf.
891 #
892 # Timeshift works the following way:
893 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
894 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
895 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
896 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
897 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
898 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
899 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
900 #
901
902 # in other words:
903 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
904 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
905 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
906 # - the user can now PVR around
907 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
908 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
909 # after!
910 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
911 # - if the user rewinds, or press pause, timeshift will be activated again
912
913 # note that a timeshift can be enabled ("recording") and
914 # activated (currently time-shifting).
915
916 class InfoBarTimeshift:
917         def __init__(self):
918                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
919                         {
920                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
921                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
922                         }, prio=1)
923                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
924                         {
925                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
926                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
927                         }, prio=-1) # priority over record
928
929                 self.timeshift_enabled = 0
930                 self.timeshift_state = 0
931                 self.ts_pause_timer = eTimer()
932                 self.ts_pause_timer.timeout.get().append(self.pauseService)
933
934                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
935                         {
936                                 iPlayableService.evStart: self.__serviceStarted,
937                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
938                         })
939         
940         def getTimeshift(self):
941                 service = self.session.nav.getCurrentService()
942                 return service and service.timeshift()
943
944         def startTimeshift(self):
945                 print "enable timeshift"
946                 ts = self.getTimeshift()
947                 if ts is None:
948                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
949                         print "no ts interface"
950                         return 0;
951                 
952                 if self.timeshift_enabled:
953                         print "hu, timeshift already enabled?"
954                 else:
955                         if not ts.startTimeshift():
956                                 self.timeshift_enabled = 1
957
958                                 # we remove the "relative time" for now.
959                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
960                                         
961                                 # PAUSE.
962                                 self.setSeekState(self.SEEK_STATE_PAUSE)
963                                 
964                                 # enable the "TimeshiftEnableActions", which will override
965                                 # the startTimeshift actions
966                                 self.__seekableStatusChanged()
967                         else:
968                                 print "timeshift failed"
969
970         def stopTimeshift(self):
971                 if not self.timeshift_enabled:
972                         return 0
973                 print "disable timeshift"
974                 ts = self.getTimeshift()
975                 if ts is None:
976                         return 0
977                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
978
979         def stopTimeshiftConfirmed(self, confirmed):
980                 if not confirmed:
981                         return
982
983                 ts = self.getTimeshift()
984                 if ts is None:
985                         return
986
987                 ts.stopTimeshift()
988                 self.timeshift_enabled = 0
989
990                 # disable actions
991                 self.__seekableStatusChanged()
992         
993         # activates timeshift, and seeks to (almost) the end
994         def activateTimeshiftEnd(self):
995                 ts = self.getTimeshift()
996                 
997                 if ts is None:
998                         return
999                 
1000                 if ts.isTimeshiftActive():
1001                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1002                         self.pauseService()
1003                 else:
1004                         self.setSeekState(self.SEEK_STATE_PLAY)
1005                         ts.activateTimeshift()
1006                         self.seekRelative(0)
1007         
1008         # same as activateTimeshiftEnd, but pauses afterwards.
1009         def activateTimeshiftEndAndPause(self):
1010                 state = self.seekstate
1011                 self.activateTimeshiftEnd()
1012                 
1013                 # well, this is "andPause", but it could be pressed from pause,
1014                 # when pausing on the (fake-)"live" picture, so an un-pause
1015                 # is perfectly ok.
1016                 
1017                 print "now, pauseService"
1018                 if state == self.SEEK_STATE_PLAY:
1019                         print "is PLAYING, start pause timer"
1020                         self.ts_pause_timer.start(200, 1)
1021                 else:
1022                         print "unpause"
1023                         self.unPauseService()
1024         
1025         def __seekableStatusChanged(self):
1026                 enabled = False
1027                 
1028                 print "self.isSeekable", self.isSeekable()
1029                 print "self.timeshift_enabled", self.timeshift_enabled
1030                 
1031                 # when this service is not seekable, but timeshift
1032                 # is enabled, this means we can activate
1033                 # the timeshift
1034                 if not self.isSeekable() and self.timeshift_enabled:
1035                         enabled = True
1036
1037                 print "timeshift activate:", enabled
1038                 self["TimeshiftActivateActions"].setEnabled(enabled)
1039
1040         def __serviceStarted(self):
1041                 self.timeshift_enabled = False
1042                 self.__seekableStatusChanged()
1043
1044 from Screens.PiPSetup import PiPSetup
1045
1046 class InfoBarExtensions:
1047         EXTENSION_SINGLE = 0
1048         EXTENSION_LIST = 1
1049         
1050         def __init__(self):
1051                 self.list = []
1052                 
1053                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1054                         {
1055                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1056                         })
1057
1058         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1059                 self.list.append((type, extension, key))
1060                 
1061         def updateExtension(self, extension, key = None):
1062                 self.extensionsList.append(extension)
1063                 if key is not None:
1064                         if self.extensionKeys.has_key(key):
1065                                 key = None
1066                 
1067                 if key is None:
1068                         for x in self.availableKeys:
1069                                 if not self.extensionKeys.has_key(x):
1070                                         key = x
1071                                         break
1072
1073                 if key is not None:
1074                         self.extensionKeys[key] = len(self.extensionsList) - 1
1075                         
1076         def updateExtensions(self):
1077                 self.extensionsList = []
1078                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1079                 self.extensionKeys = {}
1080                 for x in self.list:
1081                         if x[0] == self.EXTENSION_SINGLE:
1082                                 self.updateExtension(x[1], x[2])
1083                         else:
1084                                 for y in x[1]():
1085                                         self.updateExtension(y[0], y[1])
1086
1087
1088         def showExtensionSelection(self):
1089                 self.updateExtensions()
1090                 extensionsList = self.extensionsList[:]
1091                 keys = []
1092                 list = []
1093                 for x in self.availableKeys:
1094                         if self.extensionKeys.has_key(x):
1095                                 entry = self.extensionKeys[x]
1096                                 extension = self.extensionsList[entry]
1097                                 if extension[2]():
1098                                         name = str(extension[0]())
1099                                         list.append((extension[0](), extension))
1100                                         keys.append(x)
1101                                         extensionsList.remove(extension)
1102                                 else:
1103                                         extensionsList.remove(extension)
1104                 for x in extensionsList:
1105                         list.append((x[0](), x))
1106                 keys += [""] * len(extensionsList)
1107                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys)
1108
1109         def extensionCallback(self, answer):
1110                 if answer is not None:
1111                         answer[1][1]()
1112
1113 from Tools.BoundFunction import boundFunction
1114
1115 # depends on InfoBarExtensions
1116 from Components.PluginComponent import plugins
1117
1118 class InfoBarPlugins:
1119         def __init__(self):
1120                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1121                 
1122         def getPluginName(self, name):
1123                 return name
1124                 
1125         def getPluginList(self):
1126                 list = []
1127                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1128                         list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
1129                 return list
1130
1131         def runPlugin(self, plugin):
1132                 plugin(session = self.session)
1133
1134 # depends on InfoBarExtensions
1135 class InfoBarSleepTimer:
1136         def __init__(self):
1137                 self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, self.available), "1")      
1138                 
1139         def available(self):
1140                 return True
1141
1142         def getSleepTimerName(self):
1143                 return _("Sleep Timer")
1144
1145         def showSleepTimerSetup(self):
1146                 self.session.open(SleepTimerEdit)
1147
1148 # depends on InfoBarExtensions
1149 class InfoBarPiP:
1150         def __init__(self):
1151                 self.session.pipshown = False
1152
1153                 self.addExtension((self.getShowHideName, self.showPiP, self.available), "blue")
1154                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1155                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1156         
1157         def available(self):
1158                 return True
1159         
1160         def pipShown(self):
1161                 return self.session.pipshown
1162         
1163         def getShowHideName(self):
1164                 if self.session.pipshown:
1165                         return _("Disable Picture in Picture")
1166                 else:
1167                         return _("Activate Picture in Picture")
1168                 
1169         def getSwapName(self):
1170                 return _("Swap Services")
1171                 
1172         def getMoveName(self):
1173                 return _("Move Picture in Picture")
1174         
1175         def showPiP(self):
1176                 if self.session.pipshown:
1177                         del self.session.pip
1178                         self.session.pipshown = False
1179                 else:
1180                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1181                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1182                         if self.session.pip.playService(newservice):
1183                                 self.session.pipshown = True
1184                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1185                         else:
1186                                 self.session.pipshown = False
1187                                 del self.session.pip
1188                         self.session.nav.playService(newservice)
1189         
1190         def swapPiP(self):
1191                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1192                 if self.session.pip.servicePath:
1193                         servicepath = self.servicelist.getCurrentServicePath()
1194                         ref=servicepath[len(servicepath)-1]
1195                         pipref=self.session.pip.getCurrentService()
1196                         self.session.pip.playService(swapservice)
1197                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1198                         if pipref.toString() != ref.toString(): # is a subservice ?
1199                                 self.session.nav.stopService() # stop portal
1200                                 self.session.nav.playService(pipref) # start subservice
1201                         self.session.pip.servicePath=servicepath
1202         
1203         def movePiP(self):
1204                 self.session.open(PiPSetup, pip = self.session.pip)
1205
1206 from RecordTimer import parseEvent
1207
1208 class InfoBarInstantRecord:
1209         """Instant Record - handles the instantRecord action in order to 
1210         start/stop instant records"""
1211         def __init__(self):
1212                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1213                         {
1214                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1215                         })
1216                 self.recording = []
1217                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1218                 self["BlinkingPoint"].hide()
1219                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1220
1221         def stopCurrentRecording(self, entry = -1):     
1222                 if entry is not None and entry != -1:
1223                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1224                         self.recording.remove(self.recording[entry])
1225
1226         def startInstantRecording(self, limitEvent = False):
1227                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1228                 
1229                 # try to get event info
1230                 event = None
1231                 try:
1232                         service = self.session.nav.getCurrentService()
1233                         epg = eEPGCache.getInstance()
1234                         event = epg.lookupEventTime(serviceref, -1, 0)
1235                         if event is None:
1236                                 info = service.info()
1237                                 ev = info.getEvent(0)
1238                                 event = ev
1239                 except:
1240                         pass
1241
1242                 begin = time()
1243                 end = time() + 3600 * 10
1244                 name = "instant record"
1245                 description = ""
1246                 eventid = None
1247                 
1248                 if event is not None:
1249                         curEvent = parseEvent(event)
1250                         name = curEvent[2]
1251                         description = curEvent[3]
1252                         eventid = curEvent[4]
1253                         if limitEvent:
1254                                 end = curEvent[1]
1255                 else:
1256                         if limitEvent:
1257                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1258                                 
1259                 data = (begin, end, name, description, eventid)
1260                 
1261                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1262                 recording.dontSave = True
1263                 self.recording.append(recording)
1264                 
1265                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1266                 
1267         def isInstantRecordRunning(self):
1268                 print "self.recording:", self.recording
1269                 if len(self.recording) > 0:
1270                         for x in self.recording:
1271                                 if x.isRunning():
1272                                         return True
1273                 return False
1274
1275         def recordQuestionCallback(self, answer):
1276                 print "pre:\n", self.recording
1277                 
1278                 if answer is None or answer[1] == "no":
1279                         return
1280                 list = []
1281                 recording = self.recording[:]
1282                 for x in recording:
1283                         if not x in self.session.nav.RecordTimer.timer_list:
1284                                 self.recording.remove(x)
1285                         elif x.dontSave and x.isRunning():
1286                                 list.append(TimerEntryComponent(x, False))              
1287
1288                 if answer[1] == "changeduration":
1289                         if len(self.recording) == 1:
1290                                 self.changeDuration(0)
1291                         else:
1292                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1293                 elif answer[1] == "stop":
1294                         if len(self.recording) == 1:
1295                                 self.stopCurrentRecording(0)
1296                         else:
1297                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1298                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1299                         limitEvent = False
1300                         if answer[1] == "event":
1301                                 limitEvent = True
1302                         if answer[1] == "manualduration":
1303                                 self.selectedEntry = len(self.recording)
1304                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1305                         self.startInstantRecording(limitEvent = limitEvent)
1306                         
1307                 print "after:\n", self.recording
1308
1309         def changeDuration(self, entry):
1310                 if entry is not None:
1311                         self.selectedEntry = entry
1312                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1313
1314         def inputCallback(self, value):
1315                 if value is not None:
1316                         print "stopping recording after", int(value), "minutes."
1317                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1318                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1319
1320         def instantRecord(self):
1321                 try:
1322                         stat = os_stat(resolveFilename(SCOPE_HDD))
1323                 except:
1324                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1325                         return
1326
1327                 if self.isInstantRecordRunning():
1328                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("A recording is currently running.\nWhat do you want to do?"), list=[(_("stop recording"), "stop"), (_("change recording (duration)"), "changeduration"), (_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"), (_("do nothing"), "no")])
1329                 else:
1330                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Start recording?"), list=[(_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"),(_("don't record"), "no")])
1331
1332 from Tools.ISO639 import LanguageCodes
1333
1334 class InfoBarAudioSelection:
1335         def __init__(self):
1336                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1337                         {
1338                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1339                         })
1340
1341         def audioSelection(self):
1342                 service = self.session.nav.getCurrentService()
1343                 audio = service and service.audioTracks()
1344                 self.audioTracks = audio
1345                 n = audio and audio.getNumberOfTracks() or 0
1346                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1347                 tlist = []
1348                 print "tlist:", tlist
1349                 if n > 0:
1350                         self.audioChannel = service.audioChannel()
1351
1352                         for x in range(n):
1353                                 i = audio.getTrackInfo(x)
1354                                 language = i.getLanguage()
1355                                 description = i.getDescription()
1356         
1357                                 if LanguageCodes.has_key(language):
1358                                         language = LanguageCodes[language][0]
1359         
1360                                 if len(description):
1361                                         description += " (" + language + ")"
1362                                 else:
1363                                         description = language
1364         
1365                                 tlist.append((description, x))
1366                         
1367                         selectedAudio = tlist[0][1]
1368                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1369
1370                         selection = 2
1371                         for x in tlist:
1372                                 if x[1] != selectedAudio:
1373                                         selection += 1
1374                                 else:
1375                                         break
1376
1377                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1378                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1379                 else:
1380                         del self.audioTracks
1381
1382         def audioSelected(self, audio):
1383                 if audio is not None:
1384                         if isinstance(audio[1], str):
1385                                 if audio[1] == "mode":
1386                                         keys = ["red", "green", "yellow"]
1387                                         selection = self.audioChannel.getCurrentChannel()
1388                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1389                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1390                         else:
1391                                 del self.audioChannel
1392                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1393                                         self.audioTracks.selectTrack(audio[1])
1394                 else:
1395                         del self.audioChannel
1396                 del self.audioTracks
1397
1398         def modeSelected(self, mode):
1399                 if mode is not None:
1400                         self.audioChannel.selectChannel(mode[1])
1401                 del self.audioChannel
1402
1403 class InfoBarSubserviceSelection:
1404         def __init__(self):
1405                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1406                         {
1407                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1408                         })
1409
1410                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1411                         {
1412                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1413                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1414                         }, -1)
1415                 self["SubserviceQuickzapAction"].setEnabled(False)
1416
1417                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1418
1419                 self.bsel = None
1420
1421         def checkSubservicesAvail(self, ev):
1422                 if ev == iPlayableService.evUpdatedEventInfo:
1423                         service = self.session.nav.getCurrentService()
1424                         subservices = service and service.subServices()
1425                         if not subservices or subservices.getNumberOfSubservices() == 0:
1426                                 self["SubserviceQuickzapAction"].setEnabled(False)
1427
1428         def nextSubservice(self):
1429                 self.changeSubservice(+1)
1430
1431         def prevSubservice(self):
1432                 self.changeSubservice(-1)
1433
1434         def changeSubservice(self, direction):
1435                 service = self.session.nav.getCurrentService()
1436                 subservices = service and service.subServices()
1437                 n = subservices and subservices.getNumberOfSubservices()
1438                 if n and n > 0:
1439                         selection = -1
1440                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1441                         for x in range(n):
1442                                 if subservices.getSubservice(x).toString() == ref.toString():
1443                                         selection = x
1444                         if selection != -1:
1445                                 selection += direction
1446                                 if selection >= n:
1447                                         selection=0
1448                                 elif selection < 0:
1449                                         selection=n-1
1450                                 newservice = subservices.getSubservice(selection)
1451                                 if newservice.valid():
1452                                         del subservices
1453                                         del service
1454                                         if config.usage.show_infobar_on_zap.value:
1455                                                 self.doShow()
1456                                         self.session.nav.playService(newservice)
1457
1458         def subserviceSelection(self):
1459                 service = self.session.nav.getCurrentService()
1460                 subservices = service and service.subServices()
1461                 self.bouquets = self.servicelist.getBouquetList()
1462                 n = subservices and subservices.getNumberOfSubservices()
1463                 selection = 0
1464                 if n and n > 0:
1465                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1466                         tlist = []
1467                         for x in range(n):
1468                                 i = subservices.getSubservice(x)
1469                                 if i.toString() == ref.toString():
1470                                         selection = x
1471                                 tlist.append((i.getName(), i))
1472
1473                         if self.bouquets and len(self.bouquets):
1474                                 keys = ["red", "green", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1475                                 if config.usage.multibouquet.value:
1476                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1477                                 else:
1478                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1479                                 selection += 3
1480                         else:
1481                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1482                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1483                                 selection += 2
1484
1485                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
1486
1487         def subserviceSelected(self, service):
1488                 del self.bouquets
1489                 if not service is None:
1490                         if isinstance(service[1], str):
1491                                 if service[1] == "quickzap":
1492                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1493                                         self.session.open(SubservicesQuickzap, service[2])
1494                         else:
1495                                 self["SubserviceQuickzapAction"].setEnabled(True)
1496                                 if config.usage.show_infobar_on_zap.value:
1497                                         self.doShow()
1498                                 self.session.nav.playService(service[1])
1499
1500         def addSubserviceToBouquetCallback(self, service):
1501                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1502                         self.selectedSubservice = service
1503                         if self.bouquets is None:
1504                                 cnt = 0
1505                         else:
1506                                 cnt = len(self.bouquets)
1507                         if cnt > 1: # show bouquet list
1508                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1509                         elif cnt == 1: # add to only one existing bouquet
1510                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1511                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1512
1513         def bouquetSelClosed(self, confirmed):
1514                 self.bsel = None
1515                 del self.selectedSubservice
1516                 if confirmed:
1517                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1518
1519         def addSubserviceToBouquet(self, dest):
1520                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1521                 if self.bsel:
1522                         self.bsel.close(True)
1523                 else:
1524                         del self.selectedSubservice
1525
1526 class InfoBarAdditionalInfo:
1527         def __init__(self):
1528                 self["NimA"] = Pixmap()
1529                 self["NimB"] = Pixmap()
1530                 self["NimA_Active"] = Pixmap()
1531                 self["NimB_Active"] = Pixmap()
1532
1533                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1534                 self["TimeshiftPossible"] = self["RecordingPossible"]
1535                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1536
1537                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1538                 res_mgr = eDVBResourceManager.getInstance()
1539                 if res_mgr:
1540                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1541
1542         def tunerUseMaskChanged(self, mask):
1543                 if mask&1:
1544                         self["NimA_Active"].show()
1545                 else:
1546                         self["NimA_Active"].hide()
1547                 if mask&2:
1548                         self["NimB_Active"].show()
1549                 else:
1550                         self["NimB_Active"].hide()
1551
1552         def checkTunerState(self, service):
1553                 info = service.frontendInfo()
1554                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1555                 if feNumber is None:
1556                         self["NimA"].hide()
1557                         self["NimB"].hide()
1558                 elif feNumber == 0:
1559                         self["NimB"].hide()
1560                         self["NimA"].show()
1561                 elif feNumber == 1:
1562                         self["NimA"].hide()
1563                         self["NimB"].show()
1564
1565         def gotServiceEvent(self, ev):
1566                 service = self.session.nav.getCurrentService()
1567                 if ev == iPlayableService.evStart:
1568                         self.checkTunerState(service)
1569
1570 class InfoBarNotifications:
1571         def __init__(self):
1572                 self.onExecBegin.append(self.checkNotifications)
1573                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1574                 self.onClose.append(self.__removeNotification)
1575         
1576         def __removeNotification(self):
1577                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1578         
1579         def checkNotificationsIfExecing(self):
1580                 if self.execing:
1581                         self.checkNotifications()
1582
1583         def checkNotifications(self):
1584                 if len(Notifications.notifications):
1585                         n = Notifications.notifications[0]
1586                         
1587                         Notifications.notifications = Notifications.notifications[1:]
1588                         cb = n[0]
1589                         if cb is not None:
1590                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1591                         else:
1592                                 dlg = self.session.open(n[1], *n[2], **n[3])
1593                         
1594                         # remember that this notification is currently active
1595                         d = (n[4], dlg)
1596                         Notifications.current_notifications.append(d)
1597                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1598
1599         def __notificationClosed(self, d):
1600                 Notifications.current_notifications.remove(d)
1601
1602 class InfoBarServiceNotifications:
1603         def __init__(self):
1604                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1605                         {
1606                                 iPlayableService.evEnd: self.serviceHasEnded
1607                         })
1608
1609         def serviceHasEnded(self):
1610                 print "service end!"
1611
1612                 try:
1613                         self.setSeekState(self.SEEK_STATE_PLAY)
1614                 except:
1615                         pass
1616
1617 class InfoBarCueSheetSupport:
1618         CUT_TYPE_IN = 0
1619         CUT_TYPE_OUT = 1
1620         CUT_TYPE_MARK = 2
1621         CUT_TYPE_LAST = 3
1622         
1623         ENABLE_RESUME_SUPPORT = False
1624         
1625         def __init__(self):
1626                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1627                         {
1628                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1629                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1630                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1631                         }, prio=1) 
1632                 
1633                 self.cut_list = [ ]
1634                 self.is_closing = False
1635                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1636                         {
1637                                 iPlayableService.evStart: self.__serviceStarted,
1638                         })
1639
1640         def __serviceStarted(self):
1641                 if self.is_closing:
1642                         return
1643                 print "new service started! trying to download cuts!"
1644                 self.downloadCuesheet()
1645                 
1646                 if self.ENABLE_RESUME_SUPPORT:
1647                         last = None
1648                         
1649                         for (pts, what) in self.cut_list:
1650                                 if what == self.CUT_TYPE_LAST:
1651                                         last = pts
1652                         
1653                         if last is not None:
1654                                 self.resume_point = last
1655                                 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1656
1657         def playLastCB(self, answer):
1658                 if answer == True:
1659                         seekable = self.__getSeekable()
1660                         if seekable is not None:
1661                                 seekable.seekTo(self.resume_point)
1662
1663         def __getSeekable(self):
1664                 service = self.session.nav.getCurrentService()
1665                 if service is None:
1666                         return None
1667                 return service.seek()
1668
1669         def cueGetCurrentPosition(self):
1670                 seek = self.__getSeekable()
1671                 if seek is None:
1672                         return None
1673                 r = seek.getPlayPosition()
1674                 if r[0]:
1675                         return None
1676                 return long(r[1])
1677
1678         def jumpPreviousNextMark(self, cmp, alternative=None):
1679                 current_pos = self.cueGetCurrentPosition()
1680                 if current_pos is None:
1681                         return
1682                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1683                 if mark is not None:
1684                         pts = mark[0]
1685                 elif alternative is not None:
1686                         pts = alternative
1687                 else:
1688                         return
1689
1690                 seekable = self.__getSeekable()
1691                 if seekable is not None:
1692                         seekable.seekTo(pts)
1693
1694         def jumpPreviousMark(self):
1695                 # we add 2 seconds, so if the play position is <2s after
1696                 # the mark, the mark before will be used
1697                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1698
1699         def jumpNextMark(self):
1700                 self.jumpPreviousNextMark(lambda x: x)
1701
1702         def getNearestCutPoint(self, pts, cmp=abs):
1703                 # can be optimized
1704                 nearest = None
1705                 for cp in self.cut_list:
1706                         diff = cmp(cp[0] - pts)
1707                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1708                                 nearest = cp
1709                 return nearest
1710
1711         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1712                 current_pos = self.cueGetCurrentPosition()
1713                 if current_pos is None:
1714                         print "not seekable"
1715                         return
1716                 
1717                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1718                 
1719                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1720                         if onlyreturn:
1721                                 return nearest_cutpoint
1722                         if not onlyadd:
1723                                 self.removeMark(nearest_cutpoint)
1724                 elif not onlyremove and not onlyreturn:
1725                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1726                 
1727                 if onlyreturn:
1728                         return None
1729
1730         def addMark(self, point):
1731                 insort(self.cut_list, point)
1732                 self.uploadCuesheet()
1733
1734         def removeMark(self, point):
1735                 self.cut_list.remove(point)
1736                 self.uploadCuesheet()
1737
1738         def __getCuesheet(self):
1739                 service = self.session.nav.getCurrentService()
1740                 if service is None:
1741                         return None
1742                 return service.cueSheet()
1743
1744         def uploadCuesheet(self):
1745                 cue = self.__getCuesheet()
1746
1747                 if cue is None:
1748                         print "upload failed, no cuesheet interface"
1749                         return
1750                 cue.setCutList(self.cut_list)
1751
1752         def downloadCuesheet(self):
1753                 cue = self.__getCuesheet()
1754
1755                 if cue is None:
1756                         print "upload failed, no cuesheet interface"
1757                         return
1758                 self.cut_list = cue.getCutList()
1759
1760 class InfoBarSummary(Screen):
1761         skin = """
1762         <screen position="0,0" size="132,64">
1763                 <widget source="CurrentTime" render="Label" position="56,46" size="82,18" font="Regular;16" >
1764                         <convert type="ClockToText">WithSeconds</convert>
1765                 </widget>
1766                 <widget source="CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
1767                         <convert type="ServiceName">Name</convert>
1768                 </widget>
1769         </screen>"""
1770
1771         def __init__(self, session, parent):
1772                 Screen.__init__(self, session)
1773                 self["CurrentService"] = CurrentService(self.session.nav)
1774                 self["CurrentTime"] = Clock()
1775
1776 class InfoBarSummarySupport:
1777         def __init__(self):
1778                 pass
1779         
1780         def createSummary(self):
1781                 return InfoBarSummary
1782
1783 class InfoBarTeletextPlugin:
1784         def __init__(self):
1785                 self.teletext_plugin = None
1786                 
1787                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1788                         self.teletext_plugin = p
1789                 
1790                 if self.teletext_plugin is not None:
1791                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1792                                 {
1793                                         "startTeletext": (self.startTeletext, _("View teletext..."))
1794                                 })
1795                 else:
1796                         print "no teletext plugin found!"
1797
1798         def startTeletext(self):
1799                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1800
1801 class InfoBarSubtitleSupport(object):
1802         def __init__(self):
1803                 object.__init__(self)
1804                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1805                 self.__subtitles_enabled = False
1806
1807                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1808                         {
1809                                 iPlayableService.evEnd: self.__serviceStopped,
1810                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
1811                         })
1812                 self.cached_subtitle_checked = False
1813
1814         def __serviceStopped(self):
1815                 self.subtitle_window.hide()
1816                 self.__subtitles_enabled = False
1817                 self.cached_subtitle_checked = False
1818
1819         def __updatedInfo(self):
1820                 if not self.cached_subtitle_checked:
1821                         subtitle = self.getCurrentServiceSubtitle()
1822                         self.cached_subtitle_checked = True
1823                         if subtitle:
1824                                 self.__selected_subtitle = subtitle.getCachedSubtitle()
1825                         if self.__selected_subtitle:
1826                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1827                                 self.subtitle_window.show()
1828                                 self.__subtitles_enabled = True
1829
1830         def getCurrentServiceSubtitle(self):
1831                 service = self.session.nav.getCurrentService()
1832                 return service and service.subtitle()
1833         
1834         def setSubtitlesEnable(self, enable=True):
1835                 subtitle = self.getCurrentServiceSubtitle()
1836                 if enable and self.__selected_subtitle is not None:
1837                         if subtitle and not self.__subtitles_enabled:
1838                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1839                                 self.subtitle_window.show()
1840                                 self.__subtitles_enabled = True
1841                 else:
1842                         if subtitle:
1843                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1844                         self.__subtitles_enabled = False
1845                         self.subtitle_window.hide()
1846
1847         def setSelectedSubtitle(self, subtitle):
1848                 self.__selected_subtitle = subtitle
1849
1850         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
1851         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
1852
1853 class InfoBarServiceErrorPopupSupport:
1854         def __init__(self):
1855                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1856                         {
1857                                 iPlayableService.evTuneFailed: self.__tuneFailed,
1858                                 iPlayableService.evStart: self.__serviceStarted
1859                         })
1860                 self.__serviceStarted()
1861
1862         def __serviceStarted(self):
1863                 self.last_error = None
1864                 Notifications.RemovePopup(id = "ZapError")
1865
1866         def __tuneFailed(self):
1867                 service = self.session.nav.getCurrentService()
1868                 info = service and service.info()
1869                 error = info and info.getInfo(iServiceInformation.sDVBState)
1870                 
1871                 if error == self.last_error:
1872                         error = None
1873                 else:
1874                         self.last_error = error
1875
1876                 errors = {
1877                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
1878                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
1879                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
1880                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
1881                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
1882                         eDVBServicePMTHandler.eventNewProgramInfo: None,
1883                         eDVBServicePMTHandler.eventTuned: None,
1884                         eDVBServicePMTHandler.eventSOF: None,
1885                         eDVBServicePMTHandler.eventEOF: None
1886                 }
1887                 
1888                 if error not in errors:
1889                         error = None
1890
1891                 error = error and errors[error]
1892                 
1893                 if error is not None:
1894                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
1895                 else:
1896                         Notifications.RemovePopup(id = "ZapError")