fix PIP
[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                         self.session.pip.show()
1233                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1234                         if self.session.pip.playService(newservice):
1235                                 self.session.pipshown = True
1236                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1237                         else:
1238                                 self.session.pipshown = False
1239                                 del self.session.pip
1240                         self.session.nav.playService(newservice)
1241
1242         def swapPiP(self):
1243                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1244                 if self.session.pip.servicePath:
1245                         servicepath = self.servicelist.getCurrentServicePath()
1246                         ref=servicepath[len(servicepath)-1]
1247                         pipref=self.session.pip.getCurrentService()
1248                         self.session.pip.playService(swapservice)
1249                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1250                         if pipref.toString() != ref.toString(): # is a subservice ?
1251                                 self.session.nav.stopService() # stop portal
1252                                 self.session.nav.playService(pipref) # start subservice
1253                         self.session.pip.servicePath=servicepath
1254
1255         def movePiP(self):
1256                 self.session.open(PiPSetup, pip = self.session.pip)
1257
1258 from RecordTimer import parseEvent
1259
1260 class InfoBarInstantRecord:
1261         """Instant Record - handles the instantRecord action in order to 
1262         start/stop instant records"""
1263         def __init__(self):
1264                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1265                         {
1266                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1267                         })
1268                 self.recording = []
1269                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1270                 self["BlinkingPoint"].hide()
1271                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1272
1273         def stopCurrentRecording(self, entry = -1):
1274                 if entry is not None and entry != -1:
1275                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1276                         self.recording.remove(self.recording[entry])
1277
1278         def startInstantRecording(self, limitEvent = False):
1279                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1280
1281                 # try to get event info
1282                 event = None
1283                 try:
1284                         service = self.session.nav.getCurrentService()
1285                         epg = eEPGCache.getInstance()
1286                         event = epg.lookupEventTime(serviceref, -1, 0)
1287                         if event is None:
1288                                 info = service.info()
1289                                 ev = info.getEvent(0)
1290                                 event = ev
1291                 except:
1292                         pass
1293
1294                 begin = time()
1295                 end = time() + 3600 * 10
1296                 name = "instant record"
1297                 description = ""
1298                 eventid = None
1299
1300                 if event is not None:
1301                         curEvent = parseEvent(event)
1302                         name = curEvent[2]
1303                         description = curEvent[3]
1304                         eventid = curEvent[4]
1305                         if limitEvent:
1306                                 end = curEvent[1]
1307                 else:
1308                         if limitEvent:
1309                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1310
1311                 data = (begin, end, name, description, eventid)
1312
1313                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1314                 recording.dontSave = True
1315                 self.recording.append(recording)
1316
1317                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1318
1319         def isInstantRecordRunning(self):
1320                 print "self.recording:", self.recording
1321                 if len(self.recording) > 0:
1322                         for x in self.recording:
1323                                 if x.isRunning():
1324                                         return True
1325                 return False
1326
1327         def recordQuestionCallback(self, answer):
1328                 print "pre:\n", self.recording
1329
1330                 if answer is None or answer[1] == "no":
1331                         return
1332                 list = []
1333                 recording = self.recording[:]
1334                 for x in recording:
1335                         if not x in self.session.nav.RecordTimer.timer_list:
1336                                 self.recording.remove(x)
1337                         elif x.dontSave and x.isRunning():
1338                                 list.append(TimerEntryComponent(x, False))
1339
1340                 if answer[1] == "changeduration":
1341                         if len(self.recording) == 1:
1342                                 self.changeDuration(0)
1343                         else:
1344                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1345                 elif answer[1] == "changeendtime":
1346                         if len(self.recording) == 1:
1347                                 self.setEndtime(0)
1348                         else:
1349                                 self.session.openWithCallback(self.setEndTime, TimerSelection, list)
1350                 elif answer[1] == "stop":
1351                         if len(self.recording) == 1:
1352                                 self.stopCurrentRecording(0)
1353                         else:
1354                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1355                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1356                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1357                         if answer[1] == "manualduration":
1358                                 self.changeDuration(len(self.recording)-1)
1359                         elif answer[1] == "manualendtime":
1360                                 self.setEndtime(len(self.recording)-1)
1361                 print "after:\n", self.recording
1362
1363         def setEndtime(self, entry):
1364                 if entry is not None:
1365                         self.selectedEntry = entry
1366                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1367                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1368                         dlg.setTitle(_("Please change recording endtime"))
1369
1370         def TimeDateInputClosed(self, ret):
1371                 if len(ret) > 1:
1372                         if ret[0]:
1373                                 localendtime = localtime(ret[1])
1374                                 print "stopping recording at", strftime("%c", localendtime)
1375                                 self.recording[self.selectedEntry].end = ret[1]
1376                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1377
1378         def changeDuration(self, entry):
1379                 if entry is not None:
1380                         self.selectedEntry = entry
1381                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1382
1383         def inputCallback(self, value):
1384                 if value is not None:
1385                         print "stopping recording after", int(value), "minutes."
1386                         self.recording[self.selectedEntry].end = time() + 60 * int(value)
1387                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1388
1389         def instantRecord(self):
1390                 try:
1391                         stat = os_stat(resolveFilename(SCOPE_HDD))
1392                 except:
1393                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1394                         return
1395
1396                 if self.isInstantRecordRunning():
1397                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1398                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1399                                 list=[(_("stop recording"), "stop"), \
1400                                 (_("change recording (duration)"), "changeduration"), \
1401                                 (_("change recording (endtime)"), "changeendtime"), \
1402                                 (_("add recording (indefinitely)"), "indefinitely"), \
1403                                 (_("add recording (stop after current event)"), "event"), \
1404                                 (_("add recording (enter recording duration)"), "manualduration"), \
1405                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1406                                 (_("do nothing"), "no")])
1407                 else:
1408                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1409                                 title=_("Start recording?"), \
1410                                 list=[(_("add recording (indefinitely)"), "indefinitely"), \
1411                                 (_("add recording (stop after current event)"), "event"), \
1412                                 (_("add recording (enter recording duration)"), "manualduration"), \
1413                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1414                                 (_("don't record"), "no")])
1415
1416 from Tools.ISO639 import LanguageCodes
1417
1418 class InfoBarAudioSelection:
1419         def __init__(self):
1420                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1421                         {
1422                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1423                         })
1424
1425         def audioSelection(self):
1426                 service = self.session.nav.getCurrentService()
1427                 audio = service and service.audioTracks()
1428                 self.audioTracks = audio
1429                 n = audio and audio.getNumberOfTracks() or 0
1430                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1431                 tlist = []
1432                 print "tlist:", tlist
1433                 if n > 0:
1434                         self.audioChannel = service.audioChannel()
1435
1436                         for x in range(n):
1437                                 i = audio.getTrackInfo(x)
1438                                 language = i.getLanguage()
1439                                 description = i.getDescription()
1440
1441                                 if LanguageCodes.has_key(language):
1442                                         language = LanguageCodes[language][0]
1443
1444                                 if len(description):
1445                                         description += " (" + language + ")"
1446                                 else:
1447                                         description = language
1448
1449                                 tlist.append((description, x))
1450
1451                         selectedAudio = tlist[0][1]
1452                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1453
1454                         selection = 2
1455                         for x in tlist:
1456                                 if x[1] != selectedAudio:
1457                                         selection += 1
1458                                 else:
1459                                         break
1460
1461                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1462                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1463                 else:
1464                         del self.audioTracks
1465
1466         def audioSelected(self, audio):
1467                 if audio is not None:
1468                         if isinstance(audio[1], str):
1469                                 if audio[1] == "mode":
1470                                         keys = ["red", "green", "yellow"]
1471                                         selection = self.audioChannel.getCurrentChannel()
1472                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1473                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1474                         else:
1475                                 del self.audioChannel
1476                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1477                                         self.audioTracks.selectTrack(audio[1])
1478                 else:
1479                         del self.audioChannel
1480                 del self.audioTracks
1481
1482         def modeSelected(self, mode):
1483                 if mode is not None:
1484                         self.audioChannel.selectChannel(mode[1])
1485                 del self.audioChannel
1486
1487 class InfoBarSubserviceSelection:
1488         def __init__(self):
1489                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1490                         {
1491                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1492                         })
1493
1494                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1495                         {
1496                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1497                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1498                         }, -1)
1499                 self["SubserviceQuickzapAction"].setEnabled(False)
1500
1501                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1502
1503                 self.bsel = None
1504
1505         def checkSubservicesAvail(self, ev):
1506                 if ev == iPlayableService.evUpdatedEventInfo:
1507                         service = self.session.nav.getCurrentService()
1508                         subservices = service and service.subServices()
1509                         if not subservices or subservices.getNumberOfSubservices() == 0:
1510                                 self["SubserviceQuickzapAction"].setEnabled(False)
1511
1512         def nextSubservice(self):
1513                 self.changeSubservice(+1)
1514
1515         def prevSubservice(self):
1516                 self.changeSubservice(-1)
1517
1518         def changeSubservice(self, direction):
1519                 service = self.session.nav.getCurrentService()
1520                 subservices = service and service.subServices()
1521                 n = subservices and subservices.getNumberOfSubservices()
1522                 if n and n > 0:
1523                         selection = -1
1524                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1525                         for x in range(n):
1526                                 if subservices.getSubservice(x).toString() == ref.toString():
1527                                         selection = x
1528                         if selection != -1:
1529                                 selection += direction
1530                                 if selection >= n:
1531                                         selection=0
1532                                 elif selection < 0:
1533                                         selection=n-1
1534                                 newservice = subservices.getSubservice(selection)
1535                                 if newservice.valid():
1536                                         del subservices
1537                                         del service
1538                                         self.session.nav.playService(newservice)
1539
1540         def subserviceSelection(self):
1541                 service = self.session.nav.getCurrentService()
1542                 subservices = service and service.subServices()
1543                 self.bouquets = self.servicelist.getBouquetList()
1544                 n = subservices and subservices.getNumberOfSubservices()
1545                 selection = 0
1546                 if n and n > 0:
1547                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1548                         tlist = []
1549                         for x in range(n):
1550                                 i = subservices.getSubservice(x)
1551                                 if i.toString() == ref.toString():
1552                                         selection = x
1553                                 tlist.append((i.getName(), i))
1554
1555                         if self.bouquets and len(self.bouquets):
1556                                 keys = ["red", "green", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1557                                 if config.usage.multibouquet.value:
1558                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1559                                 else:
1560                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1561                                 selection += 3
1562                         else:
1563                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1564                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1565                                 selection += 2
1566
1567                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
1568
1569         def subserviceSelected(self, service):
1570                 del self.bouquets
1571                 if not service is None:
1572                         if isinstance(service[1], str):
1573                                 if service[1] == "quickzap":
1574                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1575                                         self.session.open(SubservicesQuickzap, service[2])
1576                         else:
1577                                 self["SubserviceQuickzapAction"].setEnabled(True)
1578                                 self.session.nav.playService(service[1])
1579
1580         def addSubserviceToBouquetCallback(self, service):
1581                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1582                         self.selectedSubservice = service
1583                         if self.bouquets is None:
1584                                 cnt = 0
1585                         else:
1586                                 cnt = len(self.bouquets)
1587                         if cnt > 1: # show bouquet list
1588                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1589                         elif cnt == 1: # add to only one existing bouquet
1590                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1591                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1592
1593         def bouquetSelClosed(self, confirmed):
1594                 self.bsel = None
1595                 del self.selectedSubservice
1596                 if confirmed:
1597                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1598
1599         def addSubserviceToBouquet(self, dest):
1600                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1601                 if self.bsel:
1602                         self.bsel.close(True)
1603                 else:
1604                         del self.selectedSubservice
1605
1606 class InfoBarAdditionalInfo:
1607         def __init__(self):
1608                 self["NimA"] = Pixmap()
1609                 self["NimB"] = Pixmap()
1610                 self["NimA_Active"] = Pixmap()
1611                 self["NimB_Active"] = Pixmap()
1612
1613                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1614                 self["TimeshiftPossible"] = self["RecordingPossible"]
1615                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1616
1617                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1618                 res_mgr = eDVBResourceManager.getInstance()
1619                 if res_mgr:
1620                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1621
1622         def tunerUseMaskChanged(self, mask):
1623                 if mask&1:
1624                         self["NimA_Active"].show()
1625                 else:
1626                         self["NimA_Active"].hide()
1627                 if mask&2:
1628                         self["NimB_Active"].show()
1629                 else:
1630                         self["NimB_Active"].hide()
1631
1632         def checkTunerState(self, service):
1633                 info = service and service.frontendInfo()
1634                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1635                 if feNumber is None:
1636                         self["NimA"].hide()
1637                         self["NimB"].hide()
1638                 elif feNumber == 0:
1639                         self["NimB"].hide()
1640                         self["NimA"].show()
1641                 elif feNumber == 1:
1642                         self["NimA"].hide()
1643                         self["NimB"].show()
1644
1645         def gotServiceEvent(self, ev):
1646                 service = self.session.nav.getCurrentService()
1647                 if ev == iPlayableService.evUpdatedInfo or ev == iPlayableService.evEnd:
1648                         self.checkTunerState(service)
1649
1650 class InfoBarNotifications:
1651         def __init__(self):
1652                 self.onExecBegin.append(self.checkNotifications)
1653                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1654                 self.onClose.append(self.__removeNotification)
1655
1656         def __removeNotification(self):
1657                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1658
1659         def checkNotificationsIfExecing(self):
1660                 if self.execing:
1661                         self.checkNotifications()
1662
1663         def checkNotifications(self):
1664                 if len(Notifications.notifications):
1665                         n = Notifications.notifications[0]
1666
1667                         Notifications.notifications = Notifications.notifications[1:]
1668                         cb = n[0]
1669
1670                         if n[3].has_key("onSessionOpenCallback"):
1671                                 n[3]["onSessionOpenCallback"]()
1672                                 del n[3]["onSessionOpenCallback"]
1673
1674                         if cb is not None:
1675                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1676                         else:
1677                                 dlg = self.session.open(n[1], *n[2], **n[3])
1678
1679                         # remember that this notification is currently active
1680                         d = (n[4], dlg)
1681                         Notifications.current_notifications.append(d)
1682                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1683
1684         def __notificationClosed(self, d):
1685                 Notifications.current_notifications.remove(d)
1686
1687 class InfoBarServiceNotifications:
1688         def __init__(self):
1689                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1690                         {
1691                                 iPlayableService.evEnd: self.serviceHasEnded
1692                         })
1693
1694         def serviceHasEnded(self):
1695                 print "service end!"
1696
1697                 try:
1698                         self.setSeekState(self.SEEK_STATE_PLAY)
1699                 except:
1700                         pass
1701
1702 class InfoBarCueSheetSupport:
1703         CUT_TYPE_IN = 0
1704         CUT_TYPE_OUT = 1
1705         CUT_TYPE_MARK = 2
1706         CUT_TYPE_LAST = 3
1707
1708         ENABLE_RESUME_SUPPORT = False
1709
1710         def __init__(self):
1711                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1712                         {
1713                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to next marked position")),
1714                                 "jumpNextMark": (self.jumpNextMark, _("jump to previous marked position")),
1715                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1716                         }, prio=1) 
1717
1718                 self.cut_list = [ ]
1719                 self.is_closing = False
1720                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1721                         {
1722                                 iPlayableService.evStart: self.__serviceStarted,
1723                         })
1724
1725         def __serviceStarted(self):
1726                 if self.is_closing:
1727                         return
1728                 print "new service started! trying to download cuts!"
1729                 self.downloadCuesheet()
1730
1731                 if self.ENABLE_RESUME_SUPPORT:
1732                         last = None
1733
1734                         for (pts, what) in self.cut_list:
1735                                 if what == self.CUT_TYPE_LAST:
1736                                         last = pts
1737
1738                         if last is not None:
1739                                 self.resume_point = last
1740                                 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1741
1742         def playLastCB(self, answer):
1743                 if answer == True:
1744                         seekable = self.__getSeekable()
1745                         if seekable is not None:
1746                                 seekable.seekTo(self.resume_point)
1747
1748         def __getSeekable(self):
1749                 service = self.session.nav.getCurrentService()
1750                 if service is None:
1751                         return None
1752                 return service.seek()
1753
1754         def cueGetCurrentPosition(self):
1755                 seek = self.__getSeekable()
1756                 if seek is None:
1757                         return None
1758                 r = seek.getPlayPosition()
1759                 if r[0]:
1760                         return None
1761                 return long(r[1])
1762
1763         def jumpPreviousNextMark(self, cmp, alternative=None):
1764                 current_pos = self.cueGetCurrentPosition()
1765                 if current_pos is None:
1766                         return
1767                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1768                 if mark is not None:
1769                         pts = mark[0]
1770                 elif alternative is not None:
1771                         pts = alternative
1772                 else:
1773                         return
1774
1775                 seekable = self.__getSeekable()
1776                 if seekable is not None:
1777                         seekable.seekTo(pts)
1778
1779         def jumpPreviousMark(self):
1780                 # we add 2 seconds, so if the play position is <2s after
1781                 # the mark, the mark before will be used
1782                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1783
1784         def jumpNextMark(self):
1785                 self.jumpPreviousNextMark(lambda x: x)
1786
1787         def getNearestCutPoint(self, pts, cmp=abs):
1788                 # can be optimized
1789                 nearest = None
1790                 for cp in self.cut_list:
1791                         diff = cmp(cp[0] - pts)
1792                         if cp[1] == self.CUT_TYPE_MARK and diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1793                                 nearest = cp
1794                 return nearest
1795
1796         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1797                 current_pos = self.cueGetCurrentPosition()
1798                 if current_pos is None:
1799                         print "not seekable"
1800                         return
1801
1802                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1803
1804                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1805                         if onlyreturn:
1806                                 return nearest_cutpoint
1807                         if not onlyadd:
1808                                 self.removeMark(nearest_cutpoint)
1809                 elif not onlyremove and not onlyreturn:
1810                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1811
1812                 if onlyreturn:
1813                         return None
1814
1815         def showAfterCuesheetOperation(self):
1816                 if isinstance(self, InfoBarShowHide):
1817                         self.doShow()
1818
1819         def addMark(self, point):
1820                 insort(self.cut_list, point)
1821                 self.uploadCuesheet()
1822                 self.showAfterCuesheetOperation()
1823
1824         def removeMark(self, point):
1825                 self.cut_list.remove(point)
1826                 self.uploadCuesheet()
1827                 self.showAfterCuesheetOperation()
1828
1829         def showAfterCuesheetOperation(self):
1830                 if isinstance(self, InfoBarShowHide):
1831                         self.doShow()
1832
1833         def __getCuesheet(self):
1834                 service = self.session.nav.getCurrentService()
1835                 if service is None:
1836                         return None
1837                 return service.cueSheet()
1838
1839         def uploadCuesheet(self):
1840                 cue = self.__getCuesheet()
1841
1842                 if cue is None:
1843                         print "upload failed, no cuesheet interface"
1844                         return
1845                 cue.setCutList(self.cut_list)
1846
1847         def downloadCuesheet(self):
1848                 cue = self.__getCuesheet()
1849
1850                 if cue is None:
1851                         print "download failed, no cuesheet interface"
1852                         self.cut_list = [ ]
1853                 else:
1854                         self.cut_list = cue.getCutList()
1855
1856 class InfoBarSummary(Screen):
1857         skin = """
1858         <screen position="0,0" size="132,64">
1859                 <widget source="CurrentTime" render="Label" position="56,46" size="82,18" font="Regular;16" >
1860                         <convert type="ClockToText">WithSeconds</convert>
1861                 </widget>
1862                 <widget source="CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
1863                         <convert type="ServiceName">Name</convert>
1864                 </widget>
1865         </screen>"""
1866
1867         def __init__(self, session, parent):
1868                 Screen.__init__(self, session)
1869                 self["CurrentService"] = CurrentService(self.session.nav)
1870                 self["CurrentTime"] = Clock()
1871
1872 class InfoBarSummarySupport:
1873         def __init__(self):
1874                 pass
1875
1876         def createSummary(self):
1877                 return InfoBarSummary
1878
1879 class InfoBarTeletextPlugin:
1880         def __init__(self):
1881                 self.teletext_plugin = None
1882
1883                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1884                         self.teletext_plugin = p
1885
1886                 if self.teletext_plugin is not None:
1887                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1888                                 {
1889                                         "startTeletext": (self.startTeletext, _("View teletext..."))
1890                                 })
1891                 else:
1892                         print "no teletext plugin found!"
1893
1894         def startTeletext(self):
1895                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1896
1897 class InfoBarSubtitleSupport(object):
1898         def __init__(self):
1899                 object.__init__(self)
1900                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1901                 self.__subtitles_enabled = False
1902
1903                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1904                         {
1905                                 iPlayableService.evEnd: self.__serviceStopped,
1906                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
1907                         })
1908                 self.cached_subtitle_checked = False
1909                 self.__selected_subtitle = None
1910
1911         def __serviceStopped(self):
1912                 self.subtitle_window.hide()
1913                 self.__subtitles_enabled = False
1914                 self.cached_subtitle_checked = False
1915
1916         def __updatedInfo(self):
1917                 if not self.cached_subtitle_checked:
1918                         subtitle = self.getCurrentServiceSubtitle()
1919                         self.cached_subtitle_checked = True
1920                         self.__selected_subtitle = subtitle and subtitle.getCachedSubtitle()
1921                         if self.__selected_subtitle:
1922                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1923                                 self.subtitle_window.show()
1924                                 self.__subtitles_enabled = True
1925
1926         def getCurrentServiceSubtitle(self):
1927                 service = self.session.nav.getCurrentService()
1928                 return service and service.subtitle()
1929
1930         def setSubtitlesEnable(self, enable=True):
1931                 subtitle = self.getCurrentServiceSubtitle()
1932                 if enable and self.__selected_subtitle is not None:
1933                         if subtitle and not self.__subtitles_enabled:
1934                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1935                                 self.subtitle_window.show()
1936                                 self.__subtitles_enabled = True
1937                 else:
1938                         if subtitle:
1939                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1940                         self.__subtitles_enabled = False
1941                         self.subtitle_window.hide()
1942
1943         def setSelectedSubtitle(self, subtitle):
1944                 self.__selected_subtitle = subtitle
1945
1946         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
1947         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
1948
1949 class InfoBarServiceErrorPopupSupport:
1950         def __init__(self):
1951                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1952                         {
1953                                 iPlayableService.evTuneFailed: self.__tuneFailed,
1954                                 iPlayableService.evStart: self.__serviceStarted
1955                         })
1956                 self.__serviceStarted()
1957
1958         def __serviceStarted(self):
1959                 self.last_error = None
1960                 Notifications.RemovePopup(id = "ZapError")
1961
1962         def __tuneFailed(self):
1963                 service = self.session.nav.getCurrentService()
1964                 info = service and service.info()
1965                 error = info and info.getInfo(iServiceInformation.sDVBState)
1966
1967                 if error == self.last_error:
1968                         error = None
1969                 else:
1970                         self.last_error = error
1971
1972                 errors = {
1973                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
1974                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
1975                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
1976                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
1977                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
1978                         eDVBServicePMTHandler.eventNewProgramInfo: None,
1979                         eDVBServicePMTHandler.eventTuned: None,
1980                         eDVBServicePMTHandler.eventSOF: None,
1981                         eDVBServicePMTHandler.eventEOF: None
1982                 }
1983
1984                 error = errors.get(error) #this returns None when the key not exist in the dict
1985
1986                 if error is not None:
1987                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
1988                 else:
1989                         Notifications.RemovePopup(id = "ZapError")