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