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