2db57c524796d3f6c96d255d6d38fee683a58a06
[openblackhole/openblackhole-enigma2.git] / lib / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector, SilentBouquetSelector
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.MovieList import AUDIO_EXTENSIONS
9 from Components.PluginComponent import plugins
10 from Components.ServiceEventTracker import ServiceEventTracker
11 from Components.Sources.Boolean import Boolean
12 from Components.config import config, ConfigBoolean, ConfigClock
13 from Components.SystemInfo import SystemInfo
14 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath, ConfigSelection
15 from EpgSelection import EPGSelection
16 from Plugins.Plugin import PluginDescriptor
17
18 from Screen import Screen
19 from Screens import ScreenSaver
20 from Screens import Standby
21 from Screens.ChoiceBox import ChoiceBox
22 from Screens.Dish import Dish
23 from Screens.EventView import EventViewEPGSelect, EventViewSimple
24 from Screens.InputBox import InputBox
25 from Screens.MessageBox import MessageBox
26 from Screens.MinuteInput import MinuteInput
27 from Screens.TimerSelection import TimerSelection
28 from Screens.PictureInPicture import PictureInPicture
29 import Screens.Standby
30 from Screens.SubtitleDisplay import SubtitleDisplay
31 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
32 from Screens.TimeDateInput import TimeDateInput
33 from Screens.UnhandledKey import UnhandledKey
34 from ServiceReference import ServiceReference, isPlayableForCur
35
36 from Tools import Notifications, ASCIItranslit
37 from Tools.Directories import fileExists, getRecordingFilename, moveFiles
38
39 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
40         iPlayableService, eServiceReference, eEPGCache, eActionMap
41
42 from time import time, localtime, strftime
43 from os import stat as os_stat
44 from os import rename as os_rename
45 import os
46 from bisect import insort
47 from sys import maxint
48
49 from RecordTimer import RecordTimerEntry, RecordTimer, findSafeRecordPath
50
51 # hack alert!
52 from Menu import MainMenu, mdom
53
54 def isStandardInfoBar(self):
55         return self.__class__.__name__ == "InfoBar"
56
57 def setResumePoint(session):
58         global resumePointCache, resumePointCacheLast
59         service = session.nav.getCurrentService()
60         ref = session.nav.getCurrentlyPlayingServiceOrGroup()
61         if (service is not None) and (ref is not None): # and (ref.type != 1):
62                 # ref type 1 has its own memory...
63                 seek = service.seek()
64                 if seek:
65                         pos = seek.getPlayPosition()
66                         if not pos[0]:
67                                 key = ref.toString()
68                                 lru = int(time())
69                                 l = seek.getLength()
70                                 if l:
71                                         l = l[1]
72                                 else:
73                                         l = None
74                                 resumePointCache[key] = [lru, pos[1], l]
75                                 if len(resumePointCache) > 50:
76                                         candidate = key
77                                         for k,v in resumePointCache.items():
78                                                 if v[0] < lru:
79                                                         candidate = k
80                                         del resumePointCache[candidate]
81                                 if lru - resumePointCacheLast > 3600:
82                                         saveResumePoints()
83
84 def delResumePoint(ref):
85         global resumePointCache, resumePointCacheLast
86         try:
87                 del resumePointCache[ref.toString()]
88         except KeyError:
89                 pass
90         if int(time()) - resumePointCacheLast > 3600:
91                 saveResumePoints()
92
93 def getResumePoint(session):
94         global resumePointCache
95         ref = session.nav.getCurrentlyPlayingServiceOrGroup()
96         if (ref is not None) and (ref.type != 1):
97                 try:
98                         entry = resumePointCache[ref.toString()]
99                         entry[0] = int(time()) # update LRU timestamp
100                         return entry[1]
101                 except KeyError:
102                         return None
103
104 def saveResumePoints():
105         global resumePointCache, resumePointCacheLast
106         import cPickle
107         try:
108                 f = open('/home/root/resumepoints.pkl', 'wb')
109                 cPickle.dump(resumePointCache, f, cPickle.HIGHEST_PROTOCOL)
110         except Exception, ex:
111                 print "[InfoBar] Failed to write resumepoints:", ex
112         resumePointCacheLast = int(time())
113
114 def loadResumePoints():
115         import cPickle
116         try:
117                 return cPickle.load(open('/home/root/resumepoints.pkl', 'rb'))
118         except Exception, ex:
119                 print "[InfoBar] Failed to load resumepoints:", ex
120                 return {}
121
122 resumePointCache = loadResumePoints()
123 resumePointCacheLast = int(time())
124
125 class InfoBarDish:
126         def __init__(self):
127                 self.dishDialog = self.session.instantiateDialog(Dish)
128
129 class InfoBarUnhandledKey:
130         def __init__(self):
131                 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
132                 self.hideUnhandledKeySymbolTimer = eTimer()
133                 self.hideUnhandledKeySymbolTimer.callback.append(self.unhandledKeyDialog.hide)
134                 self.checkUnusedTimer = eTimer()
135                 self.checkUnusedTimer.callback.append(self.checkUnused)
136                 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
137                 eActionMap.getInstance().bindAction('', -maxint -1, self.actionA) #highest prio
138                 eActionMap.getInstance().bindAction('', maxint, self.actionB) #lowest prio
139                 self.flags = (1<<1)
140                 self.uflags = 0
141
142         #this function is called on every keypress!
143         def actionA(self, key, flag):
144                 self.unhandledKeyDialog.hide()
145                 if flag != 4:
146                         if self.flags & (1<<1):
147                                 self.flags = self.uflags = 0
148                         self.flags |= (1<<flag)
149                         if flag == 1: # break
150                                 self.checkUnusedTimer.start(0, True)
151                 return 0
152
153         #this function is only called when no other action has handled this key
154         def actionB(self, key, flag):
155                 if flag != 4:
156                         self.uflags |= (1<<flag)
157
158         def checkUnused(self):
159                 if self.flags == self.uflags:
160                         self.unhandledKeyDialog.show()
161                         self.hideUnhandledKeySymbolTimer.start(2000, True)
162
163 class InfoBarScreenSaver:
164         def __init__(self):
165                 self.onExecBegin.append(self.__onExecBegin)
166                 self.onExecEnd.append(self.__onExecEnd)
167                 self.screenSaverTimer = eTimer()
168                 self.screenSaverTimer.callback.append(self.screensaverTimeout)
169                 self.screensaver = self.session.instantiateDialog(ScreenSaver.Screensaver)
170                 self.onLayoutFinish.append(self.__layoutFinished)
171
172         def __layoutFinished(self):
173                 self.screensaver.hide()
174
175         def __onExecBegin(self):
176                 self.ScreenSaverTimerStart()
177
178         def __onExecEnd(self):
179                 if self.screensaver.shown:
180                         self.screensaver.hide()
181                         eActionMap.getInstance().unbindAction('', self.keypressScreenSaver)
182                 self.screenSaverTimer.stop()
183
184         def ScreenSaverTimerStart(self):
185                 time = int(config.usage.screen_saver.value)
186                 flag = self.seekstate[0]
187                 if not flag:
188                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
189                         if ref:
190                                 ref = ref.toString().split(":")
191                                 flag = ref[2] == "2" or os.path.splitext(ref[10])[1].lower() in AUDIO_EXTENSIONS
192                 if time and flag:
193                         self.screenSaverTimer.startLongTimer(time)
194                 else:
195                         self.screenSaverTimer.stop()
196
197         def screensaverTimeout(self):
198                 if self.execing and not Standby.inStandby and not Standby.inTryQuitMainloop:
199                         self.hide()
200                         if hasattr(self, "pvrStateDialog"):
201                                 self.pvrStateDialog.hide()
202                         self.screensaver.show()
203                         eActionMap.getInstance().bindAction('', -maxint - 1, self.keypressScreenSaver)
204
205         def keypressScreenSaver(self, key, flag):
206                 if flag:
207                         self.screensaver.hide()
208                         self.show()
209                         self.ScreenSaverTimerStart()
210                         eActionMap.getInstance().unbindAction('', self.keypressScreenSaver)
211
212 class SecondInfoBar(Screen):
213
214         def __init__(self, session):
215                 Screen.__init__(self, session)
216                 self.skin = None
217
218 class InfoBarShowHide(InfoBarScreenSaver):
219         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
220         fancy animations. """
221         STATE_HIDDEN = 0
222         STATE_HIDING = 1
223         STATE_SHOWING = 2
224         STATE_SHOWN = 3
225
226         def __init__(self):
227                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
228                         {
229                                 "toggleShow": self.toggleShow,
230                                 "hide": self.keyHide,
231                         }, 1) # lower prio to make it possible to override ok and cancel..
232
233                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
234                         {
235                                 iPlayableService.evStart: self.serviceStarted,
236                         })
237
238                 InfoBarScreenSaver.__init__(self)
239                 self.__state = self.STATE_SHOWN
240                 self.__locked = 0
241
242                 self.hideTimer = eTimer()
243                 self.hideTimer.callback.append(self.doTimerHide)
244                 self.hideTimer.start(5000, True)
245
246                 self.onShow.append(self.__onShow)
247                 self.onHide.append(self.__onHide)
248
249                 self.onShowHideNotifiers = []
250
251                 self.secondInfoBarScreen = ""
252                 if isStandardInfoBar(self):
253                         self.secondInfoBarScreen = self.session.instantiateDialog(SecondInfoBar)
254                         self.secondInfoBarScreen.show()
255
256                 self.onLayoutFinish.append(self.__layoutFinished)
257
258         def __layoutFinished(self):
259                 if self.secondInfoBarScreen:
260                         self.secondInfoBarScreen.hide()
261
262         def __onShow(self):
263                 self.__state = self.STATE_SHOWN
264                 for x in self.onShowHideNotifiers:
265                         x(True)
266                 self.startHideTimer()
267
268         def __onHide(self):
269                 self.__state = self.STATE_HIDDEN
270                 if self.secondInfoBarScreen:
271                         self.secondInfoBarScreen.hide()
272                 for x in self.onShowHideNotifiers:
273                         x(False)
274
275         def keyHide(self):
276                 if self.__state == self.STATE_SHOWN:
277                         self.hide()
278                 else:
279                         if self.session.pipshown:
280                                 self.showPiP()
281
282         def connectShowHideNotifier(self, fnc):
283                 if not fnc in self.onShowHideNotifiers:
284                         self.onShowHideNotifiers.append(fnc)
285
286         def disconnectShowHideNotifier(self, fnc):
287                 if fnc in self.onShowHideNotifiers:
288                         self.onShowHideNotifiers.remove(fnc)
289
290         def serviceStarted(self):
291                 if self.execing:
292                         if config.usage.show_infobar_on_zap.value:
293                                 self.doShow()
294
295         def startHideTimer(self):
296                 if self.__state == self.STATE_SHOWN and not self.__locked:
297                         self.hideTimer.stop()
298                         if self.secondInfoBarScreen and self.secondInfoBarScreen.shown:
299                                 idx = config.usage.show_second_infobar.index - 1
300                         else:
301                                 idx = config.usage.infobar_timeout.index
302                         if idx:
303                                 self.hideTimer.start(idx*1000, True)
304
305         def doShow(self):
306                 self.show()
307                 self.startHideTimer()
308
309         def doTimerHide(self):
310                 self.hideTimer.stop()
311                 if self.__state == self.STATE_SHOWN:
312                         self.hide()
313
314         def toggleShow(self):
315                 if self.__state == self.STATE_HIDDEN:
316                         self.show()
317                         if self.secondInfoBarScreen:
318                                 self.secondInfoBarScreen.hide()
319                 elif isStandardInfoBar(self) and config.usage.show_second_infobar.value == "EPG":
320                         self.showDefaultEPG()
321                 elif self.secondInfoBarScreen and config.usage.show_second_infobar.value and not self.secondInfoBarScreen.shown:
322                         self.secondInfoBarScreen.show()
323                         self.startHideTimer()
324                 else:
325                         self.hide()
326                         self.hideTimer.stop()
327
328         def lockShow(self):
329                 self.__locked = self.__locked + 1
330                 if self.execing:
331                         self.show()
332                         self.hideTimer.stop()
333
334         def unlockShow(self):
335                 self.__locked = self.__locked - 1
336                 if self.execing:
337                         self.startHideTimer()
338
339 #       def startShow(self):
340 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
341 #               self.__state = self.STATE_SHOWN
342 #
343 #       def startHide(self):
344 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
345 #               self.__state = self.STATE_HIDDEN
346
347 class NumberZap(Screen):
348         def quit(self):
349                 self.Timer.stop()
350                 self.close()
351
352         def keyOK(self):
353                 self.Timer.stop()
354                 self.close(self.service, self.bouquet)
355
356         def handleServiceName(self):
357                 if self.searchNumber:
358                         self.service, self.bouquet = self.searchNumber(int(self["number"].getText()))
359                         self ["servicename"].text = ServiceReference(self.service).getServiceName()
360                         if not self.startBouquet:
361                                 self.startBouquet = self.bouquet
362
363         def keyBlue(self):
364                 self.Timer.start(3000, True)
365                 if self.searchNumber:
366                         if self.startBouquet == self.bouquet:
367                                 self.service, self.bouquet = self.searchNumber(int(self["number"].getText()), firstBouquetOnly = True)
368                         else:
369                                 self.service, self.bouquet = self.searchNumber(int(self["number"].getText()))
370                         self ["servicename"].text = ServiceReference(self.service).getServiceName()
371
372         def keyNumberGlobal(self, number):
373                 self.Timer.start(1000, True)
374                 self.field = self.field + str(number)
375                 self["number"].setText(self.field)
376
377                 self.handleServiceName()
378
379                 if len(self.field) >= 5:
380                         self.keyOK()
381
382         def __init__(self, session, number, searchNumberFunction = None):
383                 Screen.__init__(self, session)
384                 self.field = str(number)
385                 self.searchNumber = searchNumberFunction
386                 self.startBouquet = None
387
388                 self["channel"] = Label(_("Channel:"))
389                 self["number"] = Label(self.field)
390                 self["servicename"] = Label()
391
392                 self.handleServiceName()
393
394                 self["actions"] = NumberActionMap( [ "SetupActions", "ShortcutActions" ],
395                         {
396                                 "cancel": self.quit,
397                                 "ok": self.keyOK,
398                                 "blue": self.keyBlue,
399                                 "1": self.keyNumberGlobal,
400                                 "2": self.keyNumberGlobal,
401                                 "3": self.keyNumberGlobal,
402                                 "4": self.keyNumberGlobal,
403                                 "5": self.keyNumberGlobal,
404                                 "6": self.keyNumberGlobal,
405                                 "7": self.keyNumberGlobal,
406                                 "8": self.keyNumberGlobal,
407                                 "9": self.keyNumberGlobal,
408                                 "0": self.keyNumberGlobal
409                         })
410
411                 self.Timer = eTimer()
412                 self.Timer.callback.append(self.keyOK)
413                 self.Timer.start(3000, True)
414
415 class InfoBarNumberZap:
416         """ Handles an initial number for NumberZapping """
417         def __init__(self):
418                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
419                         {
420                                 "1": self.keyNumberGlobal,
421                                 "2": self.keyNumberGlobal,
422                                 "3": self.keyNumberGlobal,
423                                 "4": self.keyNumberGlobal,
424                                 "5": self.keyNumberGlobal,
425                                 "6": self.keyNumberGlobal,
426                                 "7": self.keyNumberGlobal,
427                                 "8": self.keyNumberGlobal,
428                                 "9": self.keyNumberGlobal,
429                                 "0": self.keyNumberGlobal,
430                         })
431
432         def keyNumberGlobal(self, number):
433                 if number == 0:
434                         if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
435                                 self.pipDoHandle0Action()
436                         else:
437                                 self.servicelist.recallPrevService()
438                 else:
439                         if self.has_key("TimeshiftActions") and self.timeshiftEnabled():
440                                 ts = self.getTimeshift()
441                                 if ts and ts.isTimeshiftActive():
442                                         return
443                         self.session.openWithCallback(self.numberEntered, NumberZap, number, self.searchNumber)
444
445         def numberEntered(self, service = None, bouquet = None):
446                 if service:
447                         self.selectAndStartService(service, bouquet)
448
449         def searchNumberHelper(self, serviceHandler, num, bouquet):
450                 servicelist = serviceHandler.list(bouquet)
451                 if servicelist:
452                         serviceIterator = servicelist.getNext()
453                         while serviceIterator.valid():
454                                 if num == serviceIterator.getChannelNum():
455                                         return serviceIterator
456                                 serviceIterator = servicelist.getNext()
457                 return None
458
459         def searchNumber(self, number, firstBouquetOnly = False):
460                 bouquet = self.servicelist.getRoot()
461                 service = None
462                 serviceHandler = eServiceCenter.getInstance()
463                 if not firstBouquetOnly:
464                         service = self.searchNumberHelper(serviceHandler, number, bouquet)
465                 if config.usage.multibouquet.value and not service:
466                         bouquet = self.servicelist.bouquet_root
467                         bouquetlist = serviceHandler.list(bouquet)
468                         if bouquetlist:
469                                 bouquet = bouquetlist.getNext()
470                                 while bouquet.valid():
471                                         if bouquet.flags & eServiceReference.isDirectory:
472                                                 service = self.searchNumberHelper(serviceHandler, number, bouquet)
473                                                 if service:
474                                                         playable = not (service.flags & (eServiceReference.isMarker|eServiceReference.isDirectory)) or (service.flags & eServiceReference.isNumberedMarker)
475                                                         if not playable:
476                                                                 service = None
477                                                         break
478                                                 if config.usage.alternative_number_mode.value or firstBouquetOnly:
479                                                         break
480                                         bouquet = bouquetlist.getNext()
481                 return service, bouquet
482
483         def selectAndStartService(self, service, bouquet):
484                 if service:
485                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
486                                 self.servicelist.clearPath()
487                                 if self.servicelist.bouquet_root != bouquet:
488                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
489                                 self.servicelist.enterPath(bouquet)
490                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
491                         self.servicelist.zap(enable_pipzap = True)
492                         self.servicelist.startRoot = None
493
494         def zapToNumber(self, number):
495                 service, bouquet = self.searchNumber(number)
496                 self.selectAndStartService(service, bouquet)
497
498 config.misc.initialchannelselection = ConfigBoolean(default = True)
499
500 class InfoBarChannelSelection:
501         """ ChannelSelection - handles the channelSelection dialog and the initial
502         channelChange actions which open the channelSelection dialog """
503         def __init__(self):
504                 #instantiate forever
505                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
506
507                 if config.misc.initialchannelselection.value:
508                         self.onShown.append(self.firstRun)
509
510                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
511                         {
512                                 "switchChannelUp": (self.switchChannelUp, _("Open service list and select previous channel")),
513                                 "switchChannelDown": (self.switchChannelDown, _("Open service list and select next channel")),
514                                 "zapUp": (self.zapUp, _("Switch to previous channel")),
515                                 "zapDown": (self.zapDown, _("Switch next channel")),
516                                 "historyBack": (self.historyBack, _("Switch to previous channel in history")),
517                                 "historyNext": (self.historyNext, _("Switch to next channel in history")),
518                                 "openServiceList": (self.openServiceList, _("Open service list")),
519                         })
520
521         def showTvChannelList(self, zap=False):
522                 self.servicelist.setModeTv()
523                 if zap:
524                         self.servicelist.zap()
525
526         def showRadioChannelList(self, zap=False):
527                 self.servicelist.setModeRadio()
528                 if zap:
529                         self.servicelist.zap()
530
531         def firstRun(self):
532                 self.onShown.remove(self.firstRun)
533                 config.misc.initialchannelselection.value = False
534                 config.misc.initialchannelselection.save()
535                 self.switchChannelDown()
536
537         def historyBack(self):
538                 self.servicelist.historyBack()
539
540         def historyNext(self):
541                 self.servicelist.historyNext()
542
543         def switchChannelUp(self):
544                 self.servicelist.moveUp()
545                 self.session.execDialog(self.servicelist)
546
547         def switchChannelDown(self):
548                 self.servicelist.moveDown()
549                 self.session.execDialog(self.servicelist)
550
551         def openServiceList(self):
552                 self.session.execDialog(self.servicelist)
553
554         def zapUp(self):
555                 if self.servicelist.inBouquet():
556                         prev = self.servicelist.getCurrentSelection()
557                         if prev:
558                                 prev = prev.toString()
559                                 while True:
560                                         if config.usage.quickzap_bouquet_change.value:
561                                                 if self.servicelist.atBegin():
562                                                         self.servicelist.prevBouquet()
563                                         self.servicelist.moveUp()
564                                         cur = self.servicelist.getCurrentSelection()
565                                         if cur and (cur.toString() == prev or isPlayableForCur(cur)):
566                                                         break
567                 else:
568                         self.servicelist.moveUp()
569                 self.servicelist.zap(enable_pipzap = True)
570
571         def zapDown(self):
572                 if self.servicelist.inBouquet():
573                         prev = self.servicelist.getCurrentSelection()
574                         if prev:
575                                 prev = prev.toString()
576                                 while True:
577                                         if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
578                                                 self.servicelist.nextBouquet()
579                                         else:
580                                                 self.servicelist.moveDown()
581                                         cur = self.servicelist.getCurrentSelection()
582                                         if cur and (cur.toString() == prev or isPlayableForCur(cur)):
583                                                         break
584                 else:
585                         self.servicelist.moveDown()
586                 self.servicelist.zap(enable_pipzap = True)
587
588 class InfoBarMenu:
589         """ Handles a menu action, to open the (main) menu """
590         def __init__(self):
591                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
592                         {
593                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
594                         })
595                 self.session.infobar = None
596
597         def mainMenu(self):
598                 print "loading mainmenu XML..."
599                 menu = mdom.getroot()
600                 assert menu.tag == "menu", "root element in menu must be 'menu'!"
601
602                 self.session.infobar = self
603                 # so we can access the currently active infobar from screens opened from within the mainmenu
604                 # at the moment used from the SubserviceSelection
605
606                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
607
608         def mainMenuClosed(self, *val):
609                 self.session.infobar = None
610
611 class InfoBarSimpleEventView:
612         """ Opens the Eventview for now/next """
613         def __init__(self):
614                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
615                         {
616                                 "showEventInfo": (self.openEventView, _("Show event details")),
617                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
618                         })
619
620         def showEventInfoWhenNotVisible(self):
621                 if self.shown:
622                         self.openEventView()
623                 else:
624                         self.toggleShow()
625                         return 1
626
627         def openEventView(self):
628                 epglist = [ ]
629                 self.epglist = epglist
630                 service = self.session.nav.getCurrentService()
631                 ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
632                 info = service.info()
633                 ptr=info.getEvent(0)
634                 if ptr:
635                         epglist.append(ptr)
636                 ptr=info.getEvent(1)
637                 if ptr:
638                         epglist.append(ptr)
639                 if epglist:
640                         self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
641
642         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
643                 epglist = self.epglist
644                 if len(epglist) > 1:
645                         tmp = epglist[0]
646                         epglist[0] = epglist[1]
647                         epglist[1] = tmp
648                         setEvent(epglist[0])
649
650 class SimpleServicelist:
651         def __init__(self, services):
652                 self.services = services
653                 self.length = len(services)
654                 self.current = 0
655
656         def selectService(self, service):
657                 if not self.length:
658                         self.current = -1
659                         return False
660                 else:
661                         self.current = 0
662                         while self.services[self.current].ref != service:
663                                 self.current += 1
664                                 if self.current >= self.length:
665                                         return False
666                 return True
667
668         def nextService(self):
669                 if not self.length:
670                         return
671                 if self.current+1 < self.length:
672                         self.current += 1
673                 else:
674                         self.current = 0
675
676         def prevService(self):
677                 if not self.length:
678                         return
679                 if self.current-1 > -1:
680                         self.current -= 1
681                 else:
682                         self.current = self.length - 1
683
684         def currentService(self):
685                 if not self.length or self.current >= self.length:
686                         return None
687                 return self.services[self.current]
688
689 class InfoBarEPG:
690         """ EPG - Opens an EPG list when the showEPGList action fires """
691         def __init__(self):
692                 self.is_now_next = False
693                 self.dlg_stack = [ ]
694                 self.bouquetSel = None
695                 self.eventView = None
696                 self.epglist = []
697                 self.defaultEPGType = self.getDefaultEPGtype()
698                 self.defaultGuideType = self.getDefaultGuidetype()
699                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
700                         {
701                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
702                         })
703
704                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
705                         {
706                                 "showEventInfo": (self.showDefaultEPG, _("Show EPG...")),
707                                 "showEventInfoSingleEPG": (self.showSingleEPG, _("Show single service EPG")),
708                                 "showEventInfoMultiEPG": (self.showMultiEPG, _("Show multi channel EPG")),
709                                 "showEventInfoPlugin": (self.showEventInfoPlugins, _("List EPG functions...")),
710                                 "showEventGuidePlugin": (self.showEventGuidePlugins, _("List EPG functions...")),
711                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
712                         })
713
714         def getEPGPluginList(self):
715                 pluginlist = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
716                 if pluginlist:
717                         pluginlist.append((_("Show EPG for current channel..."), self.openSingleServiceEPG))
718                         pluginlist.append((_("Multi EPG"), self.openMultiServiceEPG))
719                         pluginlist.append((_("Current event EPG"), self.openEventView))
720                 return pluginlist
721
722         def getDefaultEPGtype(self):
723                 pluginlist = self.getEPGPluginList()
724                 config.usage.defaultEPGType=ConfigSelection(default = "None", choices = pluginlist)
725                 for plugin in pluginlist:
726                         if plugin[0] == config.usage.defaultEPGType.value:
727                                 return plugin[1]
728                 return None
729
730         def getDefaultGuidetype(self):
731                 pluginlist = self.getEPGPluginList()
732                 config.usage.defaultGuideType=ConfigSelection(default = "None", choices = pluginlist)
733                 for plugin in pluginlist:
734                         if plugin[0] == config.usage.defaultGuideType.value:
735                                 return plugin[1]
736                 return None
737
738         def showEventInfoWhenNotVisible(self):
739                 if self.shown:
740                         self.openEventView()
741                 else:
742                         self.toggleShow()
743                         return 1
744
745         def zapToService(self, service, preview = False, zapback = False):
746                 if self.servicelist.startServiceRef is None:
747                         self.servicelist.startServiceRef = self.session.nav.getCurrentlyPlayingServiceOrGroup()
748                 if service is not None:
749                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
750                                 self.servicelist.clearPath()
751                                 if self.servicelist.bouquet_root != self.epg_bouquet:
752                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
753                                 self.servicelist.enterPath(self.epg_bouquet)
754                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
755                 if not zapback or preview:
756                         self.servicelist.zap(enable_pipzap = True)
757                 if (self.servicelist.dopipzap or zapback) and not preview:
758                         self.servicelist.zapBack()
759                 if not preview:
760                         self.servicelist.startServiceRef = None
761                         self.servicelist.startRoot = None
762
763         def getBouquetServices(self, bouquet):
764                 services = [ ]
765                 servicelist = eServiceCenter.getInstance().list(bouquet)
766                 if not servicelist is None:
767                         while True:
768                                 service = servicelist.getNext()
769                                 if not service.valid(): #check if end of list
770                                         break
771                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
772                                         continue
773                                 services.append(ServiceReference(service))
774                 return services
775
776         def openBouquetEPG(self, bouquet, withCallback=True):
777                 services = self.getBouquetServices(bouquet)
778                 if services:
779                         self.epg_bouquet = bouquet
780                         if withCallback:
781                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
782                         else:
783                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
784
785         def changeBouquetCB(self, direction, epg):
786                 if self.bouquetSel:
787                         if direction > 0:
788                                 self.bouquetSel.down()
789                         else:
790                                 self.bouquetSel.up()
791                         bouquet = self.bouquetSel.getCurrent()
792                         services = self.getBouquetServices(bouquet)
793                         if services:
794                                 self.epg_bouquet = bouquet
795                                 epg.setServices(services)
796
797         def closed(self, ret=False):
798                 closedScreen = self.dlg_stack.pop()
799                 if self.bouquetSel and closedScreen == self.bouquetSel:
800                         self.bouquetSel = None
801                 elif self.eventView and closedScreen == self.eventView:
802                         self.eventView = None
803                 if ret:
804                         dlgs=len(self.dlg_stack)
805                         if dlgs > 0:
806                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
807
808         def openMultiServiceEPG(self, withCallback=True):
809                 bouquets = self.servicelist.getBouquetList()
810                 if bouquets is None:
811                         cnt = 0
812                 else:
813                         cnt = len(bouquets)
814                 if config.usage.multiepg_ask_bouquet.value:
815                         self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
816                 else:
817                         self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
818
819         def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
820                 if cnt > 1: # show bouquet list
821                         if withCallback:
822                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
823                                 self.dlg_stack.append(self.bouquetSel)
824                         else:
825                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
826                 elif cnt == 1:
827                         self.openBouquetEPG(bouquets[0][1], withCallback)
828
829         def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
830                 root = self.servicelist.getRoot()
831                 rootstr = root.toCompareString()
832                 current = 0
833                 for bouquet in bouquets:
834                         if bouquet[1].toCompareString() == rootstr:
835                                 break
836                         current += 1
837                 if current >= cnt:
838                         current = 0
839                 if cnt > 1: # create bouquet list for bouq+/-
840                         self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
841                 if cnt >= 1:
842                         self.openBouquetEPG(root, withCallback)
843
844         def changeServiceCB(self, direction, epg):
845                 if self.serviceSel:
846                         if direction > 0:
847                                 self.serviceSel.nextService()
848                         else:
849                                 self.serviceSel.prevService()
850                         epg.setService(self.serviceSel.currentService())
851
852         def SingleServiceEPGClosed(self, ret=False):
853                 self.serviceSel = None
854
855         def openSingleServiceEPG(self):
856                 ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
857                 if ref:
858                         if self.servicelist.getMutableList() is not None: # bouquet in channellist
859                                 current_path = self.servicelist.getRoot()
860                                 services = self.getBouquetServices(current_path)
861                                 self.serviceSel = SimpleServicelist(services)
862                                 if self.serviceSel.selectService(ref):
863                                         self.epg_bouquet = current_path
864                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, self.zapToService, serviceChangeCB = self.changeServiceCB)
865                                 else:
866                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
867                         else:
868                                 self.session.open(EPGSelection, ref)
869
870         def runPlugin(self, plugin):
871                 plugin(session = self.session, servicelist = self.servicelist)
872
873         def showEventInfoPlugins(self):
874                 pluginlist = self.getEPGPluginList()
875                 if pluginlist:
876                         pluginlist.append((_("Select default EPG type..."), self.SelectDefaultInfoPlugin))
877                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = pluginlist, skin_name = "EPGExtensionsList")
878                 else:
879                         self.openSingleServiceEPG()
880
881         def EventInfoPluginChosen(self, answer):
882                 if answer is not None:
883                         answer[1]()
884
885         def SelectDefaultInfoPlugin(self):
886                 self.session.openWithCallback(self.DefaultInfoPluginChosen, ChoiceBox, title=_("Please select a default EPG type..."), list = self.getEPGPluginList(), skin_name = "EPGExtensionsList")
887
888         def DefaultInfoPluginChosen(self, answer):
889                 if answer is not None:
890                         self.defaultEPGType = answer[1]
891                         config.usage.defaultEPGType.value = answer[0]
892                         config.usage.defaultEPGType.save()
893
894         def showEventGuidePlugins(self):
895                 pluginlist = self.getEPGPluginList()
896                 if pluginlist:
897                         pluginlist.append((_("Select default EPG type..."), self.SelectDefaultGuidePlugin))
898                         self.session.openWithCallback(self.EventGuidePluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = pluginlist, skin_name = "EPGExtensionsList")
899                 else:
900                         self.openSingleServiceEPG()
901
902         def EventGuidePluginChosen(self, answer):
903                 if answer is not None:
904                         answer[1]()
905
906         def SelectDefaultGuidePlugin(self):
907                 self.session.openWithCallback(self.DefaultGuidePluginChosen, ChoiceBox, title=_("Please select a default EPG type..."), list = self.getEPGPluginList(), skin_name = "EPGExtensionsList")
908
909         def DefaultGuidePluginChosen(self, answer):
910                 if answer is not None:
911                         self.defaultGuideType = answer[1]
912                         config.usage.defaultGuideType.value = answer[0]
913                         config.usage.defaultGuideType.save()
914
915         def openSimilarList(self, eventid, refstr):
916                 self.session.open(EPGSelection, refstr, None, eventid)
917
918         def getNowNext(self):
919                 epglist = [ ]
920                 service = self.session.nav.getCurrentService()
921                 info = service and service.info()
922                 ptr = info and info.getEvent(0)
923                 if ptr:
924                         epglist.append(ptr)
925                 ptr = info and info.getEvent(1)
926                 if ptr:
927                         epglist.append(ptr)
928                 self.epglist = epglist
929
930         def __evEventInfoChanged(self):
931                 if self.is_now_next and len(self.dlg_stack) == 1:
932                         self.getNowNext()
933                         if self.eventView and self.epglist:
934                                 self.eventView.setEvent(self.epglist[0])
935
936         def showDefaultEPG(self):
937                 if self.defaultEPGType is not None:
938                         self.defaultEPGType()
939                         return
940                 self.openEventView()
941
942         def showSingleEPG(self):
943                 if self.defaultGuideType is not None:
944                         self.defaultGuideType()
945                         return
946                 pluginlist = self.getEPGPluginList()
947                 self.openSingleServiceEPG()
948
949         def showMultiEPG(self):
950                 if self.defaultGuideType is not None:
951                         self.defaultGuideType()
952                         return
953                 pluginlist = self.getEPGPluginList()
954                 self.openMultiServiceEPG()              
955
956         def openEventView(self):
957                 ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
958                 self.getNowNext()
959                 epglist = self.epglist
960                 if not epglist:
961                         self.is_now_next = False
962                         epg = eEPGCache.getInstance()
963                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
964                         if ptr:
965                                 epglist.append(ptr)
966                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
967                                 if ptr:
968                                         epglist.append(ptr)
969                 else:
970                         self.is_now_next = True
971                 if epglist:
972                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
973                         self.dlg_stack.append(self.eventView)
974                 else:
975                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
976                         self.openMultiServiceEPG(False)
977
978         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
979                 epglist = self.epglist
980                 if len(epglist) > 1:
981                         tmp = epglist[0]
982                         epglist[0]=epglist[1]
983                         epglist[1]=tmp
984                         setEvent(epglist[0])
985
986 class InfoBarRdsDecoder:
987         """provides RDS and Rass support/display"""
988         def __init__(self):
989                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
990                 self.session.instantiateSummaryDialog(self.rds_display)
991                 self.rass_interactive = None
992
993                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
994                         {
995                                 iPlayableService.evEnd: self.__serviceStopped,
996                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
997                         })
998
999                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
1000                 {
1001                         "startRassInteractive": self.startRassInteractive
1002                 },-1)
1003
1004                 self["RdsActions"].setEnabled(False)
1005
1006                 self.onLayoutFinish.append(self.rds_display.show)
1007                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
1008
1009         def RassInteractivePossibilityChanged(self, state):
1010                 self["RdsActions"].setEnabled(state)
1011
1012         def RassSlidePicChanged(self):
1013                 if not self.rass_interactive:
1014                         service = self.session.nav.getCurrentService()
1015                         decoder = service and service.rdsDecoder()
1016                         if decoder:
1017                                 decoder.showRassSlidePicture()
1018
1019         def __serviceStopped(self):
1020                 if self.rass_interactive is not None:
1021                         rass_interactive = self.rass_interactive
1022                         self.rass_interactive = None
1023                         rass_interactive.close()
1024
1025         def startRassInteractive(self):
1026                 self.rds_display.hide()
1027                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
1028
1029         def RassInteractiveClosed(self, *val):
1030                 if self.rass_interactive is not None:
1031                         self.rass_interactive = None
1032                         self.RassSlidePicChanged()
1033                 self.rds_display.show()
1034
1035 class InfoBarSeek:
1036         """handles actions like seeking, pause"""
1037
1038         SEEK_STATE_PLAY = (0, 0, 0, ">")
1039         SEEK_STATE_PAUSE = (1, 0, 0, "||")
1040         SEEK_STATE_EOF = (1, 0, 0, "END")
1041
1042         def __init__(self, actionmap = "InfobarSeekActions"):
1043                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1044                         {
1045                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1046                                 iPlayableService.evStart: self.__serviceStarted,
1047                                 iPlayableService.evEOF: self.__evEOF,
1048                                 iPlayableService.evSOF: self.__evSOF,
1049                         })
1050                 self.fast_winding_hint_message_showed = False
1051
1052                 class InfoBarSeekActionMap(HelpableActionMap):
1053                         def __init__(self, screen, *args, **kwargs):
1054                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
1055                                 self.screen = screen
1056
1057                         def action(self, contexts, action):
1058                                 print "action:", action
1059                                 if action[:5] == "seek:":
1060                                         time = int(action[5:])
1061                                         self.screen.doSeekRelative(time * 90000)
1062                                         return 1
1063                                 elif action[:8] == "seekdef:":
1064                                         key = int(action[8:])
1065                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
1066                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
1067                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
1068                                         self.screen.doSeekRelative(time * 90000)
1069                                         return 1
1070                                 else:
1071                                         return HelpableActionMap.action(self, contexts, action)
1072
1073                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
1074                         {
1075                                 "playpauseService": self.playpauseService,
1076                                 "pauseService": (self.pauseService, _("Pause playback")),
1077                                 "unPauseService": (self.unPauseService, _("Continue playback")),
1078
1079                                 "seekFwd": (self.seekFwd, _("Seek forward")),
1080                                 "seekFwdManual": (self.seekFwdManual, _("Seek forward (enter time)")),
1081                                 "seekBack": (self.seekBack, _("Seek backward")),
1082                                 "seekBackManual": (self.seekBackManual, _("Seek backward (enter time)"))
1083                         }, prio=-1)
1084                         # give them a little more priority to win over color buttons
1085
1086                 self["SeekActions"].setEnabled(False)
1087
1088                 self.seekstate = self.SEEK_STATE_PLAY
1089                 self.lastseekstate = self.SEEK_STATE_PLAY
1090
1091                 self.onPlayStateChanged = [ ]
1092
1093                 self.lockedBecauseOfSkipping = False
1094
1095                 self.__seekableStatusChanged()
1096
1097         def makeStateForward(self, n):
1098                 return (0, n, 0, ">> %dx" % n)
1099
1100         def makeStateBackward(self, n):
1101                 return (0, -n, 0, "<< %dx" % n)
1102
1103         def makeStateSlowMotion(self, n):
1104                 return (0, 0, n, "/%d" % n)
1105
1106         def isStateForward(self, state):
1107                 return state[1] > 1
1108
1109         def isStateBackward(self, state):
1110                 return state[1] < 0
1111
1112         def isStateSlowMotion(self, state):
1113                 return state[1] == 0 and state[2] > 1
1114
1115         def getHigher(self, n, lst):
1116                 for x in lst:
1117                         if x > n:
1118                                 return x
1119                 return False
1120
1121         def getLower(self, n, lst):
1122                 lst = lst[:]
1123                 lst.reverse()
1124                 for x in lst:
1125                         if x < n:
1126                                 return x
1127                 return False
1128
1129         def showAfterSeek(self):
1130                 if isinstance(self, InfoBarShowHide):
1131                         self.doShow()
1132
1133         def up(self):
1134                 pass
1135
1136         def down(self):
1137                 pass
1138
1139         def getSeek(self):
1140                 service = self.session.nav.getCurrentService()
1141                 if service is None:
1142                         return None
1143
1144                 seek = service.seek()
1145
1146                 if seek is None or not seek.isCurrentlySeekable():
1147                         return None
1148
1149                 return seek
1150
1151         def isSeekable(self):
1152                 if self.getSeek() is None or (isStandardInfoBar(self) and not self.timeshiftEnabled()):
1153                         return False
1154                 return True
1155
1156         def __seekableStatusChanged(self):
1157 #               print "seekable status changed!"
1158                 if not self.isSeekable():
1159                         self["SeekActions"].setEnabled(False)
1160 #                       print "not seekable, return to play"
1161                         self.setSeekState(self.SEEK_STATE_PLAY)
1162                 else:
1163                         self["SeekActions"].setEnabled(True)
1164 #                       print "seekable"
1165
1166         def __serviceStarted(self):
1167                 self.fast_winding_hint_message_showed = False
1168                 self.setSeekState(self.SEEK_STATE_PLAY)
1169                 self.__seekableStatusChanged()
1170
1171         def setSeekState(self, state):
1172                 service = self.session.nav.getCurrentService()
1173
1174                 if service is None:
1175                         return False
1176
1177                 if not self.isSeekable():
1178                         if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
1179                                 state = self.SEEK_STATE_PLAY
1180
1181                 pauseable = service.pause()
1182
1183                 if pauseable is None:
1184                         print "not pauseable."
1185                         state = self.SEEK_STATE_PLAY
1186
1187                 self.seekstate = state
1188
1189                 if pauseable is not None:
1190                         if self.seekstate[0]:
1191                                 print "resolved to PAUSE"
1192                                 pauseable.pause()
1193                         elif self.seekstate[1]:
1194                                 if not pauseable.setFastForward(self.seekstate[1]):
1195                                         print "resolved to FAST FORWARD"
1196                                 else:
1197                                         self.seekstate = self.SEEK_STATE_PLAY
1198                                         print "FAST FORWARD not possible: resolved to PLAY"
1199                         elif self.seekstate[2]:
1200                                 if not pauseable.setSlowMotion(self.seekstate[2]):
1201                                         print "resolved to SLOW MOTION"
1202                                 else:
1203                                         self.seekstate = self.SEEK_STATE_PAUSE
1204                                         print "SLOW MOTION not possible: resolved to PAUSE"
1205                         else:
1206                                 print "resolved to PLAY"
1207                                 pauseable.unpause()
1208
1209                 for c in self.onPlayStateChanged:
1210                         c(self.seekstate)
1211
1212                 self.checkSkipShowHideLock()
1213
1214                 if hasattr(self, "ScreenSaverTimerStart"):
1215                         self.ScreenSaverTimerStart()
1216
1217                 return True
1218
1219         def playpauseService(self):
1220                 if self.seekstate != self.SEEK_STATE_PLAY:
1221                         self.unPauseService()
1222                 else:
1223                         self.pauseService()
1224
1225         def pauseService(self):
1226                 if self.seekstate == self.SEEK_STATE_PAUSE:
1227                         if config.seek.on_pause.value == "play":
1228                                 self.unPauseService()
1229                         elif config.seek.on_pause.value == "step":
1230                                 self.doSeekRelative(1)
1231                         elif config.seek.on_pause.value == "last":
1232                                 self.setSeekState(self.lastseekstate)
1233                                 self.lastseekstate = self.SEEK_STATE_PLAY
1234                 else:
1235                         if self.seekstate != self.SEEK_STATE_EOF:
1236                                 self.lastseekstate = self.seekstate
1237                         self.setSeekState(self.SEEK_STATE_PAUSE)
1238
1239         def unPauseService(self):
1240                 print "unpause"
1241                 if self.seekstate == self.SEEK_STATE_PLAY:
1242                         return 0
1243                 self.setSeekState(self.SEEK_STATE_PLAY)
1244
1245         def doSeek(self, pts):
1246                 seekable = self.getSeek()
1247                 if seekable is None:
1248                         return
1249                 seekable.seekTo(pts)
1250
1251         def doSeekRelative(self, pts):
1252                 seekable = self.getSeek()
1253                 if seekable is None:
1254                         return
1255                 prevstate = self.seekstate
1256
1257                 if self.seekstate == self.SEEK_STATE_EOF:
1258                         if prevstate == self.SEEK_STATE_PAUSE:
1259                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1260                         else:
1261                                 self.setSeekState(self.SEEK_STATE_PLAY)
1262                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
1263                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
1264                         self.showAfterSeek()
1265
1266         def seekFwd(self):
1267                 seek = self.getSeek()
1268                 if seek and not (seek.isCurrentlySeekable() & 2):
1269                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1270                                 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
1271                                 self.fast_winding_hint_message_showed = True
1272                                 return
1273                         return 0 # trade as unhandled action
1274                 if self.seekstate == self.SEEK_STATE_PLAY:
1275                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1276                 elif self.seekstate == self.SEEK_STATE_PAUSE:
1277                         if len(config.seek.speeds_slowmotion.value):
1278                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
1279                         else:
1280                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1281                 elif self.seekstate == self.SEEK_STATE_EOF:
1282                         pass
1283                 elif self.isStateForward(self.seekstate):
1284                         speed = self.seekstate[1]
1285                         if self.seekstate[2]:
1286                                 speed /= self.seekstate[2]
1287                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
1288                         self.setSeekState(self.makeStateForward(speed))
1289                 elif self.isStateBackward(self.seekstate):
1290                         speed = -self.seekstate[1]
1291                         if self.seekstate[2]:
1292                                 speed /= self.seekstate[2]
1293                         speed = self.getLower(speed, config.seek.speeds_backward.value)
1294                         if speed:
1295                                 self.setSeekState(self.makeStateBackward(speed))
1296                         else:
1297                                 self.setSeekState(self.SEEK_STATE_PLAY)
1298                 elif self.isStateSlowMotion(self.seekstate):
1299                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
1300                         self.setSeekState(self.makeStateSlowMotion(speed))
1301
1302         def seekBack(self):
1303                 seek = self.getSeek()
1304                 if seek and not (seek.isCurrentlySeekable() & 2):
1305                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1306                                 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
1307                                 self.fast_winding_hint_message_showed = True
1308                                 return
1309                         return 0 # trade as unhandled action
1310                 seekstate = self.seekstate
1311                 if seekstate == self.SEEK_STATE_PLAY:
1312                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1313                 elif seekstate == self.SEEK_STATE_EOF:
1314                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1315                         self.doSeekRelative(-6)
1316                 elif seekstate == self.SEEK_STATE_PAUSE:
1317                         self.doSeekRelative(-1)
1318                 elif self.isStateForward(seekstate):
1319                         speed = seekstate[1]
1320                         if seekstate[2]:
1321                                 speed /= seekstate[2]
1322                         speed = self.getLower(speed, config.seek.speeds_forward.value)
1323                         if speed:
1324                                 self.setSeekState(self.makeStateForward(speed))
1325                         else:
1326                                 self.setSeekState(self.SEEK_STATE_PLAY)
1327                 elif self.isStateBackward(seekstate):
1328                         speed = -seekstate[1]
1329                         if seekstate[2]:
1330                                 speed /= seekstate[2]
1331                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1332                         self.setSeekState(self.makeStateBackward(speed))
1333                 elif self.isStateSlowMotion(seekstate):
1334                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1335                         if speed:
1336                                 self.setSeekState(self.makeStateSlowMotion(speed))
1337                         else:
1338                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1339
1340         def seekFwdManual(self):
1341                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1342
1343         def fwdSeekTo(self, minutes):
1344                 print "Seek", minutes, "minutes forward"
1345                 self.doSeekRelative(minutes * 60 * 90000)
1346
1347         def seekBackManual(self):
1348                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1349
1350         def rwdSeekTo(self, minutes):
1351                 print "rwdSeekTo"
1352                 self.doSeekRelative(-minutes * 60 * 90000)
1353
1354         def checkSkipShowHideLock(self):
1355                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1356
1357                 if config.usage.show_infobar_on_skip.value:
1358                         if self.lockedBecauseOfSkipping and not wantlock:
1359                                 self.unlockShow()
1360                                 self.lockedBecauseOfSkipping = False
1361
1362                         if wantlock and not self.lockedBecauseOfSkipping:
1363                                 self.lockShow()
1364                                 self.lockedBecauseOfSkipping = True
1365
1366         def calcRemainingTime(self):
1367                 seekable = self.getSeek()
1368                 if seekable is not None:
1369                         len = seekable.getLength()
1370                         try:
1371                                 tmp = self.cueGetEndCutPosition()
1372                                 if tmp:
1373                                         len = (False, tmp)
1374                         except:
1375                                 pass
1376                         pos = seekable.getPlayPosition()
1377                         speednom = self.seekstate[1] or 1
1378                         speedden = self.seekstate[2] or 1
1379                         if not len[0] and not pos[0]:
1380                                 if len[1] <= pos[1]:
1381                                         return 0
1382                                 time = (len[1] - pos[1])*speedden/(90*speednom)
1383                                 return time
1384                 return False
1385
1386         def __evEOF(self):
1387                 if self.seekstate == self.SEEK_STATE_EOF:
1388                         return
1389
1390                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1391                 seekstate = self.seekstate
1392                 if self.seekstate != self.SEEK_STATE_PAUSE:
1393                         self.setSeekState(self.SEEK_STATE_EOF)
1394
1395                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1396                         seekable = self.getSeek()
1397                         if seekable is not None:
1398                                 seekable.seekTo(-1)
1399                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1400                         self.doEofInternal(True)
1401                 else:
1402                         self.doEofInternal(False)
1403
1404         def doEofInternal(self, playing):
1405                 pass            # Defined in subclasses
1406
1407         def __evSOF(self):
1408                 self.setSeekState(self.SEEK_STATE_PLAY)
1409                 self.doSeek(0)
1410
1411 from Screens.PVRState import PVRState, TimeshiftState
1412
1413 class InfoBarPVRState:
1414         def __init__(self, screen=PVRState, force_show = False):
1415                 self.onPlayStateChanged.append(self.__playStateChanged)
1416                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1417                 self.onShow.append(self._mayShow)
1418                 self.onHide.append(self.pvrStateDialog.hide)
1419                 self.force_show = force_show
1420
1421         def _mayShow(self):
1422                 if self.shown and self.seekstate != self.SEEK_STATE_PLAY:
1423                         self.pvrStateDialog.show()
1424
1425         def __playStateChanged(self, state):
1426                 playstateString = state[3]
1427                 self.pvrStateDialog["state"].setText(playstateString)
1428
1429                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1430                 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1431                         self.pvrStateDialog.hide()
1432                 else:
1433                         self._mayShow()
1434
1435 class InfoBarTimeshiftState(InfoBarPVRState):
1436         def __init__(self):
1437                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1438                 self.__hideTimer = eTimer()
1439                 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1440
1441         def _mayShow(self):
1442                 if self.shown and self.timeshiftEnabled():
1443                         self.pvrStateDialog.show()
1444                         if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1445                                 self.__hideTimer.start(5*1000, True)
1446
1447         def __hideTimeshiftState(self):
1448                 self.pvrStateDialog.hide()
1449
1450 class InfoBarShowMovies:
1451
1452         # i don't really like this class.
1453         # it calls a not further specified "movie list" on up/down/movieList,
1454         # so this is not more than an action map
1455         def __init__(self):
1456                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1457                         {
1458                                 "movieList": (self.showMovies, _("Open the movie list")),
1459                                 "up": (self.up, _("Open the movie list")),
1460                                 "down": (self.down, _("Open the movie list"))
1461                         })
1462
1463 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1464
1465 # Hrmf.
1466 #
1467 # Timeshift works the following way:
1468 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1469 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1470 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1471 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1472 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1473 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1474 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1475 #
1476
1477 # in other words:
1478 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1479 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1480 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1481 # - the user can now PVR around
1482 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1483 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1484 # after!
1485 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1486 # - if the user rewinds, or press pause, timeshift will be activated again
1487
1488 # note that a timeshift can be enabled ("recording") and
1489 # activated (currently time-shifting).
1490
1491 class InfoBarTimeshift:
1492         def __init__(self):
1493                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1494                         {
1495                                 "timeshiftStart": (self.startTimeshift, _("Start timeshift")),  # the "yellow key"
1496                                 "timeshiftStop": (self.stopTimeshift, _("Stop timeshift"))      # currently undefined :), probably 'TV'
1497                         }, prio=1)
1498                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1499                         {
1500                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1501                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1502                         }, prio=-1) # priority over record
1503
1504                 self["TimeshiftActivateActions"].setEnabled(False)
1505                 self.ts_rewind_timer = eTimer()
1506                 self.ts_rewind_timer.callback.append(self.rewindService)
1507                 self.ts_start_delay_timer = eTimer()
1508                 self.ts_start_delay_timer.callback.append(self.startTimeshiftWithoutPause)
1509                 self.save_timeshift_file = False
1510                 self.timeshift_was_activated = False
1511
1512                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1513                         {
1514                                 iPlayableService.evStart: self.__serviceStarted,
1515                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1516                                 iPlayableService.evEnd: self.__serviceEnd
1517                         })
1518
1519         def getTimeshift(self):
1520                 service = self.session.nav.getCurrentService()
1521                 return service and service.timeshift()
1522
1523         def timeshiftEnabled(self):
1524                 ts = self.getTimeshift()
1525                 return ts and ts.isTimeshiftEnabled()
1526
1527         def startTimeshift(self, pauseService = True):
1528                 print "enable timeshift"
1529                 ts = self.getTimeshift()
1530                 if ts is None:
1531                         if not pauseService and not int(config.usage.timeshift_start_delay.value):
1532                                 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR, simple = True)
1533                         print "no ts interface"
1534                         return 0
1535
1536                 if ts.isTimeshiftEnabled():
1537                         print "hu, timeshift already enabled?"
1538                 else:
1539                         if not ts.startTimeshift():
1540                                 # we remove the "relative time" for now.
1541                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1542
1543                                 if pauseService:
1544                                         # PAUSE.
1545                                         #self.setSeekState(self.SEEK_STATE_PAUSE)
1546                                         self.activateTimeshiftEnd(False)
1547
1548                                 # enable the "TimeshiftEnableActions", which will override
1549                                 # the startTimeshift actions
1550                                 self.__seekableStatusChanged()
1551
1552                                 # get current timeshift filename and calculate new
1553                                 self.save_timeshift_file = False
1554                                 self.save_timeshift_in_movie_dir = False
1555                                 self.current_timeshift_filename = ts.getTimeshiftFilename()
1556                                 self.new_timeshift_filename = self.generateNewTimeshiftFileName()
1557                         else:
1558                                 print "timeshift failed"
1559
1560         def startTimeshiftWithoutPause(self):
1561                 self.startTimeshift(False)
1562
1563         def stopTimeshift(self):
1564                 ts = self.getTimeshift()
1565                 if ts and ts.isTimeshiftEnabled():
1566                         if int(config.usage.timeshift_start_delay.value):
1567                                 ts.switchToLive()
1568                         else:
1569                                 self.checkTimeshiftRunning(self.stopTimeshiftcheckTimeshiftRunningCallback)
1570                 else:
1571                         return 0
1572
1573         def stopTimeshiftcheckTimeshiftRunningCallback(self, answer):
1574                 ts = self.getTimeshift()
1575                 if answer and ts:
1576                         ts.stopTimeshift()
1577                         self.pvrStateDialog.hide()
1578
1579                         # disable actions
1580                         self.__seekableStatusChanged()
1581
1582         # activates timeshift, and seeks to (almost) the end
1583         def activateTimeshiftEnd(self, back = True):
1584                 ts = self.getTimeshift()
1585                 print "activateTimeshiftEnd"
1586
1587                 if ts is None:
1588                         return
1589
1590                 if ts.isTimeshiftActive():
1591                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1592                         self.pauseService()
1593                 else:
1594                         print "play, ..."
1595                         ts.activateTimeshift() # activate timeshift will automatically pause
1596                         self.setSeekState(self.SEEK_STATE_PAUSE)
1597                         seekable = self.getSeek()
1598                         if seekable is not None:
1599                                 seekable.seekTo(-90000) # seek approx. 1 sec before end
1600                         self.timeshift_was_activated = True
1601                 if back:
1602                         self.ts_rewind_timer.start(200, 1)
1603
1604         def rewindService(self):
1605                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1606
1607         # generates only filename without path
1608         def generateNewTimeshiftFileName(self):
1609                 name = "timeshift record"
1610                 info = { }
1611                 self.getProgramInfoAndEvent(info, name)
1612
1613                 serviceref = info["serviceref"]
1614
1615                 service_name = ""
1616                 if isinstance(serviceref, eServiceReference):
1617                         service_name = ServiceReference(serviceref).getServiceName()
1618                 begin_date = strftime("%Y%m%d %H%M", localtime(time()))
1619                 filename = begin_date + " - " + service_name
1620
1621                 if config.recording.filename_composition.value == "short":
1622                         filename = strftime("%Y%m%d", localtime(time())) + " - " + info["name"]
1623                 elif config.recording.filename_composition.value == "long":
1624                         filename += " - " + info["name"] + " - " + info["description"]
1625                 else:
1626                         filename += " - " + info["name"] # standard
1627
1628                 if config.recording.ascii_filenames.value:
1629                         filename = ASCIItranslit.legacyEncode(filename)
1630
1631                 print "New timeshift filename: ", filename
1632                 return filename
1633
1634         # same as activateTimeshiftEnd, but pauses afterwards.
1635         def activateTimeshiftEndAndPause(self):
1636                 print "activateTimeshiftEndAndPause"
1637                 #state = self.seekstate
1638                 self.activateTimeshiftEnd(False)
1639
1640         def __seekableStatusChanged(self):
1641                 self["TimeshiftActivateActions"].setEnabled(not self.isSeekable() and self.timeshiftEnabled())
1642                 state = self.getSeek() is not None and self.timeshiftEnabled()
1643                 self["SeekActions"].setEnabled(state)
1644                 if not state:
1645                         self.setSeekState(self.SEEK_STATE_PLAY)
1646                 self.restartSubtitle()
1647
1648         def __serviceStarted(self):
1649                 self.pvrStateDialog.hide()
1650                 self.__seekableStatusChanged()
1651                 if self.ts_start_delay_timer.isActive():
1652                         self.ts_start_delay_timer.stop()
1653                 if int(config.usage.timeshift_start_delay.value):
1654                         self.ts_start_delay_timer.start(int(config.usage.timeshift_start_delay.value) * 1000, True)
1655
1656         def checkTimeshiftRunning(self, returnFunction):
1657                 if self.timeshiftEnabled() and config.usage.check_timeshift.value and self.timeshift_was_activated:
1658                         message = _("Stop timeshift?")
1659                         if not self.save_timeshift_file:
1660                                 choice = [(_("yes"), "stop"), (_("no"), "continue"), (_("Yes and save"), "save"), (_("Yes and save in movie dir"), "save_movie")]
1661                         else:
1662                                 choice = [(_("yes"), "stop"), (_("no"), "continue")]
1663                                 message += "\n" + _("Reminder, you have chosen to save timeshift file.")
1664                         self.session.openWithCallback(boundFunction(self.checkTimeshiftRunningCallback, returnFunction), MessageBox, message, simple = True, list = choice)
1665                 else:
1666                         returnFunction(True)
1667
1668         def checkTimeshiftRunningCallback(self, returnFunction, answer):
1669                 if answer:
1670                         if "movie" in answer:
1671                                 self.save_timeshift_in_movie_dir = True
1672                         if "save" in answer:
1673                                 self.save_timeshift_file = True
1674                                 ts = self.getTimeshift()
1675                                 if ts:
1676                                         ts.saveTimeshiftFile()
1677                                         del ts
1678                         if "continue" not in answer:
1679                                 self.saveTimeshiftFiles()
1680                 returnFunction(answer and answer != "continue")
1681
1682         # renames/moves timeshift files if requested
1683         def __serviceEnd(self):
1684                 self.saveTimeshiftFiles()
1685                 self.timeshift_was_activated = False
1686
1687         def saveTimeshiftFiles(self):
1688                 if self.save_timeshift_file and self.current_timeshift_filename and self.new_timeshift_filename:
1689                         if config.usage.timeshift_path.value and not self.save_timeshift_in_movie_dir:
1690                                 dirname = config.usage.timeshift_path.value
1691                         else:
1692                                 dirname = defaultMoviePath()
1693                         filename = getRecordingFilename(self.new_timeshift_filename, dirname) + ".ts"
1694
1695                         fileList = []
1696                         fileList.append((self.current_timeshift_filename, filename))
1697                         if fileExists(self.current_timeshift_filename + ".sc"):
1698                                 fileList.append((self.current_timeshift_filename + ".sc", filename + ".sc"))
1699                         if fileExists(self.current_timeshift_filename + ".cuts"):
1700                                 fileList.append((self.current_timeshift_filename + ".cuts", filename + ".cuts"))
1701
1702                         moveFiles(fileList)
1703                         self.save_timeshift_file = False
1704
1705 from Screens.PiPSetup import PiPSetup
1706
1707 class InfoBarExtensions:
1708         EXTENSION_SINGLE = 0
1709         EXTENSION_LIST = 1
1710
1711         def __init__(self):
1712                 self.list = []
1713
1714                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1715                         {
1716                                 "extensions": (self.showExtensionSelection, _("Show extensions...")),
1717                         }, 1) # lower priority
1718
1719         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1720                 self.list.append((type, extension, key))
1721
1722         def updateExtension(self, extension, key = None):
1723                 self.extensionsList.append(extension)
1724                 if key is not None:
1725                         if self.extensionKeys.has_key(key):
1726                                 key = None
1727
1728                 if key is None:
1729                         for x in self.availableKeys:
1730                                 if not self.extensionKeys.has_key(x):
1731                                         key = x
1732                                         break
1733
1734                 if key is not None:
1735                         self.extensionKeys[key] = len(self.extensionsList) - 1
1736
1737         def updateExtensions(self):
1738                 self.extensionsList = []
1739                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1740                 self.extensionKeys = {}
1741                 for x in self.list:
1742                         if x[0] == self.EXTENSION_SINGLE:
1743                                 self.updateExtension(x[1], x[2])
1744                         else:
1745                                 for y in x[1]():
1746                                         self.updateExtension(y[0], y[1])
1747
1748
1749         def showExtensionSelection(self):
1750                 self.updateExtensions()
1751                 extensionsList = self.extensionsList[:]
1752                 keys = []
1753                 list = []
1754                 for x in self.availableKeys:
1755                         if self.extensionKeys.has_key(x):
1756                                 entry = self.extensionKeys[x]
1757                                 extension = self.extensionsList[entry]
1758                                 if extension[2]():
1759                                         name = str(extension[0]())
1760                                         list.append((extension[0](), extension))
1761                                         keys.append(x)
1762                                         extensionsList.remove(extension)
1763                                 else:
1764                                         extensionsList.remove(extension)
1765                 list.extend([(x[0](), x) for x in extensionsList])
1766
1767                 keys += [""] * len(extensionsList)
1768                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1769
1770         def extensionCallback(self, answer):
1771                 if answer is not None:
1772                         answer[1][1]()
1773
1774 from Tools.BoundFunction import boundFunction
1775 import inspect
1776
1777 # depends on InfoBarExtensions
1778
1779 class InfoBarPlugins:
1780         def __init__(self):
1781                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1782
1783         def getPluginName(self, name):
1784                 return name
1785
1786         def getPluginList(self):
1787                 l = []
1788                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1789                         args = inspect.getargspec(p.__call__)[0]
1790                         if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1791                                 l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1792                 l.sort(key = lambda e: e[2]) # sort by name
1793                 return l
1794
1795         def runPlugin(self, plugin):
1796                 if isinstance(self, InfoBarChannelSelection):
1797                         plugin(session = self.session, servicelist = self.servicelist)
1798                 else:
1799                         plugin(session = self.session)
1800
1801 from Components.Task import job_manager
1802 class InfoBarJobman:
1803         def __init__(self):
1804                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1805
1806         def getJobList(self):
1807                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1808
1809         def getJobName(self, job):
1810                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1811
1812         def showJobView(self, job):
1813                 from Screens.TaskView import JobView
1814                 job_manager.in_background = False
1815                 self.session.openWithCallback(self.JobViewCB, JobView, job)
1816
1817         def JobViewCB(self, in_background):
1818                 job_manager.in_background = in_background
1819
1820 # depends on InfoBarExtensions
1821 class InfoBarPiP:
1822         def __init__(self):
1823                 try:
1824                         self.session.pipshown
1825                 except:
1826                         self.session.pipshown = False
1827                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1828                         self["PiPActions"] = HelpableActionMap(self, "InfobarPiPActions",
1829                                 {
1830                                         "activatePiP": (self.showPiP, _("Activate PiP")),
1831                                 })
1832                         if (self.allowPiP):
1833                                 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1834                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1835                                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1836                                 self.addExtension((self.getTogglePipzapName, self.togglePipzap, self.pipShown), "red")
1837                         else:
1838                                 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1839                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1840
1841         def pipShown(self):
1842                 return self.session.pipshown
1843
1844         def pipHandles0Action(self):
1845                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1846
1847         def getShowHideName(self):
1848                 if self.session.pipshown:
1849                         return _("Disable Picture in Picture")
1850                 else:
1851                         return _("Activate Picture in Picture")
1852
1853         def getSwapName(self):
1854                 return _("Swap services")
1855
1856         def getMoveName(self):
1857                 return _("Move Picture in Picture")
1858
1859         def getTogglePipzapName(self):
1860                 slist = self.servicelist
1861                 if slist and slist.dopipzap:
1862                         return _("Zap focus to main screen")
1863                 return _("Zap focus to Picture in Picture")
1864
1865
1866         def togglePipzap(self):
1867                 if not self.session.pipshown:
1868                         self.showPiP()
1869                 slist = self.servicelist
1870                 if slist:
1871                         slist.togglePipzap()
1872
1873         def showPiP(self):
1874                 if self.session.pipshown:
1875                         slist = self.servicelist
1876                         if slist and slist.dopipzap:
1877                                 slist.togglePipzap()
1878                         del self.session.pip
1879                         self.session.pipshown = False
1880                 else:
1881                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1882                         self.session.pip.show()
1883                         newservice = self.servicelist.servicelist.getCurrent()
1884                         if self.session.pip.playService(newservice):
1885                                 self.session.pipshown = True
1886                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1887                         else:
1888                                 self.session.pipshown = False
1889                                 del self.session.pip
1890
1891         def swapPiP(self):
1892                 swapservice = self.session.nav.getCurrentlyPlayingServiceOrGroup()
1893                 pipref = self.session.pip.getCurrentService()
1894                 if swapservice and pipref and pipref.toString() != swapservice.toString():
1895                         currentServicePath = self.servicelist.getCurrentServicePath()
1896                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)    
1897                         self.session.pip.playService(swapservice)
1898                         self.session.nav.stopService() # stop portal
1899                         self.session.nav.playService(pipref) # start subservice
1900                         self.session.pip.servicePath = currentServicePath
1901                         if self.servicelist.dopipzap:
1902                                 # This unfortunately won't work with subservices
1903                                 self.servicelist.setCurrentSelection(self.session.pip.getCurrentService())
1904
1905         def movePiP(self):
1906                 self.session.open(PiPSetup, pip = self.session.pip)
1907
1908         def pipDoHandle0Action(self):
1909                 use = config.usage.pip_zero_button.value
1910                 if "swap" == use:
1911                         self.swapPiP()
1912                 elif "swapstop" == use:
1913                         self.swapPiP()
1914                         self.showPiP()
1915                 elif "stop" == use:
1916                         self.showPiP()
1917
1918 from RecordTimer import parseEvent, RecordTimerEntry
1919
1920 class InfoBarInstantRecord:
1921         """Instant Record - handles the instantRecord action in order to
1922         start/stop instant records"""
1923         def __init__(self):
1924                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1925                         {
1926                                 "instantRecord": (self.instantRecord, _("Instant recording...")),
1927                         })
1928                 if isStandardInfoBar(self):
1929                         self.recording = []
1930                 else:
1931                         from Screens.InfoBar import InfoBar
1932                         InfoBarInstance = InfoBar.instance
1933                         if InfoBarInstance:
1934                                 self.recording = InfoBarInstance.recording
1935
1936         def stopCurrentRecording(self, entry = -1):
1937                 if entry is not None and entry != -1:
1938                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1939                         self.recording.remove(self.recording[entry])
1940
1941         def getProgramInfoAndEvent(self, info, name):
1942                 info["serviceref"] = self.session.nav.getCurrentlyPlayingServiceOrGroup()
1943
1944                 # try to get event info
1945                 event = None
1946                 try:
1947                         service = self.session.nav.getCurrentService()
1948                         epg = eEPGCache.getInstance()
1949                         event = epg.lookupEventTime(info["serviceref"], -1, 0)
1950                         if event is None:
1951                                 event = service.info().getEvent(0)
1952                 except:
1953                         pass
1954
1955                 info["event"] = event
1956                 info["name"]  = name
1957                 info["description"] = ""
1958                 info["eventid"] = None
1959
1960                 if event is not None:
1961                         curEvent = parseEvent(event)
1962                         info["name"] = curEvent[2]
1963                         info["description"] = curEvent[3]
1964                         info["eventid"] = curEvent[4]
1965                         info["end"] = curEvent[1]
1966
1967
1968         def startInstantRecording(self, limitEvent = False):
1969                 begin = int(time())
1970                 end = begin + 3600      # dummy
1971                 name = "instant record"
1972                 info = { }
1973
1974                 self.getProgramInfoAndEvent(info, name)
1975                 serviceref = info["serviceref"]
1976                 event = info["event"]
1977
1978                 if event is not None:
1979                         if limitEvent:
1980                                 end = info["end"]
1981                 else:
1982                         if limitEvent:
1983                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1984
1985                 if isinstance(serviceref, eServiceReference):
1986                         serviceref = ServiceReference(serviceref)
1987
1988                 recording = RecordTimerEntry(serviceref, begin, end, info["name"], info["description"], info["eventid"], dirname = preferredInstantRecordPath())
1989                 recording.dontSave = True
1990
1991                 if event is None or limitEvent == False:
1992                         recording.autoincrease = True
1993                         recording.setAutoincreaseEnd()
1994
1995                 simulTimerList = self.session.nav.RecordTimer.record(recording)
1996
1997                 if simulTimerList is None:      # no conflict
1998                         recording.autoincrease = False
1999                         self.recording.append(recording)
2000                 else:
2001                         if len(simulTimerList) > 1: # with other recording
2002                                 name = simulTimerList[1].name
2003                                 name_date = ' '.join((name, strftime('%F %T', localtime(simulTimerList[1].begin))))
2004                                 print "[TIMER] conflicts with", name_date
2005                                 recording.autoincrease = True   # start with max available length, then increment
2006                                 if recording.setAutoincreaseEnd():
2007                                         self.session.nav.RecordTimer.record(recording)
2008                                         self.recording.append(recording)
2009                                         self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
2010                                 else:
2011                                         self.session.open(MessageBox, _("Could not record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
2012                         else:
2013                                 self.session.open(MessageBox, _("Could not record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
2014                         recording.autoincrease = False
2015
2016         def isInstantRecordRunning(self):
2017                 print "self.recording:", self.recording
2018                 if self.recording:
2019                         for x in self.recording:
2020                                 if x.isRunning():
2021                                         return True
2022                 return False
2023
2024         def recordQuestionCallback(self, answer):
2025                 print "pre:\n", self.recording
2026
2027                 if answer is None or answer[1] == "no":
2028                         return
2029                 list = []
2030                 recording = self.recording[:]
2031                 for x in recording:
2032                         if not x in self.session.nav.RecordTimer.timer_list:
2033                                 self.recording.remove(x)
2034                         elif x.dontSave and x.isRunning():
2035                                 list.append((x, False))
2036
2037                 if answer[1] == "changeduration":
2038                         if len(self.recording) == 1:
2039                                 self.changeDuration(0)
2040                         else:
2041                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
2042                 elif answer[1] == "changeendtime":
2043                         if len(self.recording) == 1:
2044                                 self.setEndtime(0)
2045                         else:
2046                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
2047                 elif answer[1] == "timer":
2048                         import TimerEdit
2049                         self.session.open(TimerEdit.TimerEditList)
2050                 elif answer[1] == "stop":
2051                         self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
2052                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
2053                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
2054                         if answer[1] == "manualduration":
2055                                 self.changeDuration(len(self.recording)-1)
2056                         elif answer[1] == "manualendtime":
2057                                 self.setEndtime(len(self.recording)-1)
2058                 elif "timeshift" in answer[1]:
2059                         ts = self.getTimeshift()
2060                         if ts:
2061                                 ts.saveTimeshiftFile()
2062                                 self.save_timeshift_file = True
2063                                 if "movie" in answer[1]:
2064                                         self.save_timeshift_in_movie_dir = True
2065                 print "after:\n", self.recording
2066
2067         def setEndtime(self, entry):
2068                 if entry is not None and entry >= 0:
2069                         self.selectedEntry = entry
2070                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
2071                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
2072                         dlg.setTitle(_("Please change recording endtime"))
2073
2074         def TimeDateInputClosed(self, ret):
2075                 if len(ret) > 1:
2076                         if ret[0]:
2077                                 print "stopping recording at", strftime("%F %T", localtime(ret[1]))
2078                                 if self.recording[self.selectedEntry].end != ret[1]:
2079                                         self.recording[self.selectedEntry].autoincrease = False
2080                                 self.recording[self.selectedEntry].end = ret[1]
2081                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
2082
2083         def changeDuration(self, entry):
2084                 if entry is not None and entry >= 0:
2085                         self.selectedEntry = entry
2086                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
2087
2088         def inputCallback(self, value):
2089                 if value is not None:
2090                         print "stopping recording after", int(value), "minutes."
2091                         entry = self.recording[self.selectedEntry]
2092                         if int(value) != 0:
2093                                 entry.autoincrease = False
2094                         entry.end = int(time()) + 60 * int(value)
2095                         self.session.nav.RecordTimer.timeChanged(entry)
2096
2097         def isTimerRecordRunning(self):
2098                 identical = timers = 0
2099                 for timer in self.session.nav.RecordTimer.timer_list:
2100                         if timer.isRunning() and not timer.justplay:
2101                                 timers += 1
2102                                 if self.recording:
2103                                         for x in self.recording:
2104                                                 if x.isRunning() and x == timer:
2105                                                         identical += 1
2106                 return timers > identical
2107
2108         def instantRecord(self):
2109                 pirr = preferredInstantRecordPath()
2110                 if not findSafeRecordPath(pirr) and not findSafeRecordPath(defaultMoviePath()):
2111                         if not pirr:
2112                                 pirr = ""
2113                         self.session.open(MessageBox, _("Missing ") + "\n" + pirr +
2114                                                  "\n" + _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
2115                         return
2116
2117                 if isStandardInfoBar(self):
2118                         common = ((_("Add recording (stop after current event)"), "event"),
2119                                 (_("Add recording (indefinitely)"), "indefinitely"),
2120                                 (_("Add recording (enter recording duration)"), "manualduration"),
2121                                 (_("Add recording (enter recording endtime)"), "manualendtime"),)
2122                 else:
2123                         common = ()
2124                 if self.isInstantRecordRunning():
2125                         title =_("A recording is currently running.\nWhat do you want to do?")
2126                         list = ((_("Stop recording"), "stop"),) + common + \
2127                                 ((_("Change recording (duration)"), "changeduration"),
2128                                 (_("Change recording (endtime)"), "changeendtime"),)
2129                         if self.isTimerRecordRunning():
2130                                 list += ((_("Stop timer recording"), "timer"),)
2131                         list += ((_("Do nothing"), "no"),)
2132                 else:
2133                         title=_("Start recording?")
2134                         list = common
2135                         if self.isTimerRecordRunning():
2136                                 list += ((_("Stop timer recording"), "timer"),)
2137                         if isStandardInfoBar(self):
2138                                 list += ((_("Do not record"), "no"),)
2139                 if isStandardInfoBar(self) and self.timeshiftEnabled():
2140                         list = list + ((_("Save timeshift file"), "timeshift"),
2141                                 (_("Save timeshift file in movie directory"), "timeshift_movie"))
2142                 if list:
2143                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=title, list=list)
2144                 else:
2145                         return 0
2146
2147 from Tools.ISO639 import LanguageCodes
2148
2149 class InfoBarAudioSelection:
2150         def __init__(self):
2151                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
2152                         {
2153                                 "audioSelection": (self.audioSelection, _("Audio options...")),
2154                         })
2155
2156         def audioSelection(self):
2157                 from Screens.AudioSelection import AudioSelection
2158                 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
2159
2160         def audioSelected(self, ret=None):
2161                 print "[infobar::audioSelected]", ret
2162
2163 class InfoBarSubserviceSelection:
2164         def __init__(self):
2165                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
2166                         {
2167                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
2168                         })
2169
2170                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
2171                         {
2172                                 "nextSubservice": (self.nextSubservice, _("Switch to next sub service")),
2173                                 "prevSubservice": (self.prevSubservice, _("Switch to previous sub service"))
2174                         }, -1)
2175                 self["SubserviceQuickzapAction"].setEnabled(False)
2176
2177                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2178                         {
2179                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
2180                         })
2181                 self.onClose.append(self.__removeNotifications)
2182
2183                 self.bsel = None
2184
2185         def __removeNotifications(self):
2186                 self.session.nav.event.remove(self.checkSubservicesAvail)
2187
2188         def checkSubservicesAvail(self):
2189                 service = self.session.nav.getCurrentService()
2190                 subservices = service and service.subServices()
2191                 if not subservices or subservices.getNumberOfSubservices() == 0:
2192                         self["SubserviceQuickzapAction"].setEnabled(False)
2193
2194         def nextSubservice(self):
2195                 self.changeSubservice(+1)
2196
2197         def prevSubservice(self):
2198                 self.changeSubservice(-1)
2199
2200         def changeSubservice(self, direction):
2201                 service = self.session.nav.getCurrentService()
2202                 subservices = service and service.subServices()
2203                 n = subservices and subservices.getNumberOfSubservices()
2204                 if n and n > 0:
2205                         selection = -1
2206                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
2207                         idx = 0
2208                         while idx < n:
2209                                 if subservices.getSubservice(idx).toString() == ref.toString():
2210                                         selection = idx
2211                                         break
2212                                 idx += 1
2213                         if selection != -1:
2214                                 selection += direction
2215                                 if selection >= n:
2216                                         selection=0
2217                                 elif selection < 0:
2218                                         selection=n-1
2219                                 newservice = subservices.getSubservice(selection)
2220                                 if newservice.valid():
2221                                         del subservices
2222                                         del service
2223                                         self.session.nav.playService(newservice, False)
2224
2225         def subserviceSelection(self):
2226                 service = self.session.nav.getCurrentService()
2227                 subservices = service and service.subServices()
2228                 self.bouquets = self.servicelist.getBouquetList()
2229                 n = subservices and subservices.getNumberOfSubservices()
2230                 selection = 0
2231                 if n and n > 0:
2232                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
2233                         tlist = []
2234                         idx = 0
2235                         while idx < n:
2236                                 i = subservices.getSubservice(idx)
2237                                 if i.toString() == ref.toString():
2238                                         selection = idx
2239                                 tlist.append((i.getName(), i))
2240                                 idx += 1
2241
2242                         if self.bouquets and len(self.bouquets):
2243                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
2244                                 if config.usage.multibouquet.value:
2245                                         tlist = [(_("Quick zap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
2246                                 else:
2247                                         tlist = [(_("Quick zap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
2248                                 selection += 3
2249                         else:
2250                                 tlist = [(_("Quick zap"), "quickzap", service.subServices()), ("--", "")] + tlist
2251                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
2252                                 selection += 2
2253
2254                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a sub service..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
2255
2256         def subserviceSelected(self, service):
2257                 del self.bouquets
2258                 if not service is None:
2259                         if isinstance(service[1], str):
2260                                 if service[1] == "quickzap":
2261                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
2262                                         self.session.open(SubservicesQuickzap, service[2])
2263                         else:
2264                                 self["SubserviceQuickzapAction"].setEnabled(True)
2265                                 self.session.nav.playService(service[1], False)
2266
2267         def addSubserviceToBouquetCallback(self, service):
2268                 if len(service) > 1 and isinstance(service[1], eServiceReference):
2269                         self.selectedSubservice = service
2270                         if self.bouquets is None:
2271                                 cnt = 0
2272                         else:
2273                                 cnt = len(self.bouquets)
2274                         if cnt > 1: # show bouquet list
2275                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
2276                         elif cnt == 1: # add to only one existing bouquet
2277                                 self.addSubserviceToBouquet(self.bouquets[0][1])
2278                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
2279
2280         def bouquetSelClosed(self, confirmed):
2281                 self.bsel = None
2282                 del self.selectedSubservice
2283                 if confirmed:
2284                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
2285
2286         def addSubserviceToBouquet(self, dest):
2287                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
2288                 if self.bsel:
2289                         self.bsel.close(True)
2290                 else:
2291                         del self.selectedSubservice
2292
2293 class InfoBarRedButton:
2294         def __init__(self):
2295                 self["RedButtonActions"] = HelpableActionMap(self, "InfobarRedButtonActions",
2296                         {
2297                                 "activateRedButton": (self.activateRedButton, _("Red button...")),
2298                         })
2299                 self.onHBBTVActivation = [ ]
2300                 self.onRedButtonActivation = [ ]
2301
2302         def activateRedButton(self):
2303                 service = self.session.nav.getCurrentService()
2304                 info = service and service.info()
2305                 if info and info.getInfoString(iServiceInformation.sHBBTVUrl) != "":
2306                         for x in self.onHBBTVActivation:
2307                                 x()
2308                 elif False: # TODO: other red button services
2309                         for x in self.onRedButtonActivation:
2310                                 x()
2311
2312 class InfoBarTimerButton:
2313         def __init__(self):
2314                 self["TimerButtonActions"] = HelpableActionMap(self, "InfobarTimerButtonActions",
2315                         {
2316                                 "timerSelection": (self.timerSelection, _("Timer selection...")),
2317                         })
2318
2319         def timerSelection(self):
2320                 from Screens.TimerEdit import TimerEditList
2321                 self.session.open(TimerEditList)
2322
2323 class InfoBarVmodeButton:
2324         def __init__(self):
2325                 self["VmodeButtonActions"] = HelpableActionMap(self, "InfobarVmodeButtonActions",
2326                         {
2327                                 "vmodeSelection": (self.vmodeSelection, _("Letterbox zoom")),
2328                         })
2329
2330         def vmodeSelection(self):
2331                 self.session.open(VideoMode)
2332
2333 class VideoMode(Screen):
2334         def __init__(self,session):
2335                 Screen.__init__(self, session)
2336                 self["videomode"] = Label()
2337
2338                 self["actions"] = NumberActionMap( [ "InfobarVmodeButtonActions" ],
2339                         {
2340                                 "vmodeSelection": self.selectVMode
2341                         })
2342
2343                 self.Timer = eTimer()
2344                 self.Timer.callback.append(self.quit)
2345                 self.selectVMode()
2346
2347         def selectVMode(self):
2348                 policy = config.av.policy_43
2349                 if self.isWideScreen():
2350                         policy = config.av.policy_169
2351                 idx = policy.choices.index(policy.value)
2352                 idx = (idx + 1) % len(policy.choices)
2353                 policy.value = policy.choices[idx]
2354                 self["videomode"].setText(policy.value)
2355                 self.Timer.start(1000, True)
2356
2357         def isWideScreen(self):
2358                 from Components.Converter.ServiceInfo import WIDESCREEN
2359                 service = self.session.nav.getCurrentService()
2360                 info = service and service.info()
2361                 return info.getInfo(iServiceInformation.sAspect) in WIDESCREEN
2362
2363         def quit(self):
2364                 self.Timer.stop()
2365                 self.close()
2366
2367 class InfoBarAdditionalInfo:
2368         def __init__(self):
2369
2370                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
2371                 self["TimeshiftPossible"] = self["RecordingPossible"]
2372                 self["ExtensionsAvailable"] = Boolean(fixed=1)
2373                 # TODO: these properties should be queried from the input device keymap
2374                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=0)
2375                 self["ShowAudioOnYellow"] = Boolean(fixed=0)
2376                 self["ShowRecordOnRed"] = Boolean(fixed=0)
2377
2378 class InfoBarNotifications:
2379         def __init__(self):
2380                 self.onExecBegin.append(self.checkNotifications)
2381                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
2382                 self.onClose.append(self.__removeNotification)
2383
2384         def __removeNotification(self):
2385                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
2386
2387         def checkNotificationsIfExecing(self):
2388                 if self.execing:
2389                         self.checkNotifications()
2390
2391         def checkNotifications(self):
2392                 notifications = Notifications.notifications
2393                 if notifications:
2394                         n = notifications[0]
2395
2396                         del notifications[0]
2397                         cb = n[0]
2398
2399                         if n[3].has_key("onSessionOpenCallback"):
2400                                 n[3]["onSessionOpenCallback"]()
2401                                 del n[3]["onSessionOpenCallback"]
2402
2403                         if cb:
2404                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
2405                         elif not Notifications.current_notifications and n[4] == "ZapError":
2406                                 if n[3].has_key("timeout"):
2407                                         del n[3]["timeout"]
2408                                 n[3]["enable_input"] = False
2409                                 dlg = self.session.instantiateDialog(n[1], *n[2], **n[3])
2410                                 self.hide()
2411                                 dlg.show()
2412                                 self.notificationDialog = dlg
2413                                 eActionMap.getInstance().bindAction('', -maxint - 1, self.keypressNotification)
2414                         else:
2415                                 dlg = self.session.open(n[1], *n[2], **n[3])
2416
2417                         # remember that this notification is currently active
2418                         d = (n[4], dlg)
2419                         Notifications.current_notifications.append(d)
2420                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
2421
2422         def closeNotificationInstantiateDialog(self):
2423                 if hasattr(self, "notificationDialog"):
2424                         self.session.deleteDialog(self.notificationDialog)
2425                         del self.notificationDialog
2426                         eActionMap.getInstance().unbindAction('', self.keypressNotification)
2427
2428         def keypressNotification(self, key, flag):
2429                 if flag:
2430                         self.closeNotificationInstantiateDialog()
2431
2432         def __notificationClosed(self, d):
2433                 Notifications.current_notifications.remove(d)
2434
2435 class InfoBarServiceNotifications:
2436         def __init__(self):
2437                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2438                         {
2439                                 iPlayableService.evEnd: self.serviceHasEnded
2440                         })
2441
2442         def serviceHasEnded(self):
2443                 print "service end!"
2444
2445                 try:
2446                         self.setSeekState(self.SEEK_STATE_PLAY)
2447                 except:
2448                         pass
2449
2450 class InfoBarCueSheetSupport:
2451         CUT_TYPE_IN = 0
2452         CUT_TYPE_OUT = 1
2453         CUT_TYPE_MARK = 2
2454         CUT_TYPE_LAST = 3
2455
2456         ENABLE_RESUME_SUPPORT = False
2457
2458         def __init__(self, actionmap = "InfobarCueSheetActions"):
2459                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
2460                         {
2461                                 "jumpPreviousMark": (self.jumpPreviousMark, _("Jump to previous marked position")),
2462                                 "jumpNextMark": (self.jumpNextMark, _("Jump to next marked position")),
2463                                 "toggleMark": (self.toggleMark, _("Toggle a cut mark at the current position"))
2464                         }, prio=1)
2465
2466                 self.cut_list = [ ]
2467                 self.is_closing = False
2468                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2469                         {
2470                                 iPlayableService.evStart: self.__serviceStarted,
2471                         })
2472
2473         def __serviceStarted(self):
2474                 if self.is_closing:
2475                         return
2476                 print "new service started! trying to download cuts!"
2477                 self.downloadCuesheet()
2478
2479                 if self.ENABLE_RESUME_SUPPORT:
2480                         for (pts, what) in self.cut_list:
2481                                 if what == self.CUT_TYPE_LAST:
2482                                         last = pts
2483                                         break
2484                         else:
2485                                 last = getResumePoint(self.session)
2486                         if last is None:
2487                                 return
2488                         # only resume if at least 10 seconds ahead, or <10 seconds before the end.
2489                         seekable = self.__getSeekable()
2490                         if seekable is None:
2491                                 return # Should not happen?
2492                         length = seekable.getLength() or (None,0)
2493                         print "seekable.getLength() returns:", length
2494                         # Hmm, this implies we don't resume if the length is unknown...
2495                         if (last > 900000) and (not length[1]  or (last < length[1] - 900000)):
2496                                 self.resume_point = last
2497                                 l = last / 90000
2498                                 if config.usage.on_movie_start.value == "ask" or not length[1]:
2499                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?") + "\n" + (_("Resume position at %s") % ("%d:%02d:%02d" % (l/3600, l%3600/60, l%60))), timeout=10)
2500                                 elif config.usage.on_movie_start.value == "resume":
2501 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2502 # TRANSLATORS: at the start of a movie, when the user has selected
2503 # TRANSLATORS: "Resume from last position" as start behavior.
2504 # TRANSLATORS: The purpose is to notify the user that the movie starts
2505 # TRANSLATORS: in the middle somewhere and not from the beginning.
2506 # TRANSLATORS: (Some translators seem to have interpreted it as a
2507 # TRANSLATORS: question or a choice, but it is a statement.)
2508                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2509
2510         def playLastCB(self, answer):
2511                 if answer == True:
2512                         self.doSeek(self.resume_point)
2513                 self.hideAfterResume()
2514
2515         def hideAfterResume(self):
2516                 if isinstance(self, InfoBarShowHide):
2517                         self.hide()
2518
2519         def __getSeekable(self):
2520                 service = self.session.nav.getCurrentService()
2521                 if service is None:
2522                         return None
2523                 return service.seek()
2524
2525         def cueGetCurrentPosition(self):
2526                 seek = self.__getSeekable()
2527                 if seek is None:
2528                         return None
2529                 r = seek.getPlayPosition()
2530                 if r[0]:
2531                         return None
2532                 return long(r[1])
2533
2534         def cueGetEndCutPosition(self):
2535                 ret = False
2536                 isin = True
2537                 for cp in self.cut_list:
2538                         if cp[1] == self.CUT_TYPE_OUT:
2539                                 if isin:
2540                                         isin = False
2541                                         ret = cp[0]
2542                         elif cp[1] == self.CUT_TYPE_IN:
2543                                 isin = True
2544                 return ret
2545
2546         def jumpPreviousNextMark(self, cmp, start=False):
2547                 current_pos = self.cueGetCurrentPosition()
2548                 if current_pos is None:
2549                         return False
2550                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2551                 if mark is not None:
2552                         pts = mark[0]
2553                 else:
2554                         return False
2555
2556                 self.doSeek(pts)
2557                 return True
2558
2559         def jumpPreviousMark(self):
2560                 # we add 5 seconds, so if the play position is <5s after
2561                 # the mark, the mark before will be used
2562                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2563
2564         def jumpNextMark(self):
2565                 if not self.jumpPreviousNextMark(lambda x: x-90000):
2566                         self.doSeek(-1)
2567
2568         def getNearestCutPoint(self, pts, cmp=abs, start=False):
2569                 # can be optimized
2570                 beforecut = True
2571                 nearest = None
2572                 bestdiff = -1
2573                 instate = True
2574                 if start:
2575                         bestdiff = cmp(0 - pts)
2576                         if bestdiff >= 0:
2577                                 nearest = [0, False]
2578                 for cp in self.cut_list:
2579                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2580                                 beforecut = False
2581                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
2582                                         diff = cmp(cp[0] - pts)
2583                                         if start and diff >= 0:
2584                                                 nearest = cp
2585                                                 bestdiff = diff
2586                                         else:
2587                                                 nearest = None
2588                                                 bestdiff = -1
2589                         if cp[1] == self.CUT_TYPE_IN:
2590                                 instate = True
2591                         elif cp[1] == self.CUT_TYPE_OUT:
2592                                 instate = False
2593                         elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2594                                 diff = cmp(cp[0] - pts)
2595                                 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2596                                         nearest = cp
2597                                         bestdiff = diff
2598                 return nearest
2599
2600         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2601                 current_pos = self.cueGetCurrentPosition()
2602                 if current_pos is None:
2603                         print "not seekable"
2604                         return
2605
2606                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2607
2608                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2609                         if onlyreturn:
2610                                 return nearest_cutpoint
2611                         if not onlyadd:
2612                                 self.removeMark(nearest_cutpoint)
2613                 elif not onlyremove and not onlyreturn:
2614                         self.addMark((current_pos, self.CUT_TYPE_MARK))
2615
2616                 if onlyreturn:
2617                         return None
2618
2619         def addMark(self, point):
2620                 insort(self.cut_list, point)
2621                 self.uploadCuesheet()
2622                 self.showAfterCuesheetOperation()
2623
2624         def removeMark(self, point):
2625                 self.cut_list.remove(point)
2626                 self.uploadCuesheet()
2627                 self.showAfterCuesheetOperation()
2628
2629         def showAfterCuesheetOperation(self):
2630                 if isinstance(self, InfoBarShowHide):
2631                         self.doShow()
2632
2633         def __getCuesheet(self):
2634                 service = self.session.nav.getCurrentService()
2635                 if service is None:
2636                         return None
2637                 return service.cueSheet()
2638
2639         def uploadCuesheet(self):
2640                 cue = self.__getCuesheet()
2641
2642                 if cue is None:
2643                         print "upload failed, no cuesheet interface"
2644                         return
2645                 cue.setCutList(self.cut_list)
2646
2647         def downloadCuesheet(self):
2648                 cue = self.__getCuesheet()
2649
2650                 if cue is None:
2651                         print "download failed, no cuesheet interface"
2652                         self.cut_list = [ ]
2653                 else:
2654                         self.cut_list = cue.getCutList()
2655
2656 class InfoBarSummary(Screen):
2657         skin = """
2658         <screen position="0,0" size="132,64">
2659                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2660                         <convert type="ClockToText">WithSeconds</convert>
2661                 </widget>
2662                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2663                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2664                         <convert type="ConditionalShowHide">Blink</convert>
2665                 </widget>
2666                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2667                         <convert type="ServiceName">Name</convert>
2668                 </widget>
2669                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2670                         <convert type="EventTime">Progress</convert>
2671                 </widget>
2672         </screen>"""
2673
2674 # for picon:  (path="piconlcd" will use LCD picons)
2675 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2676 #                       <convert type="ServiceName">Reference</convert>
2677 #               </widget>
2678
2679 class InfoBarSummarySupport:
2680         def __init__(self):
2681                 pass
2682
2683         def createSummary(self):
2684                 return InfoBarSummary
2685
2686 class InfoBarMoviePlayerSummary(Screen):
2687         skin = """
2688         <screen position="0,0" size="132,64">
2689                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2690                         <convert type="ClockToText">WithSeconds</convert>
2691                 </widget>
2692                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2693                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2694                         <convert type="ConditionalShowHide">Blink</convert>
2695                 </widget>
2696                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2697                         <convert type="ServiceName">Name</convert>
2698                 </widget>
2699                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2700                         <convert type="ServicePosition">Position</convert>
2701                 </widget>
2702         </screen>"""
2703
2704 class InfoBarMoviePlayerSummarySupport:
2705         def __init__(self):
2706                 pass
2707
2708         def createSummary(self):
2709                 return InfoBarMoviePlayerSummary
2710
2711 class InfoBarTeletextPlugin:
2712         def __init__(self):
2713                 self.teletext_plugin = None
2714
2715                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2716                         self.teletext_plugin = p
2717
2718                 if self.teletext_plugin is not None:
2719                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2720                                 {
2721                                         "startTeletext": (self.startTeletext, _("View teletext..."))
2722                                 })
2723                 else:
2724                         print "no teletext plugin found!"
2725
2726         def startTeletext(self):
2727                 self.teletext_plugin(session=self.session, infobar=self, service=self.session.nav.getCurrentService())
2728
2729 class InfoBarSubtitleSupport(object):
2730         def __init__(self):
2731                 object.__init__(self)
2732                 self["SubtitleSelectionAction"] = HelpableActionMap(self, "InfobarSubtitleSelectionActions",
2733                         {
2734                                 "subtitleSelection": (self.subtitleSelection, _("Subtitle selection...")),
2735                         })
2736
2737                 self.selected_subtitle = None
2738
2739                 if isStandardInfoBar(self):
2740                         self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2741                 else:
2742                         from Screens.InfoBar import InfoBar
2743                         self.subtitle_window = InfoBar.instance.subtitle_window
2744
2745                 self.subtitle_window.hide()
2746
2747                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2748                         {
2749                                 iPlayableService.evStart: self.__serviceChanged,
2750                                 iPlayableService.evEnd: self.__serviceChanged,
2751                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2752                         })
2753
2754         def getCurrentServiceSubtitle(self):
2755                 service = self.session.nav.getCurrentService()
2756                 return service and service.subtitle()
2757
2758         def subtitleSelection(self):
2759                 subtitle = self.getCurrentServiceSubtitle()
2760                 subtitlelist = subtitle and subtitle.getSubtitleList()
2761                 if self.selected_subtitle or subtitlelist and len(subtitlelist)>0:
2762                         from Screens.AudioSelection import SubtitleSelection
2763                         self.session.open(SubtitleSelection, self)
2764                 else:
2765                         return 0
2766
2767         def __serviceChanged(self):
2768                 if self.selected_subtitle:
2769                         self.selected_subtitle = None
2770                         self.subtitle_window.hide()
2771
2772         def __updatedInfo(self):
2773                 if not self.selected_subtitle:
2774                         subtitle = self.getCurrentServiceSubtitle()
2775                         cachedsubtitle = subtitle.getCachedSubtitle()
2776                         if cachedsubtitle:
2777                                 self.enableSubtitle(cachedsubtitle)
2778
2779         def enableSubtitle(self, selectedSubtitle):
2780                 subtitle = self.getCurrentServiceSubtitle()
2781                 self.selected_subtitle = selectedSubtitle
2782                 if subtitle and self.selected_subtitle:
2783                         subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2784                         self.subtitle_window.show()
2785                 else:
2786                         if subtitle:
2787                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2788                         self.subtitle_window.hide()
2789
2790         def restartSubtitle(self):
2791                 if self.selected_subtitle:
2792                         self.enableSubtitle(self.selected_subtitle)
2793
2794 class InfoBarServiceErrorPopupSupport:
2795         def __init__(self):
2796                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2797                         {
2798                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2799                                 iPlayableService.evTunedIn: self.__serviceStarted,
2800                                 iPlayableService.evStart: self.__serviceStarted
2801                         })
2802                 self.__serviceStarted()
2803
2804         def __serviceStarted(self):
2805                 self.closeNotificationInstantiateDialog()
2806                 self.last_error = None
2807                 Notifications.RemovePopup(id = "ZapError")
2808
2809         def __tuneFailed(self):
2810                 if not config.usage.hide_zap_errors.value:
2811                         service = self.session.nav.getCurrentService()
2812                         info = service and service.info()
2813                         error = info and info.getInfo(iServiceInformation.sDVBState)
2814
2815                         if error == self.last_error:
2816                                 error = None
2817                         else:
2818                                 self.last_error = error
2819
2820                         error = {
2821                                 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2822                                 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2823                                 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2824                                 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2825                                 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2826                                 eDVBServicePMTHandler.eventNewProgramInfo: None,
2827                                 eDVBServicePMTHandler.eventTuned: None,
2828                                 eDVBServicePMTHandler.eventSOF: None,
2829                                 eDVBServicePMTHandler.eventEOF: None,
2830                                 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2831                         }.get(error) #this returns None when the key not exist in the dict
2832
2833                         if error:
2834                                 self.closeNotificationInstantiateDialog()
2835                                 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2836
2837 class InfoBarPowersaver:
2838         def __init__(self):
2839                 self.inactivityTimer = eTimer()
2840                 self.inactivityTimer.callback.append(self.inactivityTimeout)
2841                 self.restartInactiveTimer()
2842                 self.sleepTimer = eTimer()
2843                 self.sleepTimer.callback.append(self.sleepTimerTimeout)
2844                 eActionMap.getInstance().bindAction('', -maxint - 1, self.keypress)
2845
2846         def keypress(self, key, flag):
2847                 if flag:
2848                         self.restartInactiveTimer()
2849
2850         def restartInactiveTimer(self):
2851                 time = abs(int(config.usage.inactivity_timer.value))
2852                 if time:
2853                         self.inactivityTimer.startLongTimer(time)
2854                 else:
2855                         self.inactivityTimer.stop()
2856
2857         def inactivityTimeout(self):
2858                 if config.usage.inactivity_timer_blocktime.value:
2859                         curtime = localtime(time())
2860                         if curtime.tm_year != "1970": #check if the current time is valid
2861                                 curtime = (curtime.tm_hour, curtime.tm_min, curtime.tm_sec)
2862                                 begintime = tuple(config.usage.inactivity_timer_blocktime_begin.value)
2863                                 endtime = tuple(config.usage.inactivity_timer_blocktime_end.value)
2864                                 if begintime <= endtime and (curtime >= begintime and curtime < endtime) or begintime > endtime and (curtime >= begintime or curtime < endtime):
2865                                         duration = (endtime[0]*3600 + endtime[1]*60) - (curtime[0]*3600 + curtime[1]*60 + curtime[2])
2866                                         if duration:
2867                                                 if duration < 0:
2868                                                         duration += 24*3600
2869                                                 self.inactivityTimer.startLongTimer(duration)
2870                                                 return
2871                 if Screens.Standby.inStandby:
2872                         self.inactivityTimeoutCallback(True)
2873                 else:
2874                         if int(config.usage.inactivity_timer.value) < 0:
2875                                 message = _("Your receiver will shutdown due to inactivity.")
2876                         else:
2877                                 message = _("Your receiver will got to standby due to inactivity.")
2878                         message += "\n" + _("Do you want this?")
2879                         self.session.openWithCallback(self.inactivityTimeoutCallback, MessageBox, message, timeout=60, simple = True)   
2880
2881         def inactivityTimeoutCallback(self, answer):
2882                 if answer:
2883                         self.goShutdownOrStandby(int(config.usage.inactivity_timer.value))
2884                 else:
2885                         print "[InfoBarPowersaver] abort"
2886
2887         def setSleepTimer(self, time):
2888                 print "[InfoBarPowersaver] set sleeptimer", time
2889                 if time:
2890                         if time < 0:
2891                                 message = _("And will shutdown your receiver over ")
2892                         else:
2893                                 message = _("And will put your receiver in standby over ")
2894                         m = abs(time / 60)
2895                         message = _("The sleep timer has been activated.") + "\n" + message + ngettext("%d minute", "%d minutes", m) % m
2896                         self.sleepTimer.startLongTimer(abs(time))
2897                 else:
2898                         message = _("The sleep timer has been disabled.")
2899                         self.sleepTimer.stop()
2900                 Notifications.AddPopup(message, type = MessageBox.TYPE_INFO, timeout = 5)
2901                 self.sleepTimerSetting = time
2902
2903         def sleepTimerTimeout(self):
2904                 if Screens.Standby.inStandby:
2905                         self.sleepTimerTimeoutCallback(True)
2906                 else:
2907                         list = [ (_("Yes"), True), (_("Extend sleeptimer 15 minutes"), "extend"), (_("No"), False) ]
2908                         if self.sleepTimerSetting < 0:
2909                                 message = _("Your receiver will shutdown due to the sleeptimer.")
2910                         elif self.sleepTimerSetting > 0:
2911                                 message = _("Your receiver will got to stand by due to the sleeptimer.")
2912                         message += "\n" + _("Do you want this?")
2913                         self.session.openWithCallback(self.sleepTimerTimeoutCallback, MessageBox, message, timeout=60, simple = True, list = list)      
2914
2915         def sleepTimerTimeoutCallback(self, answer):
2916                 if answer == "extend":
2917                         print "[InfoBarPowersaver] extend sleeptimer"
2918                         if self.sleepTimerSetting < 0:
2919                                 self.setSleepTimer(-900)
2920                         else:
2921                                 self.setSleepTimer(900)
2922                 elif answer:
2923                         self.goShutdownOrStandby(self.sleepTimerSetting)
2924                 else:
2925                         print "[InfoBarPowersaver] abort"
2926                         self.setSleepTimer(0)
2927
2928         def goShutdownOrStandby(self, value):
2929                 if value < 0:
2930                         if Screens.Standby.inStandby:
2931                                 print "[InfoBarPowersaver] already in standby now shut down"
2932                                 RecordTimerEntry.TryQuitMainloop(True)
2933                         elif not Screens.Standby.inTryQuitMainloop:
2934                                 print "[InfoBarPowersaver] goto shutdown"
2935                                 self.session.open(Screens.Standby.TryQuitMainloop, 1)
2936                 elif not Screens.Standby.inStandby:
2937                         print "[InfoBarPowersaver] goto standby"
2938                         self.session.open(Screens.Standby.Standby)