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