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