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