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