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