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