c21699edc250c3fe1ee2f396a97ebd52386289ba
[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.openServiceList()
718
719         def switchChannelDown(self):
720                 if "keep" not in config.usage.servicelist_cursor_behavior.value:
721                         self.servicelist.moveDown()
722                 self.openServiceList()
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.servicelist = self.session.instantiateDialog(ChannelSelection)
774                 self.session.execDialog(self.servicelist)
775
776 class InfoBarMenu:
777         """ Handles a menu action, to open the (main) menu """
778         def __init__(self):
779                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
780                         {
781                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
782                         })
783                 self.session.infobar = None
784
785         def mainMenu(self):
786                 print "loading mainmenu XML..."
787                 menu = mdom.getroot()
788                 assert menu.tag == "menu", "root element in menu must be 'menu'!"
789
790                 self.session.infobar = self
791                 # so we can access the currently active infobar from screens opened from within the mainmenu
792                 # at the moment used from the SubserviceSelection
793
794                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
795
796         def mainMenuClosed(self, *val):
797                 self.session.infobar = None
798
799 class InfoBarSimpleEventView:
800         """ Opens the Eventview for now/next """
801         def __init__(self):
802                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
803                         {
804                                 "showEventInfo": (self.openEventView, _("Show event details")),
805                                 "showEventInfoSingleEPG": (self.openEventView, _("Show event details")),
806                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
807                         })
808
809         def showEventInfoWhenNotVisible(self):
810                 if self.shown:
811                         self.openEventView()
812                 else:
813                         self.toggleShow()
814                         return 1
815
816         def openEventView(self):
817                 epglist = [ ]
818                 self.epglist = epglist
819                 service = self.session.nav.getCurrentService()
820                 ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
821                 info = service.info()
822                 ptr=info.getEvent(0)
823                 if ptr:
824                         epglist.append(ptr)
825                 ptr=info.getEvent(1)
826                 if ptr:
827                         epglist.append(ptr)
828                 if epglist:
829                         self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
830
831         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
832                 epglist = self.epglist
833                 if len(epglist) > 1:
834                         tmp = epglist[0]
835                         epglist[0] = epglist[1]
836                         epglist[1] = tmp
837                         setEvent(epglist[0])
838
839 class SimpleServicelist:
840         def __init__(self, services):
841                 self.services = services
842                 self.length = len(services)
843                 self.current = 0
844
845         def selectService(self, service):
846                 if not self.length:
847                         self.current = -1
848                         return False
849                 else:
850                         self.current = 0
851                         while self.services[self.current].ref != service:
852                                 self.current += 1
853                                 if self.current >= self.length:
854                                         return False
855                 return True
856
857         def nextService(self):
858                 if not self.length:
859                         return
860                 if self.current+1 < self.length:
861                         self.current += 1
862                 else:
863                         self.current = 0
864
865         def prevService(self):
866                 if not self.length:
867                         return
868                 if self.current-1 > -1:
869                         self.current -= 1
870                 else:
871                         self.current = self.length - 1
872
873         def currentService(self):
874                 if not self.length or self.current >= self.length:
875                         return None
876                 return self.services[self.current]
877
878 class InfoBarEPG:
879         """ EPG - Opens an EPG list when the showEPGList action fires """
880         def __init__(self):
881                 self.is_now_next = False
882                 self.dlg_stack = [ ]
883                 self.bouquetSel = None
884                 self.eventView = None
885                 self.epglist = []
886                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
887                         {
888                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
889                         })
890
891                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
892                         {
893                                 "showEventInfo": (self.showDefaultEPG, _("Show EPG...")),
894                                 "showEventInfoSingleEPG": (self.showSingleEPG, _("Show single service EPG")),
895                                 "showEventInfoMultiEPG": (self.showMultiEPG, _("Show multi channel EPG")),
896                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
897                         })
898
899         def getEPGPluginList(self, getAll=False):
900                 pluginlist = [(p.name, boundFunction(self.runPlugin, p), p.path) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO) \
901                                 if 'selectedevent' not in p.__call__.func_code.co_varnames] or []
902                 from Components.ServiceEventTracker import InfoBarCount
903                 if getAll or InfoBarCount == 1:
904                         pluginlist.append((_("Show EPG for current channel..."), self.openSingleServiceEPG, "current_channel"))
905                 pluginlist.append((_("Multi EPG"), self.openMultiServiceEPG, "multi_epg"))
906                 pluginlist.append((_("Current event EPG"), self.openEventView, "event_epg"))
907                 return pluginlist
908
909         def showEventInfoWhenNotVisible(self):
910                 if self.shown:
911                         self.openEventView()
912                 else:
913                         self.toggleShow()
914                         return 1
915
916         def zapToService(self, service, preview = False, zapback = False):
917                 if self.servicelist.startServiceRef is None:
918                         self.servicelist.startServiceRef = self.session.nav.getCurrentlyPlayingServiceOrGroup()
919                 if service is not None:
920                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
921                                 self.servicelist.clearPath()
922                                 if self.servicelist.bouquet_root != self.epg_bouquet:
923                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
924                                 self.servicelist.enterPath(self.epg_bouquet)
925                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
926                 if not zapback or preview:
927                         self.servicelist.zap(enable_pipzap = True)
928                 if (self.servicelist.dopipzap or zapback) and not preview:
929                         self.servicelist.zapBack()
930                 if not preview:
931                         self.servicelist.startServiceRef = None
932                         self.servicelist.startRoot = None
933
934         def getBouquetServices(self, bouquet):
935                 services = [ ]
936                 servicelist = eServiceCenter.getInstance().list(bouquet)
937                 if not servicelist is None:
938                         while True:
939                                 service = servicelist.getNext()
940                                 if not service.valid(): #check if end of list
941                                         break
942                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
943                                         continue
944                                 services.append(ServiceReference(service))
945                 return services
946
947         def openBouquetEPG(self, bouquet, withCallback=True):
948                 services = self.getBouquetServices(bouquet)
949                 if services:
950                         self.epg_bouquet = bouquet
951                         if withCallback:
952                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
953                         else:
954                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
955
956         def changeBouquetCB(self, direction, epg):
957                 if self.bouquetSel:
958                         if direction > 0:
959                                 self.bouquetSel.down()
960                         else:
961                                 self.bouquetSel.up()
962                         bouquet = self.bouquetSel.getCurrent()
963                         services = self.getBouquetServices(bouquet)
964                         if services:
965                                 self.epg_bouquet = bouquet
966                                 epg.setServices(services)
967
968         def closed(self, ret=False):
969                 closedScreen = self.dlg_stack.pop()
970                 if self.bouquetSel and closedScreen == self.bouquetSel:
971                         self.bouquetSel = None
972                 elif self.eventView and closedScreen == self.eventView:
973                         self.eventView = None
974                 if ret:
975                         dlgs=len(self.dlg_stack)
976                         if dlgs > 0:
977                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
978
979         def openMultiServiceEPG(self, withCallback=True):
980                 bouquets = self.servicelist.getBouquetList()
981                 if bouquets is None:
982                         cnt = 0
983                 else:
984                         cnt = len(bouquets)
985                 if config.usage.multiepg_ask_bouquet.value:
986                         self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
987                 else:
988                         self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
989
990         def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
991                 if cnt > 1: # show bouquet list
992                         if withCallback:
993                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
994                                 self.dlg_stack.append(self.bouquetSel)
995                         else:
996                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
997                 elif cnt == 1:
998                         self.openBouquetEPG(bouquets[0][1], withCallback)
999
1000         def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
1001                 root = self.servicelist.getRoot()
1002                 rootstr = root.toCompareString()
1003                 current = 0
1004                 for bouquet in bouquets:
1005                         if bouquet[1].toCompareString() == rootstr:
1006                                 break
1007                         current += 1
1008                 if current >= cnt:
1009                         current = 0
1010                 if cnt > 1: # create bouquet list for bouq+/-
1011                         self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
1012                 if cnt >= 1:
1013                         self.openBouquetEPG(root, withCallback)
1014
1015         def changeServiceCB(self, direction, epg):
1016                 if self.serviceSel:
1017                         if direction > 0:
1018                                 self.serviceSel.nextService()
1019                         else:
1020                                 self.serviceSel.prevService()
1021                         epg.setService(self.serviceSel.currentService())
1022
1023         def SingleServiceEPGClosed(self, ret=False):
1024                 self.serviceSel = None
1025
1026         def openSingleServiceEPG(self):
1027                 ref = self.servicelist.getCurrentSelection()
1028                 if ref:
1029                         if self.servicelist.getMutableList(): # bouquet in channellist
1030                                 current_path = self.servicelist.getRoot()
1031                                 services = self.getBouquetServices(current_path)
1032                                 self.serviceSel = SimpleServicelist(services)
1033                                 if self.serviceSel.selectService(ref):
1034                                         self.epg_bouquet = current_path
1035                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, self.zapToService, serviceChangeCB=self.changeServiceCB)
1036                                 else:
1037                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
1038                         else:
1039                                 self.session.open(EPGSelection, ref)
1040
1041         def runPlugin(self, plugin):
1042                 plugin(session = self.session, servicelist = self.servicelist)
1043
1044         def showEventInfoPlugins(self):
1045                 pluginlist = self.getEPGPluginList()
1046                 if pluginlist:
1047                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = pluginlist, skin_name = "EPGExtensionsList")
1048                 else:
1049                         self.openSingleServiceEPG()
1050
1051         def EventInfoPluginChosen(self, answer):
1052                 if answer is not None:
1053                         answer[1]()
1054
1055         def openSimilarList(self, eventid, refstr):
1056                 self.session.open(EPGSelection, refstr, None, eventid)
1057
1058         def getNowNext(self):
1059                 epglist = [ ]
1060                 service = self.session.nav.getCurrentService()
1061                 info = service and service.info()
1062                 ptr = info and info.getEvent(0)
1063                 if ptr:
1064                         epglist.append(ptr)
1065                 ptr = info and info.getEvent(1)
1066                 if ptr:
1067                         epglist.append(ptr)
1068                 self.epglist = epglist
1069
1070         def __evEventInfoChanged(self):
1071                 if self.is_now_next and len(self.dlg_stack) == 1:
1072                         self.getNowNext()
1073                         if self.eventView and self.epglist:
1074                                 self.eventView.setEvent(self.epglist[0])
1075
1076         def showDefaultEPG(self):
1077                 self.openEventView()
1078
1079         def showSingleEPG(self):
1080                 self.openSingleServiceEPG()
1081
1082         def showMultiEPG(self):
1083                 self.openMultiServiceEPG()
1084
1085         def openEventView(self):
1086                 from Components.ServiceEventTracker import InfoBarCount
1087                 if InfoBarCount > 1:
1088                         epglist = [ ]
1089                         self.epglist = epglist
1090                         service = self.session.nav.getCurrentService()
1091                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
1092                         info = service.info()
1093                         ptr=info.getEvent(0)
1094                         if ptr:
1095                                 epglist.append(ptr)
1096                         ptr=info.getEvent(1)
1097                         if ptr:
1098                                 epglist.append(ptr)
1099                         if epglist:
1100                                 self.session.open(EventViewEPGSelect, epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
1101                 else:
1102                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
1103                         self.getNowNext()
1104                         epglist = self.epglist
1105                         if not epglist:
1106                                 self.is_now_next = False
1107                                 epg = eEPGCache.getInstance()
1108                                 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
1109                                 if ptr:
1110                                         epglist.append(ptr)
1111                                         ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
1112                                         if ptr:
1113                                                 epglist.append(ptr)
1114                         else:
1115                                 self.is_now_next = True
1116                         if epglist:
1117                                 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
1118                                 self.dlg_stack.append(self.eventView)
1119                 if not epglist:
1120                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
1121                         self.openMultiServiceEPG(False)
1122
1123         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
1124                 epglist = self.epglist
1125                 if len(epglist) > 1:
1126                         tmp = epglist[0]
1127                         epglist[0]=epglist[1]
1128                         epglist[1]=tmp
1129                         setEvent(epglist[0])
1130
1131 class InfoBarRdsDecoder:
1132         """provides RDS and Rass support/display"""
1133         def __init__(self):
1134                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
1135                 self.session.instantiateSummaryDialog(self.rds_display)
1136                 self.rass_interactive = None
1137
1138                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1139                         {
1140                                 iPlayableService.evEnd: self.__serviceStopped,
1141                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
1142                         })
1143
1144                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
1145                 {
1146                         "startRassInteractive": self.startRassInteractive
1147                 },-1)
1148
1149                 self["RdsActions"].setEnabled(False)
1150
1151                 self.onLayoutFinish.append(self.rds_display.show)
1152                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
1153
1154         def RassInteractivePossibilityChanged(self, state):
1155                 self["RdsActions"].setEnabled(state)
1156
1157         def RassSlidePicChanged(self):
1158                 if not self.rass_interactive:
1159                         service = self.session.nav.getCurrentService()
1160                         decoder = service and service.rdsDecoder()
1161                         if decoder:
1162                                 decoder.showRassSlidePicture()
1163
1164         def __serviceStopped(self):
1165                 if self.rass_interactive is not None:
1166                         rass_interactive = self.rass_interactive
1167                         self.rass_interactive = None
1168                         rass_interactive.close()
1169
1170         def startRassInteractive(self):
1171                 self.rds_display.hide()
1172                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
1173
1174         def RassInteractiveClosed(self, *val):
1175                 if self.rass_interactive is not None:
1176                         self.rass_interactive = None
1177                         self.RassSlidePicChanged()
1178                 self.rds_display.show()
1179
1180 class InfoBarSeek:
1181         """handles actions like seeking, pause"""
1182
1183         SEEK_STATE_PLAY = (0, 0, 0, ">")
1184         SEEK_STATE_PAUSE = (1, 0, 0, "||")
1185         SEEK_STATE_EOF = (1, 0, 0, "END")
1186
1187         def __init__(self, actionmap = "InfobarSeekActions"):
1188                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1189                         {
1190                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1191                                 iPlayableService.evStart: self.__serviceStarted,
1192                                 iPlayableService.evEOF: self.__evEOF,
1193                                 iPlayableService.evSOF: self.__evSOF,
1194                         })
1195                 self.fast_winding_hint_message_showed = False
1196
1197                 class InfoBarSeekActionMap(HelpableActionMap):
1198                         def __init__(self, screen, *args, **kwargs):
1199                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
1200                                 self.screen = screen
1201
1202                         def action(self, contexts, action):
1203                                 print "action:", action
1204                                 if action[:5] == "seek:":
1205                                         time = int(action[5:])
1206                                         self.screen.doSeekRelative(time * 90000)
1207                                         return 1
1208                                 elif action[:8] == "seekdef:":
1209                                         key = int(action[8:])
1210                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
1211                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
1212                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
1213                                         self.screen.doSeekRelative(time * 90000)
1214                                         return 1
1215                                 else:
1216                                         return HelpableActionMap.action(self, contexts, action)
1217
1218                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
1219                         {
1220                                 "playpauseService": (self.playpauseService, _("Pauze/Continue playback")),
1221                                 "pauseService": (self.pauseService, _("Pause playback")),
1222                                 "unPauseService": (self.unPauseService, _("Continue playback")),
1223                                 "okButton": (self.okButton, _("Continue playback")),
1224                                 "seekFwd": (self.seekFwd, _("Seek forward")),
1225                                 "seekFwdManual": (self.seekFwdManual, _("Seek forward (enter time)")),
1226                                 "seekBack": (self.seekBack, _("Seek backward")),
1227                                 "seekBackManual": (self.seekBackManual, _("Seek backward (enter time)")),
1228                                 "jumpPreviousMark": (self.seekPreviousMark, _("Jump to previous marked position")),
1229                                 "jumpNextMark": (self.seekNextMark, _("Jump to next marked position")),
1230                         }, prio=-1)
1231                         # give them a little more priority to win over color buttons
1232
1233                 self["SeekActions"].setEnabled(False)
1234
1235                 self.seekstate = self.SEEK_STATE_PLAY
1236                 self.lastseekstate = self.SEEK_STATE_PLAY
1237
1238                 self.onPlayStateChanged = [ ]
1239
1240                 self.lockedBecauseOfSkipping = False
1241
1242                 self.__seekableStatusChanged()
1243
1244         def makeStateForward(self, n):
1245                 return (0, n, 0, ">> %dx" % n)
1246
1247         def makeStateBackward(self, n):
1248                 return (0, -n, 0, "<< %dx" % n)
1249
1250         def makeStateSlowMotion(self, n):
1251                 return (0, 0, n, "/%d" % n)
1252
1253         def isStateForward(self, state):
1254                 return state[1] > 1
1255
1256         def isStateBackward(self, state):
1257                 return state[1] < 0
1258
1259         def isStateSlowMotion(self, state):
1260                 return state[1] == 0 and state[2] > 1
1261
1262         def getHigher(self, n, lst):
1263                 for x in lst:
1264                         if x > n:
1265                                 return x
1266                 return False
1267
1268         def getLower(self, n, lst):
1269                 lst = lst[:]
1270                 lst.reverse()
1271                 for x in lst:
1272                         if x < n:
1273                                 return x
1274                 return False
1275
1276         def showAfterSeek(self):
1277                 if isinstance(self, InfoBarShowHide):
1278                         self.doShow()
1279
1280         def up(self):
1281                 pass
1282
1283         def down(self):
1284                 pass
1285
1286         def getSeek(self):
1287                 service = self.session.nav.getCurrentService()
1288                 if service is None:
1289                         return None
1290
1291                 seek = service.seek()
1292
1293                 if seek is None or not seek.isCurrentlySeekable():
1294                         return None
1295
1296                 return seek
1297
1298         def isSeekable(self):
1299                 if self.getSeek() is None or (isStandardInfoBar(self) and not self.timeshiftEnabled()):
1300                         return False
1301                 return True
1302
1303         def __seekableStatusChanged(self):
1304 #               print "seekable status changed!"
1305                 if not self.isSeekable():
1306                         self["SeekActions"].setEnabled(False)
1307 #                       print "not seekable, return to play"
1308                         self.setSeekState(self.SEEK_STATE_PLAY)
1309                 else:
1310                         self["SeekActions"].setEnabled(True)
1311 #                       print "seekable"
1312
1313         def __serviceStarted(self):
1314                 self.fast_winding_hint_message_showed = False
1315                 self.setSeekState(self.SEEK_STATE_PLAY)
1316                 self.__seekableStatusChanged()
1317
1318         def setSeekState(self, state):
1319                 service = self.session.nav.getCurrentService()
1320
1321                 if service is None:
1322                         return False
1323
1324                 if not self.isSeekable():
1325                         if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
1326                                 state = self.SEEK_STATE_PLAY
1327
1328                 pauseable = service.pause()
1329
1330                 if pauseable is None:
1331                         print "not pauseable."
1332                         state = self.SEEK_STATE_PLAY
1333
1334                 self.seekstate = state
1335
1336                 if pauseable is not None:
1337                         if self.seekstate[0]:
1338                                 print "resolved to PAUSE"
1339                                 pauseable.pause()
1340                         elif self.seekstate[1]:
1341                                 if not pauseable.setFastForward(self.seekstate[1]):
1342                                         print "resolved to FAST FORWARD"
1343                                 else:
1344                                         self.seekstate = self.SEEK_STATE_PLAY
1345                                         print "FAST FORWARD not possible: resolved to PLAY"
1346                         elif self.seekstate[2]:
1347                                 if not pauseable.setSlowMotion(self.seekstate[2]):
1348                                         print "resolved to SLOW MOTION"
1349                                 else:
1350                                         self.seekstate = self.SEEK_STATE_PAUSE
1351                                         print "SLOW MOTION not possible: resolved to PAUSE"
1352                         else:
1353                                 print "resolved to PLAY"
1354                                 pauseable.unpause()
1355
1356                 for c in self.onPlayStateChanged:
1357                         c(self.seekstate)
1358
1359                 self.checkSkipShowHideLock()
1360
1361                 if hasattr(self, "ScreenSaverTimerStart"):
1362                         self.ScreenSaverTimerStart()
1363
1364                 return True
1365
1366         def playpauseService(self):
1367                 if self.seekstate != self.SEEK_STATE_PLAY:
1368                         self.unPauseService()
1369                 else:
1370                         self.pauseService()
1371
1372         def okButton(self):
1373                 if self.seekstate == self.SEEK_STATE_PLAY:
1374                         return 0
1375                 elif self.seekstate == self.SEEK_STATE_PAUSE:
1376                         self.pauseService()
1377                 else:
1378                         self.unPauseService()
1379
1380         def pauseService(self):
1381                 if self.seekstate == self.SEEK_STATE_PAUSE:
1382                         if config.seek.on_pause.value == "play":
1383                                 self.unPauseService()
1384                         elif config.seek.on_pause.value == "step":
1385                                 self.doSeekRelative(1)
1386                         elif config.seek.on_pause.value == "last":
1387                                 self.setSeekState(self.lastseekstate)
1388                                 self.lastseekstate = self.SEEK_STATE_PLAY
1389                 else:
1390                         if self.seekstate != self.SEEK_STATE_EOF:
1391                                 self.lastseekstate = self.seekstate
1392                         self.setSeekState(self.SEEK_STATE_PAUSE)
1393
1394         def unPauseService(self):
1395                 print "unpause"
1396                 if self.seekstate == self.SEEK_STATE_PLAY:
1397                         return 0
1398                 self.setSeekState(self.SEEK_STATE_PLAY)
1399
1400         def doSeek(self, pts):
1401                 seekable = self.getSeek()
1402                 if seekable is None:
1403                         return
1404                 seekable.seekTo(pts)
1405
1406         def doSeekRelative(self, pts):
1407                 seekable = self.getSeek()
1408                 if seekable is None:
1409                         return
1410                 prevstate = self.seekstate
1411
1412                 if self.seekstate == self.SEEK_STATE_EOF:
1413                         if prevstate == self.SEEK_STATE_PAUSE:
1414                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1415                         else:
1416                                 self.setSeekState(self.SEEK_STATE_PLAY)
1417                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
1418                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
1419                         self.showAfterSeek()
1420
1421         def seekFwd(self):
1422                 seek = self.getSeek()
1423                 if seek and not (seek.isCurrentlySeekable() & 2):
1424                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1425                                 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)
1426                                 self.fast_winding_hint_message_showed = True
1427                                 return
1428                         return 0 # trade as unhandled action
1429                 if self.seekstate == self.SEEK_STATE_PLAY:
1430                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1431                 elif self.seekstate == self.SEEK_STATE_PAUSE:
1432                         if len(config.seek.speeds_slowmotion.value):
1433                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
1434                         else:
1435                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1436                 elif self.seekstate == self.SEEK_STATE_EOF:
1437                         pass
1438                 elif self.isStateForward(self.seekstate):
1439                         speed = self.seekstate[1]
1440                         if self.seekstate[2]:
1441                                 speed /= self.seekstate[2]
1442                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
1443                         self.setSeekState(self.makeStateForward(speed))
1444                 elif self.isStateBackward(self.seekstate):
1445                         speed = -self.seekstate[1]
1446                         if self.seekstate[2]:
1447                                 speed /= self.seekstate[2]
1448                         speed = self.getLower(speed, config.seek.speeds_backward.value)
1449                         if speed:
1450                                 self.setSeekState(self.makeStateBackward(speed))
1451                         else:
1452                                 self.setSeekState(self.SEEK_STATE_PLAY)
1453                 elif self.isStateSlowMotion(self.seekstate):
1454                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
1455                         self.setSeekState(self.makeStateSlowMotion(speed))
1456
1457         def seekBack(self):
1458                 seek = self.getSeek()
1459                 if seek and not (seek.isCurrentlySeekable() & 2):
1460                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1461                                 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)
1462                                 self.fast_winding_hint_message_showed = True
1463                                 return
1464                         return 0 # trade as unhandled action
1465                 seekstate = self.seekstate
1466                 if seekstate == self.SEEK_STATE_PLAY:
1467                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1468                 elif seekstate == self.SEEK_STATE_EOF:
1469                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1470                         self.doSeekRelative(-6)
1471                 elif seekstate == self.SEEK_STATE_PAUSE:
1472                         self.doSeekRelative(-1)
1473                 elif self.isStateForward(seekstate):
1474                         speed = seekstate[1]
1475                         if seekstate[2]:
1476                                 speed /= seekstate[2]
1477                         speed = self.getLower(speed, config.seek.speeds_forward.value)
1478                         if speed:
1479                                 self.setSeekState(self.makeStateForward(speed))
1480                         else:
1481                                 self.setSeekState(self.SEEK_STATE_PLAY)
1482                 elif self.isStateBackward(seekstate):
1483                         speed = -seekstate[1]
1484                         if seekstate[2]:
1485                                 speed /= seekstate[2]
1486                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1487                         self.setSeekState(self.makeStateBackward(speed))
1488                 elif self.isStateSlowMotion(seekstate):
1489                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1490                         if speed:
1491                                 self.setSeekState(self.makeStateSlowMotion(speed))
1492                         else:
1493                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1494
1495         def seekFwdManual(self):
1496                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1497
1498         def fwdSeekTo(self, minutes):
1499                 print "Seek", minutes, "minutes forward"
1500                 self.doSeekRelative(minutes * 60 * 90000)
1501
1502         def seekBackManual(self):
1503                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1504
1505         def rwdSeekTo(self, minutes):
1506                 print "rwdSeekTo"
1507                 self.doSeekRelative(-minutes * 60 * 90000)
1508
1509         def checkSkipShowHideLock(self):
1510                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1511
1512                 if config.usage.show_infobar_on_skip.value:
1513                         if self.lockedBecauseOfSkipping and not wantlock:
1514                                 self.unlockShow()
1515                                 self.lockedBecauseOfSkipping = False
1516
1517                         if wantlock and not self.lockedBecauseOfSkipping:
1518                                 self.lockShow()
1519                                 self.lockedBecauseOfSkipping = True
1520
1521         def calcRemainingTime(self):
1522                 seekable = self.getSeek()
1523                 if seekable is not None:
1524                         len = seekable.getLength()
1525                         try:
1526                                 tmp = self.cueGetEndCutPosition()
1527                                 if tmp:
1528                                         len = (False, tmp)
1529                         except:
1530                                 pass
1531                         pos = seekable.getPlayPosition()
1532                         speednom = self.seekstate[1] or 1
1533                         speedden = self.seekstate[2] or 1
1534                         if not len[0] and not pos[0]:
1535                                 if len[1] <= pos[1]:
1536                                         return 0
1537                                 time = (len[1] - pos[1])*speedden/(90*speednom)
1538                                 return time
1539                 return False
1540
1541         def __evEOF(self):
1542                 if self.seekstate == self.SEEK_STATE_EOF:
1543                         return
1544
1545                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1546                 seekstate = self.seekstate
1547                 if self.seekstate != self.SEEK_STATE_PAUSE:
1548                         self.setSeekState(self.SEEK_STATE_EOF)
1549
1550                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1551                         seekable = self.getSeek()
1552                         if seekable is not None:
1553                                 seekable.seekTo(-1)
1554                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1555                         self.doEofInternal(True)
1556                 else:
1557                         self.doEofInternal(False)
1558
1559         def doEofInternal(self, playing):
1560                 pass            # Defined in subclasses
1561
1562         def __evSOF(self):
1563                 self.setSeekState(self.SEEK_STATE_PLAY)
1564                 self.doSeek(0)
1565
1566         # This is needed, because some Mediaplayer use InfoBarSeek but not InfoBarCueSheetSupport
1567         def seekPreviousMark(self):
1568                 if isinstance(self, InfoBarCueSheetSupport):
1569                         self.jumpPreviousMark()
1570
1571         def seekNextMark(self):
1572                 if isinstance(self, InfoBarCueSheetSupport):
1573                         self.jumpNextMark()
1574
1575 from Screens.PVRState import PVRState, TimeshiftState
1576
1577 class InfoBarPVRState:
1578         def __init__(self, screen=PVRState, force_show = False):
1579                 self.onPlayStateChanged.append(self.__playStateChanged)
1580                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1581                 self.onShow.append(self._mayShow)
1582                 self.onHide.append(self.pvrStateDialog.hide)
1583                 self.force_show = force_show
1584
1585         def _mayShow(self):
1586                 if self.shown and self.seekstate != self.SEEK_STATE_PLAY:
1587                         self.pvrStateDialog.show()
1588
1589         def __playStateChanged(self, state):
1590                 playstateString = state[3]
1591                 self.pvrStateDialog["state"].setText(playstateString)
1592
1593                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1594                 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1595                         self.pvrStateDialog.hide()
1596                 else:
1597                         self._mayShow()
1598
1599 class TimeshiftLive(Screen):
1600         def __init__(self, session):
1601                 Screen.__init__(self, session)
1602
1603 class InfoBarTimeshiftState(InfoBarPVRState):
1604         def __init__(self):
1605                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1606                 self.timeshiftLiveScreen = self.session.instantiateDialog(TimeshiftLive)
1607                 self.onHide.append(self.timeshiftLiveScreen.hide)
1608                 self.secondInfoBarScreen and self.secondInfoBarScreen.onShow.append(self.timeshiftLiveScreen.hide)
1609                 self.timeshiftLiveScreen.hide()
1610                 self.__hideTimer = eTimer()
1611                 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1612                 self.onFirstExecBegin.append(self.pvrStateDialog.show)
1613
1614         def _mayShow(self):
1615                 if self.timeshiftEnabled():
1616                         if self.secondInfoBarScreen and self.secondInfoBarScreen.shown:
1617                                 self.secondInfoBarScreen.hide()
1618                         if self.timeshiftActivated():
1619                                 self.pvrStateDialog.show()
1620                                 self.timeshiftLiveScreen.hide()
1621                         elif self.showTimeshiftState:
1622                                 self.pvrStateDialog.hide()
1623                                 self.timeshiftLiveScreen.show()
1624                                 self.showTimeshiftState = False
1625                         if self.seekstate == self.SEEK_STATE_PLAY and config.usage.infobar_timeout.index and (self.pvrStateDialog.shown or self.timeshiftLiveScreen.shown):
1626                                 self.__hideTimer.startLongTimer(config.usage.infobar_timeout.index)
1627                 else:
1628                         self.__hideTimeshiftState()
1629
1630         def __hideTimeshiftState(self):
1631                 self.pvrStateDialog.hide()
1632                 self.timeshiftLiveScreen.hide()
1633
1634 class InfoBarShowMovies:
1635
1636         # i don't really like this class.
1637         # it calls a not further specified "movie list" on up/down/movieList,
1638         # so this is not more than an action map
1639         def __init__(self):
1640                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1641                         {
1642                                 "movieList": (self.showMovies, _("Open the movie list")),
1643                                 "up": (self.up, _("Open the movie list")),
1644                                 "down": (self.down, _("Open the movie list"))
1645                         })
1646
1647 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1648
1649 # Hrmf.
1650 #
1651 # Timeshift works the following way:
1652 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1653 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1654 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1655 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1656 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1657 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1658 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1659 #
1660
1661 # in other words:
1662 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1663 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1664 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1665 # - the user can now PVR around
1666 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1667 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1668 # after!
1669 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1670 # - if the user rewinds, or press pause, timeshift will be activated again
1671
1672 # note that a timeshift can be enabled ("recording") and
1673 # activated (currently time-shifting).
1674
1675 class InfoBarTimeshift:
1676         def __init__(self):
1677                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1678                         {
1679                                 "timeshiftStart": (self.startTimeshift, _("Start timeshift")),  # the "yellow key"
1680                                 "timeshiftStop": (self.stopTimeshift, _("Stop timeshift"))      # currently undefined :), probably 'TV'
1681                         }, prio=1)
1682                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1683                         {
1684                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1685                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1686                         }, prio=-1) # priority over record
1687
1688                 self["TimeshiftActivateActions"].setEnabled(False)
1689                 self.ts_rewind_timer = eTimer()
1690                 self.ts_rewind_timer.callback.append(self.rewindService)
1691                 self.ts_start_delay_timer = eTimer()
1692                 self.ts_start_delay_timer.callback.append(self.startTimeshiftWithoutPause)
1693                 self.ts_current_event_timer = eTimer()
1694                 self.ts_current_event_timer.callback.append(self.saveTimeshiftFileForEvent)
1695                 self.save_timeshift_file = False
1696                 self.timeshift_was_activated = False
1697                 self.showTimeshiftState = False
1698                 self.save_timeshift_only_current_event = False
1699
1700                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1701                         {
1702                                 iPlayableService.evStart: self.__serviceStarted,
1703                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1704                                 iPlayableService.evEnd: self.__serviceEnd
1705                         })
1706
1707         def getTimeshift(self):
1708                 service = self.session.nav.getCurrentService()
1709                 return service and service.timeshift()
1710
1711         def timeshiftEnabled(self):
1712                 ts = self.getTimeshift()
1713                 return ts and ts.isTimeshiftEnabled()
1714
1715         def timeshiftActivated(self):
1716                 ts = self.getTimeshift()
1717                 return ts and ts.isTimeshiftActive()
1718
1719         def startTimeshift(self, pauseService = True):
1720                 print "enable timeshift"
1721                 ts = self.getTimeshift()
1722                 if ts is None:
1723                         if not pauseService and not int(config.usage.timeshift_start_delay.value):
1724                                 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR, simple = True)
1725                         print "no ts interface"
1726                         return 0
1727
1728                 if ts.isTimeshiftEnabled():
1729                         print "hu, timeshift already enabled?"
1730                 else:
1731                         if not ts.startTimeshift():
1732                                 # we remove the "relative time" for now.
1733                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1734
1735                                 if pauseService:
1736                                         # PAUSE.
1737                                         #self.setSeekState(self.SEEK_STATE_PAUSE)
1738                                         self.activateTimeshiftEnd(False)
1739                                         self.showTimeshiftState = True
1740                                 else:
1741                                         self.showTimeshiftState = False
1742
1743                                 # enable the "TimeshiftEnableActions", which will override
1744                                 # the startTimeshift actions
1745                                 self.__seekableStatusChanged()
1746
1747                                 # get current timeshift filename and calculate new
1748                                 self.save_timeshift_file = False
1749                                 self.save_timeshift_in_movie_dir = False
1750                                 self.setCurrentEventTimer()
1751                                 self.current_timeshift_filename = ts.getTimeshiftFilename()
1752                                 self.new_timeshift_filename = self.generateNewTimeshiftFileName()
1753                         else:
1754                                 print "timeshift failed"
1755
1756         def startTimeshiftWithoutPause(self):
1757                 self.startTimeshift(False)
1758
1759         def stopTimeshift(self):
1760                 ts = self.getTimeshift()
1761                 if ts and ts.isTimeshiftEnabled():
1762                         if int(config.usage.timeshift_start_delay.value):
1763                                 ts.switchToLive()
1764                         else:
1765                                 self.checkTimeshiftRunning(self.stopTimeshiftcheckTimeshiftRunningCallback)
1766                 else:
1767                         return 0
1768
1769         def stopTimeshiftcheckTimeshiftRunningCallback(self, answer):
1770                 ts = self.getTimeshift()
1771                 if answer and ts:
1772                         ts.stopTimeshift()
1773                         self.pvrStateDialog.hide()
1774                         self.setCurrentEventTimer()
1775                         # disable actions
1776                         self.__seekableStatusChanged()
1777
1778         # activates timeshift, and seeks to (almost) the end
1779         def activateTimeshiftEnd(self, back = True):
1780                 self.showTimeshiftState = True
1781                 ts = self.getTimeshift()
1782                 print "activateTimeshiftEnd"
1783
1784                 if ts is None:
1785                         return
1786
1787                 if ts.isTimeshiftActive():
1788                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1789                         self.pauseService()
1790                 else:
1791                         print "play, ..."
1792                         ts.activateTimeshift() # activate timeshift will automatically pause
1793                         self.setSeekState(self.SEEK_STATE_PAUSE)
1794                         seekable = self.getSeek()
1795                         if seekable is not None:
1796                                 seekable.seekTo(-90000) # seek approx. 1 sec before end
1797                         self.timeshift_was_activated = True
1798                 if back:
1799                         self.ts_rewind_timer.start(200, 1)
1800
1801         def rewindService(self):
1802                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1803
1804         # generates only filename without path
1805         def generateNewTimeshiftFileName(self):
1806                 name = "timeshift record"
1807                 info = { }
1808                 self.getProgramInfoAndEvent(info, name)
1809
1810                 serviceref = info["serviceref"]
1811
1812                 service_name = ""
1813                 if isinstance(serviceref, eServiceReference):
1814                         service_name = ServiceReference(serviceref).getServiceName()
1815                 begin_date = strftime("%Y%m%d %H%M", localtime(time()))
1816                 filename = begin_date + " - " + service_name
1817
1818                 if config.recording.filename_composition.value == "short":
1819                         filename = strftime("%Y%m%d", localtime(time())) + " - " + info["name"]
1820                 elif config.recording.filename_composition.value == "long":
1821                         filename += " - " + info["name"] + " - " + info["description"]
1822                 else:
1823                         filename += " - " + info["name"] # standard
1824
1825                 if config.recording.ascii_filenames.value:
1826                         filename = ASCIItranslit.legacyEncode(filename)
1827
1828                 print "New timeshift filename: ", filename
1829                 return filename
1830
1831         # same as activateTimeshiftEnd, but pauses afterwards.
1832         def activateTimeshiftEndAndPause(self):
1833                 print "activateTimeshiftEndAndPause"
1834                 #state = self.seekstate
1835                 self.activateTimeshiftEnd(False)
1836
1837         def callServiceStarted(self):
1838                 self.__serviceStarted()
1839
1840         def __seekableStatusChanged(self):
1841                 self["TimeshiftActivateActions"].setEnabled(not self.isSeekable() and self.timeshiftEnabled())
1842                 state = self.getSeek() is not None and self.timeshiftEnabled()
1843                 self["SeekActions"].setEnabled(state)
1844                 if not state:
1845                         self.setSeekState(self.SEEK_STATE_PLAY)
1846                 self.restartSubtitle()
1847
1848         def __serviceStarted(self):
1849                 self.pvrStateDialog.hide()
1850                 self.__seekableStatusChanged()
1851                 if self.ts_start_delay_timer.isActive():
1852                         self.ts_start_delay_timer.stop()
1853                 if int(config.usage.timeshift_start_delay.value):
1854                         self.ts_start_delay_timer.start(int(config.usage.timeshift_start_delay.value) * 1000, True)
1855
1856         def checkTimeshiftRunning(self, returnFunction):
1857                 if self.timeshiftEnabled() and config.usage.check_timeshift.value and self.timeshift_was_activated:
1858                         message = _("Stop timeshift?")
1859                         if not self.save_timeshift_file:
1860                                 choice = [(_("yes"), "stop"), (_("no"), "continue"), (_("Yes and save"), "save"), (_("Yes and save in movie dir"), "save_movie")]
1861                         else:
1862                                 choice = [(_("yes"), "stop"), (_("no"), "continue")]
1863                                 message += "\n" + _("Reminder, you have chosen to save timeshift file.")
1864                                 if self.save_timeshift_only_current_event:
1865                                         remaining = self.currentEventTime()
1866                                         if remaining > 0:
1867                                                 message += "\n" + _("The %d min remaining before the end of the event.") % abs(remaining / 60)
1868                         self.session.openWithCallback(boundFunction(self.checkTimeshiftRunningCallback, returnFunction), MessageBox, message, simple = True, list = choice)
1869                 else:
1870                         returnFunction(True)
1871
1872         def checkTimeshiftRunningCallback(self, returnFunction, answer):
1873                 if answer:
1874                         if "movie" in answer:
1875                                 self.save_timeshift_in_movie_dir = True
1876                         if "save" in answer:
1877                                 self.save_timeshift_file = True
1878                                 ts = self.getTimeshift()
1879                                 if ts:
1880                                         ts.saveTimeshiftFile()
1881                                         del ts
1882                         if "continue" not in answer:
1883                                 self.saveTimeshiftFiles()
1884                 returnFunction(answer and answer != "continue")
1885
1886         # renames/moves timeshift files if requested
1887         def __serviceEnd(self):
1888                 self.saveTimeshiftFiles()
1889                 self.setCurrentEventTimer()
1890                 self.timeshift_was_activated = False
1891
1892         def saveTimeshiftFiles(self):
1893                 if self.save_timeshift_file and self.current_timeshift_filename and self.new_timeshift_filename:
1894                         if config.usage.timeshift_path.value and not self.save_timeshift_in_movie_dir:
1895                                 dirname = config.usage.timeshift_path.value
1896                         else:
1897                                 dirname = defaultMoviePath()
1898                         filename = getRecordingFilename(self.new_timeshift_filename, dirname) + ".ts"
1899
1900                         fileList = []
1901                         fileList.append((self.current_timeshift_filename, filename))
1902                         if fileExists(self.current_timeshift_filename + ".sc"):
1903                                 fileList.append((self.current_timeshift_filename + ".sc", filename + ".sc"))
1904                         if fileExists(self.current_timeshift_filename + ".cuts"):
1905                                 fileList.append((self.current_timeshift_filename + ".cuts", filename + ".cuts"))
1906
1907                         moveFiles(fileList)
1908                         self.save_timeshift_file = False
1909                         self.setCurrentEventTimer()
1910
1911         def currentEventTime(self):
1912                 remaining = 0
1913                 ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
1914                 if ref:
1915                         epg = eEPGCache.getInstance()
1916                         event = epg.lookupEventTime(ref, -1, 0)
1917                         if event:
1918                                 now = int(time())
1919                                 start = event.getBeginTime()
1920                                 duration = event.getDuration()
1921                                 end = start + duration
1922                                 remaining = end - now
1923                 return remaining
1924
1925         def saveTimeshiftFileForEvent(self):
1926                 if self.timeshiftEnabled() and self.save_timeshift_only_current_event and self.timeshift_was_activated and self.save_timeshift_file:
1927                         message = _("Current event is over.\nSelect an option to save the timeshift file.")
1928                         choice = [(_("Save and stop timeshift"), "save"), (_("Save and restart timeshift"), "restart"), (_("Don't save and stop timeshift"), "stop"), (_("Do nothing"), "continue")]
1929                         self.session.openWithCallback(self.saveTimeshiftFileForEventCallback, MessageBox, message, simple = True, list = choice, timeout=15)
1930
1931         def saveTimeshiftFileForEventCallback(self, answer):
1932                 self.save_timeshift_only_current_event = False
1933                 if answer:
1934                         ts = self.getTimeshift()
1935                         if ts and answer in ("save", "restart", "stop"):
1936                                 self.stopTimeshiftcheckTimeshiftRunningCallback(True)
1937                                 if answer in ("save", "restart"):
1938                                         ts.saveTimeshiftFile()
1939                                         del ts
1940                                         self.saveTimeshiftFiles()
1941                                 if answer == "restart":
1942                                         self.ts_start_delay_timer.start(1000, True)
1943                                 self.save_timeshift_file = False
1944                                 self.save_timeshift_in_movie_dir = False
1945
1946         def setCurrentEventTimer(self, duration=0):
1947                 self.ts_current_event_timer.stop()
1948                 self.save_timeshift_only_current_event = False
1949                 if duration > 0:
1950                         self.save_timeshift_only_current_event = True
1951                         self.ts_current_event_timer.startLongTimer(duration)
1952
1953 from Screens.PiPSetup import PiPSetup
1954
1955 class InfoBarExtensions:
1956         EXTENSION_SINGLE = 0
1957         EXTENSION_LIST = 1
1958
1959         def __init__(self):
1960                 self.list = []
1961
1962                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1963                         {
1964                                 "extensions": (self.showExtensionSelection, _("Show extensions...")),
1965                         }, 1) # lower priority
1966
1967         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1968                 self.list.append((type, extension, key))
1969
1970         def updateExtension(self, extension, key = None):
1971                 self.extensionsList.append(extension)
1972                 if key is not None:
1973                         if self.extensionKeys.has_key(key):
1974                                 key = None
1975
1976                 if key is None:
1977                         for x in self.availableKeys:
1978                                 if not self.extensionKeys.has_key(x):
1979                                         key = x
1980                                         break
1981
1982                 if key is not None:
1983                         self.extensionKeys[key] = len(self.extensionsList) - 1
1984
1985         def updateExtensions(self):
1986                 self.extensionsList = []
1987                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1988                 self.extensionKeys = {}
1989                 for x in self.list:
1990                         if x[0] == self.EXTENSION_SINGLE:
1991                                 self.updateExtension(x[1], x[2])
1992                         else:
1993                                 for y in x[1]():
1994                                         self.updateExtension(y[0], y[1])
1995
1996         def showExtensionSelection(self):
1997                 self.updateExtensions()
1998                 extensionsList = self.extensionsList[:]
1999                 keys = []
2000                 list = []
2001                 for x in self.availableKeys:
2002                         if self.extensionKeys.has_key(x):
2003                                 entry = self.extensionKeys[x]
2004                                 extension = self.extensionsList[entry]
2005                                 if extension[2]():
2006                                         name = str(extension[0]())
2007                                         list.append((extension[0](), extension))
2008                                         keys.append(x)
2009                                         extensionsList.remove(extension)
2010                                 else:
2011                                         extensionsList.remove(extension)
2012                 list.extend([(x[0](), x) for x in extensionsList])
2013
2014                 keys += [""] * len(extensionsList)
2015                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
2016
2017         def extensionCallback(self, answer):
2018                 if answer is not None:
2019                         answer[1][1]()
2020
2021 from Tools.BoundFunction import boundFunction
2022 import inspect
2023
2024 # depends on InfoBarExtensions
2025
2026 class InfoBarPlugins:
2027         def __init__(self):
2028                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
2029
2030         def getPluginName(self, name):
2031                 return name
2032
2033         def getPluginList(self):
2034                 l = []
2035                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
2036                         args = inspect.getargspec(p.__call__)[0]
2037                         if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
2038                                 l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
2039                 l.sort(key = lambda e: e[2]) # sort by name
2040                 return l
2041
2042         def runPlugin(self, plugin):
2043                 if isinstance(self, InfoBarChannelSelection):
2044                         plugin(session = self.session, servicelist = self.servicelist)
2045                 else:
2046                         plugin(session = self.session)
2047
2048 from Components.Task import job_manager
2049 class InfoBarJobman:
2050         def __init__(self):
2051                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
2052
2053         def getJobList(self):
2054                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
2055
2056         def getJobName(self, job):
2057                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
2058
2059         def showJobView(self, job):
2060                 from Screens.TaskView import JobView
2061                 job_manager.in_background = False
2062                 self.session.openWithCallback(self.JobViewCB, JobView, job)
2063
2064         def JobViewCB(self, in_background):
2065                 job_manager.in_background = in_background
2066
2067 # depends on InfoBarExtensions
2068 class InfoBarPiP:
2069         def __init__(self):
2070                 try:
2071                         self.session.pipshown
2072                 except:
2073                         self.session.pipshown = False
2074
2075                 self.lastPiPService = None
2076
2077                 if SystemInfo["PIPAvailable"]:
2078                         self["PiPActions"] = HelpableActionMap(self, "InfobarPiPActions",
2079                                 {
2080                                         "activatePiP": (self.activePiP, self.activePiPName),
2081                                 })
2082                         if (self.allowPiP):
2083                                 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
2084                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
2085                                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
2086                                 self.addExtension((self.getTogglePipzapName, self.togglePipzap, lambda: True), "red")
2087                         else:
2088                                 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
2089                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
2090
2091                 self.lastPiPServiceTimeoutTimer = eTimer()
2092                 self.lastPiPServiceTimeoutTimer.callback.append(self.clearLastPiPService)
2093
2094         def pipShown(self):
2095                 return self.session.pipshown
2096
2097         def pipHandles0Action(self):
2098                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
2099
2100         def getShowHideName(self):
2101                 if self.session.pipshown:
2102                         return _("Disable Picture in Picture")
2103                 else:
2104                         return _("Activate Picture in Picture")
2105
2106         def getSwapName(self):
2107                 return _("Swap services")
2108
2109         def getMoveName(self):
2110                 return _("Move Picture in Picture")
2111
2112         def getTogglePipzapName(self):
2113                 slist = self.servicelist
2114                 if slist and slist.dopipzap:
2115                         return _("Zap focus to main screen")
2116                 return _("Zap focus to Picture in Picture")
2117
2118         def togglePipzap(self):
2119                 if not self.session.pipshown:
2120                         self.showPiP()
2121                 slist = self.servicelist
2122                 if slist and self.session.pipshown:
2123                         slist.togglePipzap()
2124                         if slist.dopipzap:
2125                                 currentServicePath = slist.getCurrentServicePath()
2126                                 self.servicelist.setCurrentServicePath(self.session.pip.servicePath, doZap=False)
2127                                 self.session.pip.servicePath = currentServicePath
2128
2129         def showPiP(self):
2130                 self.lastPiPServiceTimeoutTimer.stop()
2131                 if self.session.pipshown:
2132                         slist = self.servicelist
2133                         if slist and slist.dopipzap:
2134                                 self.togglePipzap()
2135                         if self.session.pipshown:
2136                                 lastPiPServiceTimeout = int(config.usage.pip_last_service_timeout.value)
2137                                 if lastPiPServiceTimeout >= 0:
2138                                         self.lastPiPService = self.session.pip.getCurrentServiceReference()
2139                                         if lastPiPServiceTimeout:
2140                                                 self.lastPiPServiceTimeoutTimer.startLongTimer(lastPiPServiceTimeout)
2141                                 del self.session.pip
2142                                 self.session.pipshown = False
2143                         if hasattr(self, "ScreenSaverTimerStart"):
2144                                 self.ScreenSaverTimerStart()
2145                 else:
2146                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
2147                         self.session.pip.show()
2148                         newservice = self.lastPiPService or self.session.nav.getCurrentlyPlayingServiceReference() or self.servicelist.servicelist.getCurrent()
2149                         if self.session.pip.playService(newservice):
2150                                 self.session.pipshown = True
2151                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
2152                         else:
2153                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference() or self.servicelist.servicelist.getCurrent()
2154                                 if self.session.pip.playService(newservice):
2155                                         self.session.pipshown = True
2156                                         self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
2157                                 else:
2158                                         self.session.pipshown = False
2159                                         del self.session.pip
2160                         if self.session.pipshown and hasattr(self, "screenSaverTimer"):
2161                                 self.screenSaverTimer.stop()
2162                         self.lastPiPService = None
2163
2164         def clearLastPiPService(self):
2165                 self.lastPiPService = None
2166
2167         def activePiP(self):
2168                 if self.servicelist and self.servicelist.dopipzap or not self.session.pipshown:
2169                         self.showPiP()
2170                 else:
2171                         self.togglePipzap()
2172
2173         def activePiPName(self):
2174                 if self.servicelist and self.servicelist.dopipzap:
2175                         return _("Disable Picture in Picture")
2176                 if self.session.pipshown:
2177                         return _("Zap focus to Picture in Picture")
2178                 else:
2179                         return _("Activate Picture in Picture")
2180
2181         def swapPiP(self):
2182                 if self.pipShown():
2183                         swapservice = self.session.nav.getCurrentlyPlayingServiceOrGroup()
2184                         pipref = self.session.pip.getCurrentService()
2185                         if swapservice and pipref and pipref.toString() != swapservice.toString():
2186                                 currentServicePath = self.servicelist.getCurrentServicePath()
2187                                 currentBouquet = self.servicelist and self.servicelist.getRoot()
2188                                 self.servicelist.setCurrentServicePath(self.session.pip.servicePath, doZap=False)
2189                                 self.session.pip.playService(swapservice)
2190                                 self.session.nav.playService(pipref, checkParentalControl=False, adjust=False)
2191                                 self.session.pip.servicePath = currentServicePath
2192                                 self.session.pip.servicePath[1] = currentBouquet
2193                                 if self.servicelist.dopipzap:
2194                                         # This unfortunately won't work with subservices
2195                                         self.servicelist.setCurrentSelection(self.session.pip.getCurrentService())
2196
2197         def movePiP(self):
2198                 if self.pipShown():
2199                         self.session.open(PiPSetup, pip = self.session.pip)
2200
2201         def pipDoHandle0Action(self):
2202                 use = config.usage.pip_zero_button.value
2203                 if "swap" == use:
2204                         self.swapPiP()
2205                 elif "swapstop" == use:
2206                         self.swapPiP()
2207                         self.showPiP()
2208                 elif "stop" == use:
2209                         self.showPiP()
2210
2211 from RecordTimer import parseEvent, RecordTimerEntry
2212
2213 class InfoBarInstantRecord:
2214         """Instant Record - handles the instantRecord action in order to
2215         start/stop instant records"""
2216         def __init__(self):
2217                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
2218                         {
2219                                 "instantRecord": (self.instantRecord, _("Instant recording...")),
2220                         })
2221                 self.SelectedInstantServiceRef = None
2222                 if isStandardInfoBar(self):
2223                         self.recording = []
2224                 else:
2225                         from Screens.InfoBar import InfoBar
2226                         InfoBarInstance = InfoBar.instance
2227                         if InfoBarInstance:
2228                                 self.recording = InfoBarInstance.recording
2229
2230         def stopCurrentRecording(self, entry = -1):
2231                 if entry is not None and entry != -1:
2232                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
2233                         self.recording.remove(self.recording[entry])
2234
2235         def getProgramInfoAndEvent(self, info, name):
2236                 info["serviceref"] = hasattr(self, "SelectedInstantServiceRef") and self.SelectedInstantServiceRef or self.session.nav.getCurrentlyPlayingServiceOrGroup()
2237
2238                 # try to get event info
2239                 event = None
2240                 try:
2241                         epg = eEPGCache.getInstance()
2242                         event = epg.lookupEventTime(info["serviceref"], -1, 0)
2243                         if event is None:
2244                                 if hasattr(self, "SelectedInstantServiceRef") and self.SelectedInstantServiceRef:
2245                                         service_info = eServiceCenter.getInstance().info(self.SelectedInstantServiceRef)
2246                                         event = service_info and service_info.getEvent(self.SelectedInstantServiceRef)
2247                                 else:
2248                                         service = self.session.nav.getCurrentService()
2249                                         event = service and service.info().getEvent(0)
2250                 except:
2251                         pass
2252
2253                 info["event"] = event
2254                 info["name"]  = name
2255                 info["description"] = ""
2256                 info["eventid"] = None
2257
2258                 if event is not None:
2259                         curEvent = parseEvent(event)
2260                         info["name"] = curEvent[2]
2261                         info["description"] = curEvent[3]
2262                         info["eventid"] = curEvent[4]
2263                         info["end"] = curEvent[1]
2264
2265
2266         def startInstantRecording(self, limitEvent = False):
2267                 begin = int(time())
2268                 end = begin + 3600      # dummy
2269                 name = "instant record"
2270                 info = { }
2271
2272                 self.getProgramInfoAndEvent(info, name)
2273                 serviceref = info["serviceref"]
2274                 event = info["event"]
2275
2276                 if event is not None:
2277                         if limitEvent:
2278                                 end = info["end"]
2279                 else:
2280                         if limitEvent:
2281                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
2282
2283                 if isinstance(serviceref, eServiceReference):
2284                         serviceref = ServiceReference(serviceref)
2285
2286                 recording = RecordTimerEntry(serviceref, begin, end, info["name"], info["description"], info["eventid"], dirname = preferredInstantRecordPath())
2287                 recording.dontSave = True
2288
2289                 if event is None or limitEvent == False:
2290                         recording.autoincrease = True
2291                         recording.setAutoincreaseEnd()
2292
2293                 simulTimerList = self.session.nav.RecordTimer.record(recording)
2294
2295                 if simulTimerList is None:      # no conflict
2296                         recording.autoincrease = False
2297                         self.recording.append(recording)
2298                 else:
2299                         if len(simulTimerList) > 1: # with other recording
2300                                 name = simulTimerList[1].name
2301                                 name_date = ' '.join((name, strftime('%F %T', localtime(simulTimerList[1].begin))))
2302                                 print "[TIMER] conflicts with", name_date
2303                                 recording.autoincrease = True   # start with max available length, then increment
2304                                 if recording.setAutoincreaseEnd():
2305                                         self.session.nav.RecordTimer.record(recording)
2306                                         self.recording.append(recording)
2307                                         self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
2308                                 else:
2309                                         self.session.open(MessageBox, _("Could not record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
2310                         else:
2311                                 self.session.open(MessageBox, _("Could not record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
2312                         recording.autoincrease = False
2313
2314         def isInstantRecordRunning(self):
2315                 print "self.recording:", self.recording
2316                 if self.recording:
2317                         for x in self.recording:
2318                                 if x.isRunning():
2319                                         return True
2320                 return False
2321
2322         def recordQuestionCallback(self, answer):
2323                 print "pre:\n", self.recording
2324
2325                 if answer is None or answer[1] == "no":
2326                         return
2327                 list = []
2328                 recording = self.recording[:]
2329                 for x in recording:
2330                         if not x in self.session.nav.RecordTimer.timer_list:
2331                                 self.recording.remove(x)
2332                         elif x.dontSave and x.isRunning():
2333                                 list.append((x, False))
2334
2335                 if answer[1] == "changeduration":
2336                         if len(self.recording) == 1:
2337                                 self.changeDuration(0)
2338                         else:
2339                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
2340                 elif answer[1] == "changeendtime":
2341                         if len(self.recording) == 1:
2342                                 self.setEndtime(0)
2343                         else:
2344                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
2345                 elif answer[1] == "timer":
2346                         import TimerEdit
2347                         self.session.open(TimerEdit.TimerEditList)
2348                 elif answer[1] == "stop":
2349                         self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
2350                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
2351                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
2352                         if answer[1] == "manualduration":
2353                                 self.changeDuration(len(self.recording)-1)
2354                         elif answer[1] == "manualendtime":
2355                                 self.setEndtime(len(self.recording)-1)
2356                 elif "timeshift" in answer[1]:
2357                         ts = self.getTimeshift()
2358                         if ts:
2359                                 ts.saveTimeshiftFile()
2360                                 self.save_timeshift_file = True
2361                                 if "movie" in answer[1]:
2362                                         self.save_timeshift_in_movie_dir = True
2363                                 if "event" in answer[1]:
2364                                         remaining = self.currentEventTime()
2365                                         if remaining > 0:
2366                                                 self.setCurrentEventTimer(remaining-15)
2367                 print "after:\n", self.recording
2368
2369         def setEndtime(self, entry):
2370                 if entry is not None and entry >= 0:
2371                         self.selectedEntry = entry
2372                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
2373                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
2374                         dlg.setTitle(_("Please change recording endtime"))
2375
2376         def TimeDateInputClosed(self, ret):
2377                 if len(ret) > 1:
2378                         if ret[0]:
2379                                 print "stopping recording at", strftime("%F %T", localtime(ret[1]))
2380                                 if self.recording[self.selectedEntry].end != ret[1]:
2381                                         self.recording[self.selectedEntry].autoincrease = False
2382                                 self.recording[self.selectedEntry].end = ret[1]
2383                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
2384
2385         def changeDuration(self, entry):
2386                 if entry is not None and entry >= 0:
2387                         self.selectedEntry = entry
2388                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
2389
2390         def inputCallback(self, value):
2391                 if value is not None:
2392                         print "stopping recording after", int(value), "minutes."
2393                         entry = self.recording[self.selectedEntry]
2394                         if int(value) != 0:
2395                                 entry.autoincrease = False
2396                         entry.end = int(time()) + 60 * int(value)
2397                         self.session.nav.RecordTimer.timeChanged(entry)
2398
2399         def isTimerRecordRunning(self):
2400                 identical = timers = 0
2401                 for timer in self.session.nav.RecordTimer.timer_list:
2402                         if timer.isRunning() and not timer.justplay:
2403                                 timers += 1
2404                                 if self.recording:
2405                                         for x in self.recording:
2406                                                 if x.isRunning() and x == timer:
2407                                                         identical += 1
2408                 return timers > identical
2409
2410         def instantRecord(self, serviceRef=None):
2411                 self.SelectedInstantServiceRef = serviceRef
2412                 pirr = preferredInstantRecordPath()
2413                 if not findSafeRecordPath(pirr) and not findSafeRecordPath(defaultMoviePath()):
2414                         if not pirr:
2415                                 pirr = ""
2416                         self.session.open(MessageBox, _("Missing ") + "\n" + pirr +
2417                                                  "\n" + _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
2418                         return
2419
2420                 if isStandardInfoBar(self):
2421                         common = ((_("Add recording (stop after current event)"), "event"),
2422                                 (_("Add recording (indefinitely)"), "indefinitely"),
2423                                 (_("Add recording (enter recording duration)"), "manualduration"),
2424                                 (_("Add recording (enter recording endtime)"), "manualendtime"),)
2425                 else:
2426                         common = ()
2427                 if self.isInstantRecordRunning():
2428                         title =_("A recording is currently running.\nWhat do you want to do?")
2429                         list = ((_("Stop recording"), "stop"),) + common + \
2430                                 ((_("Change recording (duration)"), "changeduration"),
2431                                 (_("Change recording (endtime)"), "changeendtime"),)
2432                         if self.isTimerRecordRunning():
2433                                 list += ((_("Stop timer recording"), "timer"),)
2434                         list += ((_("Do nothing"), "no"),)
2435                 else:
2436                         title=_("Start recording?")
2437                         list = common
2438                         if self.isTimerRecordRunning():
2439                                 list += ((_("Stop timer recording"), "timer"),)
2440                         if isStandardInfoBar(self):
2441                                 list += ((_("Do not record"), "no"),)
2442                 if isStandardInfoBar(self) and self.timeshiftEnabled():
2443                         list = list + ((_("Save timeshift file"), "timeshift"),
2444                                 (_("Save timeshift file in movie directory"), "timeshift_movie"))
2445                         if self.currentEventTime() > 0:
2446                                 list += ((_("Save timeshift only for current event"), "timeshift_event"),)
2447                 if list:
2448                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=title, list=list)
2449                 else:
2450                         return 0
2451
2452 from Tools.ISO639 import LanguageCodes
2453
2454 class InfoBarAudioSelection:
2455         def __init__(self):
2456                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
2457                         {
2458                                 "audioSelection": (self.audioSelection, _("Audio options...")),
2459                         })
2460
2461         def audioSelection(self):
2462                 from Screens.AudioSelection import AudioSelection
2463                 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
2464
2465         def audioSelected(self, ret=None):
2466                 print "[infobar::audioSelected]", ret
2467
2468 class InfoBarSubserviceSelection:
2469         def __init__(self):
2470                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
2471                         {
2472                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
2473                         })
2474
2475                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
2476                         {
2477                                 "nextSubservice": (self.nextSubservice, _("Switch to next sub service")),
2478                                 "prevSubservice": (self.prevSubservice, _("Switch to previous sub service"))
2479                         }, -1)
2480                 self["SubserviceQuickzapAction"].setEnabled(False)
2481
2482                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2483                         {
2484                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
2485                         })
2486                 self.onClose.append(self.__removeNotifications)
2487
2488                 self.bsel = None
2489
2490         def __removeNotifications(self):
2491                 self.session.nav.event.remove(self.checkSubservicesAvail)
2492
2493         def checkSubservicesAvail(self):
2494                 service = self.session.nav.getCurrentService()
2495                 subservices = service and service.subServices()
2496                 if not subservices or subservices.getNumberOfSubservices() == 0:
2497                         self["SubserviceQuickzapAction"].setEnabled(False)
2498
2499         def nextSubservice(self):
2500                 self.changeSubservice(+1)
2501
2502         def prevSubservice(self):
2503                 self.changeSubservice(-1)
2504
2505         def changeSubservice(self, direction):
2506                 service = self.session.nav.getCurrentService()
2507                 subservices = service and service.subServices()
2508                 n = subservices and subservices.getNumberOfSubservices()
2509                 if n and n > 0:
2510                         selection = -1
2511                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
2512                         idx = 0
2513                         while idx < n:
2514                                 if subservices.getSubservice(idx).toString() == ref.toString():
2515                                         selection = idx
2516                                         break
2517                                 idx += 1
2518                         if selection != -1:
2519                                 selection += direction
2520                                 if selection >= n:
2521                                         selection=0
2522                                 elif selection < 0:
2523                                         selection=n-1
2524                                 newservice = subservices.getSubservice(selection)
2525                                 if newservice.valid():
2526                                         del subservices
2527                                         del service
2528                                         self.session.nav.playService(newservice, False)
2529
2530         def subserviceSelection(self):
2531                 service = self.session.nav.getCurrentService()
2532                 subservices = service and service.subServices()
2533                 self.bouquets = self.servicelist.getBouquetList()
2534                 n = subservices and subservices.getNumberOfSubservices()
2535                 selection = 0
2536                 if n and n > 0:
2537                         ref = self.session.nav.getCurrentlyPlayingServiceOrGroup()
2538                         tlist = []
2539                         idx = 0
2540                         while idx < n:
2541                                 i = subservices.getSubservice(idx)
2542                                 if i.toString() == ref.toString():
2543                                         selection = idx
2544                                 tlist.append((i.getName(), i))
2545                                 idx += 1
2546
2547                         if self.bouquets and len(self.bouquets):
2548                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
2549                                 if config.usage.multibouquet.value:
2550                                         tlist = [(_("Quick zap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
2551                                 else:
2552                                         tlist = [(_("Quick zap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
2553                                 selection += 3
2554                         else:
2555                                 tlist = [(_("Quick zap"), "quickzap", service.subServices()), ("--", "")] + tlist
2556                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
2557                                 selection += 2
2558
2559                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a sub service..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
2560
2561         def subserviceSelected(self, service):
2562                 del self.bouquets
2563                 if not service is None:
2564                         if isinstance(service[1], str):
2565                                 if service[1] == "quickzap":
2566                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
2567                                         self.session.open(SubservicesQuickzap, service[2])
2568                         else:
2569                                 self["SubserviceQuickzapAction"].setEnabled(True)
2570                                 self.session.nav.playService(service[1], False)
2571
2572         def addSubserviceToBouquetCallback(self, service):
2573                 if len(service) > 1 and isinstance(service[1], eServiceReference):
2574                         self.selectedSubservice = service
2575                         if self.bouquets is None:
2576                                 cnt = 0
2577                         else:
2578                                 cnt = len(self.bouquets)
2579                         if cnt > 1: # show bouquet list
2580                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
2581                         elif cnt == 1: # add to only one existing bouquet
2582                                 self.addSubserviceToBouquet(self.bouquets[0][1])
2583                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
2584
2585         def bouquetSelClosed(self, confirmed):
2586                 self.bsel = None
2587                 del self.selectedSubservice
2588                 if confirmed:
2589                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
2590
2591         def addSubserviceToBouquet(self, dest):
2592                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
2593                 if self.bsel:
2594                         self.bsel.close(True)
2595                 else:
2596                         del self.selectedSubservice
2597
2598 class InfoBarRedButton:
2599         def __init__(self):
2600                 self["RedButtonActions"] = HelpableActionMap(self, "InfobarRedButtonActions",
2601                         {
2602                                 "activateRedButton": (self.activateRedButton, _("Red button...")),
2603                         })
2604                 self.onHBBTVActivation = [ ]
2605                 self.onRedButtonActivation = [ ]
2606
2607         def activateRedButton(self):
2608                 service = self.session.nav.getCurrentService()
2609                 info = service and service.info()
2610                 if info and info.getInfoString(iServiceInformation.sHBBTVUrl) != "":
2611                         for x in self.onHBBTVActivation:
2612                                 x()
2613                 elif False: # TODO: other red button services
2614                         for x in self.onRedButtonActivation:
2615                                 x()
2616
2617 class InfoBarTimerButton:
2618         def __init__(self):
2619                 self["TimerButtonActions"] = HelpableActionMap(self, "InfobarTimerButtonActions",
2620                         {
2621                                 "timerSelection": (self.timerSelection, _("Timer selection...")),
2622                         })
2623
2624         def timerSelection(self):
2625                 from Screens.TimerEdit import TimerEditList
2626                 self.session.open(TimerEditList)
2627
2628 class InfoBarVmodeButton:
2629         def __init__(self):
2630                 self["VmodeButtonActions"] = HelpableActionMap(self, "InfobarVmodeButtonActions",
2631                         {
2632                                 "vmodeSelection": (self.vmodeSelection, _("Letterbox zoom")),
2633                         })
2634
2635         def vmodeSelection(self):
2636                 self.session.open(VideoMode)
2637
2638 class VideoMode(Screen):
2639         def __init__(self,session):
2640                 Screen.__init__(self, session)
2641                 self["videomode"] = Label()
2642
2643                 self["actions"] = NumberActionMap( [ "InfobarVmodeButtonActions" ],
2644                         {
2645                                 "vmodeSelection": self.selectVMode
2646                         })
2647
2648                 self.Timer = eTimer()
2649                 self.Timer.callback.append(self.quit)
2650                 self.selectVMode()
2651
2652         def selectVMode(self):
2653                 policy = config.av.policy_43
2654                 if self.isWideScreen():
2655                         policy = config.av.policy_169
2656                 idx = policy.choices.index(policy.value)
2657                 idx = (idx + 1) % len(policy.choices)
2658                 policy.value = policy.choices[idx]
2659                 self["videomode"].setText(policy.value)
2660                 self.Timer.start(1000, True)
2661
2662         def isWideScreen(self):
2663                 from Components.Converter.ServiceInfo import WIDESCREEN
2664                 service = self.session.nav.getCurrentService()
2665                 info = service and service.info()
2666                 return info.getInfo(iServiceInformation.sAspect) in WIDESCREEN
2667
2668         def quit(self):
2669                 self.Timer.stop()
2670                 self.close()
2671
2672 class InfoBarAdditionalInfo:
2673         def __init__(self):
2674
2675                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
2676                 self["TimeshiftPossible"] = self["RecordingPossible"]
2677                 self["ExtensionsAvailable"] = Boolean(fixed=1)
2678                 # TODO: these properties should be queried from the input device keymap
2679                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=0)
2680                 self["ShowAudioOnYellow"] = Boolean(fixed=0)
2681                 self["ShowRecordOnRed"] = Boolean(fixed=0)
2682
2683 class InfoBarNotifications:
2684         def __init__(self):
2685                 self.onExecBegin.append(self.checkNotifications)
2686                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
2687                 self.onClose.append(self.__removeNotification)
2688
2689         def __removeNotification(self):
2690                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
2691
2692         def checkNotificationsIfExecing(self):
2693                 if self.execing:
2694                         self.checkNotifications()
2695
2696         def checkNotifications(self):
2697                 notifications = Notifications.notifications
2698                 if notifications:
2699                         n = notifications[0]
2700
2701                         del notifications[0]
2702                         cb = n[0]
2703
2704                         if n[3].has_key("onSessionOpenCallback"):
2705                                 n[3]["onSessionOpenCallback"]()
2706                                 del n[3]["onSessionOpenCallback"]
2707
2708                         if cb:
2709                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
2710                         elif not Notifications.current_notifications and n[4] == "ZapError":
2711                                 if n[3].has_key("timeout"):
2712                                         del n[3]["timeout"]
2713                                 n[3]["enable_input"] = False
2714                                 dlg = self.session.instantiateDialog(n[1], *n[2], **n[3])
2715                                 self.hide()
2716                                 dlg.show()
2717                                 self.notificationDialog = dlg
2718                                 eActionMap.getInstance().bindAction('', -maxint - 1, self.keypressNotification)
2719                         else:
2720                                 dlg = self.session.open(n[1], *n[2], **n[3])
2721
2722                         # remember that this notification is currently active
2723                         d = (n[4], dlg)
2724                         Notifications.current_notifications.append(d)
2725                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
2726
2727         def closeNotificationInstantiateDialog(self):
2728                 if hasattr(self, "notificationDialog"):
2729                         self.session.deleteDialog(self.notificationDialog)
2730                         del self.notificationDialog
2731                         eActionMap.getInstance().unbindAction('', self.keypressNotification)
2732
2733         def keypressNotification(self, key, flag):
2734                 if flag:
2735                         self.closeNotificationInstantiateDialog()
2736
2737         def __notificationClosed(self, d):
2738                 Notifications.current_notifications.remove(d)
2739
2740 class InfoBarServiceNotifications:
2741         def __init__(self):
2742                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2743                         {
2744                                 iPlayableService.evEnd: self.serviceHasEnded
2745                         })
2746
2747         def serviceHasEnded(self):
2748                 print "service end!"
2749
2750                 try:
2751                         self.setSeekState(self.SEEK_STATE_PLAY)
2752                 except:
2753                         pass
2754
2755 class InfoBarCueSheetSupport:
2756         CUT_TYPE_IN = 0
2757         CUT_TYPE_OUT = 1
2758         CUT_TYPE_MARK = 2
2759         CUT_TYPE_LAST = 3
2760
2761         ENABLE_RESUME_SUPPORT = False
2762
2763         def __init__(self, actionmap = "InfobarCueSheetActions"):
2764                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
2765                         {
2766                                 "jumpPreviousMark": (self.jumpPreviousMark, _("Jump to previous marked position")),
2767                                 "jumpNextMark": (self.jumpNextMark, _("Jump to next marked position")),
2768                                 "toggleMark": (self.toggleMark, _("Toggle a cut mark at the current position"))
2769                         }, prio=1)
2770
2771                 self.cut_list = [ ]
2772                 self.is_closing = False
2773                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2774                         {
2775                                 iPlayableService.evStart: self.__serviceStarted,
2776                                 iPlayableService.evCuesheetChanged: self.downloadCuesheet,
2777                         })
2778
2779         def __serviceStarted(self):
2780                 if self.is_closing:
2781                         return
2782                 print "new service started! trying to download cuts!"
2783                 self.downloadCuesheet()
2784
2785                 if self.ENABLE_RESUME_SUPPORT:
2786                         for (pts, what) in self.cut_list:
2787                                 if what == self.CUT_TYPE_LAST:
2788                                         last = pts
2789                                         break
2790                         else:
2791                                 last = getResumePoint(self.session)
2792                         if last is None:
2793                                 return
2794                         # only resume if at least 10 seconds ahead, or <10 seconds before the end.
2795                         seekable = self.__getSeekable()
2796                         if seekable is None:
2797                                 return # Should not happen?
2798                         length = seekable.getLength() or (None,0)
2799                         print "seekable.getLength() returns:", length
2800                         # Hmm, this implies we don't resume if the length is unknown...
2801                         if (last > 900000) and (not length[1]  or (last < length[1] - 900000)):
2802                                 self.resume_point = last
2803                                 l = last / 90000
2804                                 if "ask" in config.usage.on_movie_start.value or not length[1]:
2805                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?") + "\n" + (_("Resume position at %s") % ("%d:%02d:%02d" % (l/3600, l%3600/60, l%60))), timeout=10, default="yes" in config.usage.on_movie_start.value)
2806                                 elif config.usage.on_movie_start.value == "resume":
2807 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2808 # TRANSLATORS: at the start of a movie, when the user has selected
2809 # TRANSLATORS: "Resume from last position" as start behavior.
2810 # TRANSLATORS: The purpose is to notify the user that the movie starts
2811 # TRANSLATORS: in the middle somewhere and not from the beginning.
2812 # TRANSLATORS: (Some translators seem to have interpreted it as a
2813 # TRANSLATORS: question or a choice, but it is a statement.)
2814                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2815
2816         def playLastCB(self, answer):
2817                 if answer == True:
2818                         self.doSeek(self.resume_point)
2819                 self.hideAfterResume()
2820
2821         def hideAfterResume(self):
2822                 if isinstance(self, InfoBarShowHide):
2823                         self.hide()
2824
2825         def __getSeekable(self):
2826                 service = self.session.nav.getCurrentService()
2827                 if service is None:
2828                         return None
2829                 return service.seek()
2830
2831         def cueGetCurrentPosition(self):
2832                 seek = self.__getSeekable()
2833                 if seek is None:
2834                         return None
2835                 r = seek.getPlayPosition()
2836                 if r[0]:
2837                         return None
2838                 return long(r[1])
2839
2840         def cueGetEndCutPosition(self):
2841                 ret = False
2842                 isin = True
2843                 for cp in self.cut_list:
2844                         if cp[1] == self.CUT_TYPE_OUT:
2845                                 if isin:
2846                                         isin = False
2847                                         ret = cp[0]
2848                         elif cp[1] == self.CUT_TYPE_IN:
2849                                 isin = True
2850                 return ret
2851
2852         def jumpPreviousNextMark(self, cmp, start=False):
2853                 current_pos = self.cueGetCurrentPosition()
2854                 if current_pos is None:
2855                         return False
2856                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2857                 if mark is not None:
2858                         pts = mark[0]
2859                 else:
2860                         return False
2861
2862                 self.doSeek(pts)
2863                 return True
2864
2865         def jumpPreviousMark(self):
2866                 # we add 5 seconds, so if the play position is <5s after
2867                 # the mark, the mark before will be used
2868                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2869
2870         def jumpNextMark(self):
2871                 if not self.jumpPreviousNextMark(lambda x: x-90000):
2872                         self.doSeek(-1)
2873
2874         def getNearestCutPoint(self, pts, cmp=abs, start=False):
2875                 # can be optimized
2876                 beforecut = True
2877                 nearest = None
2878                 bestdiff = -1
2879                 instate = True
2880                 if start:
2881                         bestdiff = cmp(0 - pts)
2882                         if bestdiff >= 0:
2883                                 nearest = [0, False]
2884                 for cp in self.cut_list:
2885                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2886                                 beforecut = False
2887                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
2888                                         diff = cmp(cp[0] - pts)
2889                                         if start and diff >= 0:
2890                                                 nearest = cp
2891                                                 bestdiff = diff
2892                                         else:
2893                                                 nearest = None
2894                                                 bestdiff = -1
2895                         if cp[1] == self.CUT_TYPE_IN:
2896                                 instate = True
2897                         elif cp[1] == self.CUT_TYPE_OUT:
2898                                 instate = False
2899                         elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2900                                 diff = cmp(cp[0] - pts)
2901                                 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2902                                         nearest = cp
2903                                         bestdiff = diff
2904                 return nearest
2905
2906         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2907                 current_pos = self.cueGetCurrentPosition()
2908                 if current_pos is None:
2909                         print "not seekable"
2910                         return
2911
2912                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2913
2914                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2915                         if onlyreturn:
2916                                 return nearest_cutpoint
2917                         if not onlyadd:
2918                                 self.removeMark(nearest_cutpoint)
2919                 elif not onlyremove and not onlyreturn:
2920                   &n