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