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