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