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