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