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