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