fix showing of infobar on servicechange via servicelist
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
index ed8149e488879e3d1393edd8ed7ba1816f6d6e43..da0e6d6ff746b0030b223b1a9ee004a775d44ec2 100644 (file)
@@ -12,13 +12,11 @@ from Components.ProgressBar import *
 from Components.ServiceEventTracker import ServiceEventTracker
 from Components.Sources.CurrentService import CurrentService
 from Components.Sources.EventInfo import EventInfo
 from Components.ServiceEventTracker import ServiceEventTracker
 from Components.Sources.CurrentService import CurrentService
 from Components.Sources.EventInfo import EventInfo
-from Components.Sources.RadioText import RadioText
 from Components.Sources.FrontendStatus import FrontendStatus
 from Components.Sources.Boolean import Boolean
 from Components.Sources.Clock import Clock
 from Components.TimerList import TimerEntryComponent
 from Components.Sources.FrontendStatus import FrontendStatus
 from Components.Sources.Boolean import Boolean
 from Components.Sources.Clock import Clock
 from Components.TimerList import TimerEntryComponent
-from Components.config import config, ConfigBoolean
-
+from Components.config import config, ConfigBoolean, ConfigClock
 from EpgSelection import EPGSelection
 from Plugins.Plugin import PluginDescriptor
 
 from EpgSelection import EPGSelection
 from Plugins.Plugin import PluginDescriptor
 
@@ -32,18 +30,20 @@ from Screens.MinuteInput import MinuteInput
 from Screens.TimerSelection import TimerSelection
 from Screens.PictureInPicture import PictureInPicture
 from Screens.SubtitleDisplay import SubtitleDisplay
 from Screens.TimerSelection import TimerSelection
 from Screens.PictureInPicture import PictureInPicture
 from Screens.SubtitleDisplay import SubtitleDisplay
+from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
 from Screens.SleepTimerEdit import SleepTimerEdit
 from Screens.SleepTimerEdit import SleepTimerEdit
+from Screens.TimeDateInput import TimeDateInput
 from ServiceReference import ServiceReference
 
 from Tools import Notifications
 from ServiceReference import ServiceReference
 
 from Tools import Notifications
-from Tools.Directories import *
+from Tools.Directories import SCOPE_HDD, resolveFilename
 
 
-#from enigma import eTimer, eDVBVolumecontrol, quitMainloop
-from enigma import *
+from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
+       iPlayableService, eServiceReference, eDVBResourceManager, iFrontendInformation, eEPGCache
 
 
-import time
-import os
-import bisect
+from time import time, localtime, strftime
+from os import stat as os_stat
+from bisect import insort
 
 # hack alert!
 from Menu import MainMenu, mdom
 
 # hack alert!
 from Menu import MainMenu, mdom
@@ -66,19 +66,48 @@ class InfoBarShowHide:
                        {
                                "toggleShow": self.toggleShow,
                                "hide": self.hide,
                        {
                                "toggleShow": self.toggleShow,
                                "hide": self.hide,
+                       }, 1) # lower prio to make it possible to override ok and cancel..
+
+               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evStart: self.__serviceStarted,
+                               iPlayableService.evUpdatedEventInfo: self.__eventInfoChanged
                        })
 
                self.__state = self.STATE_SHOWN
                self.__locked = 0
                
                        })
 
                self.__state = self.STATE_SHOWN
                self.__locked = 0
                
-               self.onExecBegin.append(self.show)
-               
                self.hideTimer = eTimer()
                self.hideTimer.timeout.get().append(self.doTimerHide)
                self.hideTimer.start(5000, True)
                
                self.onShow.append(self.__onShow)
                self.onHide.append(self.__onHide)
                self.hideTimer = eTimer()
                self.hideTimer.timeout.get().append(self.doTimerHide)
                self.hideTimer.start(5000, True)
                
                self.onShow.append(self.__onShow)
                self.onHide.append(self.__onHide)
+               self.current_begin_time=0
+
+       def __eventInfoChanged(self):
+               if self.execing:
+                       service = self.session.nav.getCurrentService()
+                       old_begin_time = self.current_begin_time
+                       info = service and service.info()
+                       ptr = info and info.getEvent(0)
+                       self.current_begin_time = ptr and ptr.getBeginTime() or 0
+                       if config.usage.show_infobar_on_event_change.value:
+                               if old_begin_time and old_begin_time != self.current_begin_time:
+                                       self.doShow()
+
+       def __serviceStarted(self, force=False):
+               new = self.servicelist.newServicePlayed()
+               if self.execing or force:
+                       self.current_begin_time=0
+                       if config.usage.show_infobar_on_zap.value:
+                               self.doShow()
+               elif not self.__checkServiceStarted in self.onExecBegin and new:
+                       self.onExecBegin.append(self.__checkServiceStarted)
+
+       def __checkServiceStarted(self):
+               self.__serviceStarted(True)
+               self.onExecBegin.remove(self.__checkServiceStarted)
 
        def __onShow(self):
                self.__state = self.STATE_SHOWN
 
        def __onShow(self):
                self.__state = self.STATE_SHOWN
@@ -193,8 +222,6 @@ class InfoBarNumberZap:
 #              print "You pressed number " + str(number)
                if number == 0:
                        self.servicelist.recallPrevService()
 #              print "You pressed number " + str(number)
                if number == 0:
                        self.servicelist.recallPrevService()
-                       if config.usage.show_infobar_on_zap.value:
-                               self.doShow()
                else:
                        self.session.openWithCallback(self.numberEntered, NumberZap, number)
 
                else:
                        self.session.openWithCallback(self.numberEntered, NumberZap, number)
 
@@ -210,9 +237,9 @@ class InfoBarNumberZap:
                                serviceIterator = servicelist.getNext()
                                if not serviceIterator.valid(): #check end of list
                                        break
                                serviceIterator = servicelist.getNext()
                                if not serviceIterator.valid(): #check end of list
                                        break
-                               if serviceIterator.flags: #assume normal dvb service have no flags set
-                                       continue
-                               num -= 1;
+                               playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
+                               if playable:
+                                       num -= 1;
                        if not num: #found service with searched number ?
                                return serviceIterator, 0
                return None, num
                        if not num: #found service with searched number ?
                                return serviceIterator, 0
                return None, num
@@ -227,12 +254,11 @@ class InfoBarNumberZap:
                        bouquetlist = serviceHandler.list(bouquet)
                        if not bouquetlist is None:
                                while number:
                        bouquetlist = serviceHandler.list(bouquet)
                        if not bouquetlist is None:
                                while number:
-                                       bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
+                                       bouquet = bouquetlist.getNext()
                                        if not bouquet.valid(): #check end of list
                                                break
                                        if not bouquet.valid(): #check end of list
                                                break
-                                       if not (bouquet.flags & eServiceReference.isDirectory):
-                                               continue
-                                       service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
+                                       if bouquet.flags & eServiceReference.isDirectory:
+                                               service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
                if not service is None:
                        if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
                                self.servicelist.clearPath()
                if not service is None:
                        if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
                                self.servicelist.clearPath()
@@ -316,8 +342,6 @@ class InfoBarChannelSelection:
                else:
                        self.servicelist.moveUp()
                self.servicelist.zap()
                else:
                        self.servicelist.moveUp()
                self.servicelist.zap()
-               if config.usage.show_infobar_on_zap.value:
-                       self.doShow()
 
        def zapDown(self):
                if self.servicelist.inBouquet():
 
        def zapDown(self):
                if self.servicelist.inBouquet():
@@ -335,8 +359,6 @@ class InfoBarChannelSelection:
                else:
                        self.servicelist.moveDown()
                self.servicelist.zap()
                else:
                        self.servicelist.moveDown()
                self.servicelist.zap()
-               if config.usage.show_infobar_on_zap.value:
-                       self.doShow()
 
 class InfoBarMenu:
        """ Handles a menu action, to open the (main) menu """
 
 class InfoBarMenu:
        """ Handles a menu action, to open the (main) menu """
@@ -541,10 +563,53 @@ class InfoBarEvent:
                self["Event_Now"] = EventInfo(self.session.nav, EventInfo.NOW)
                self["Event_Next"] = EventInfo(self.session.nav, EventInfo.NEXT)
 
                self["Event_Now"] = EventInfo(self.session.nav, EventInfo.NOW)
                self["Event_Next"] = EventInfo(self.session.nav, EventInfo.NEXT)
 
-class InfoBarRadioText:
-       """provides radio (RDS) text info display"""
+class InfoBarRdsDecoder:
+       """provides RDS and Rass support/display"""
        def __init__(self):
        def __init__(self):
-               self["RadioText"] = RadioText(self.session.nav)
+               self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
+               self.rass_interactive = None
+               
+               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evEnd: self.__serviceStopped,
+                               iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
+                       })
+               
+               self["RdsActions"] = ActionMap(["InfobarRdsActions"],
+               {
+                       "startRassInteractive": self.startRassInteractive
+               },-1)
+
+               self["RdsActions"].setEnabled(False)
+
+               self.onLayoutFinish.append(self.rds_display.show)
+               self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
+
+       def RassInteractivePossibilityChanged(self, state):
+               self["RdsActions"].setEnabled(state)
+
+       def RassSlidePicChanged(self):
+               if not self.rass_interactive:
+                       service = self.session.nav.getCurrentService()
+                       decoder = service and service.rdsDecoder()
+                       if decoder:
+                               decoder.showRassSlidePicture()
+
+       def __serviceStopped(self):
+               if self.rass_interactive is not None:
+                       rass_interactive = self.rass_interactive
+                       self.rass_interactive = None
+                       rass_interactive.close()
+
+       def startRassInteractive(self):
+               self.rds_display.hide()
+               self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
+
+       def RassInteractiveClosed(self, *val):
+               if self.rass_interactive is not None:
+                       self.rass_interactive = None
+                       self.RassSlidePicChanged()
+               self.rds_display.show()
 
 class InfoBarServiceName:
        def __init__(self):
 
 class InfoBarServiceName:
        def __init__(self):
@@ -571,7 +636,9 @@ class InfoBarSeek:
        SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
        SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
        SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
        SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
        SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
        SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
-       
+
+       SEEK_STATE_EOF = (1, 0, 0, "END")
+
        def __init__(self):
                self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
                        {
        def __init__(self):
                self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
                        {
@@ -588,15 +655,18 @@ class InfoBarSeek:
                                self.screen = screen
                                
                        def action(self, contexts, action):
                                self.screen = screen
                                
                        def action(self, contexts, action):
+                               print "action:", action
                                if action[:5] == "seek:":
                                        time = int(action[5:])
                                        self.screen.seekRelative(time * 90000)
                                if action[:5] == "seek:":
                                        time = int(action[5:])
                                        self.screen.seekRelative(time * 90000)
+                                       self.screen.doShow()
                                        return 1
                                else:
                                        return HelpableActionMap.action(self, contexts, action)
 
                self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
                        {
                                        return 1
                                else:
                                        return HelpableActionMap.action(self, contexts, action)
 
                self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
                        {
+                               "playpauseService": (self.playpauseService, _("pause")),
                                "pauseService": (self.pauseService, _("pause")),
                                "unPauseService": (self.unPauseService, _("continue")),
                                
                                "pauseService": (self.pauseService, _("pause")),
                                "unPauseService": (self.unPauseService, _("continue")),
                                
@@ -609,6 +679,8 @@ class InfoBarSeek:
                        }, prio=-1)
                        # give them a little more priority to win over color buttons
 
                        }, prio=-1)
                        # give them a little more priority to win over color buttons
 
+               self["SeekActions"].setEnabled(False)
+
                self.seekstate = self.SEEK_STATE_PLAY
                self.onClose.append(self.delTimer)
                
                self.seekstate = self.SEEK_STATE_PLAY
                self.onClose.append(self.delTimer)
                
@@ -623,7 +695,7 @@ class InfoBarSeek:
                self.onPlayStateChanged = [ ]
                
                self.lockedBecauseOfSkipping = False
                self.onPlayStateChanged = [ ]
                
                self.lockedBecauseOfSkipping = False
-       
+
        def up(self):
                pass
        
        def up(self):
                pass
        
@@ -663,6 +735,7 @@ class InfoBarSeek:
 
        def __serviceStarted(self):
                self.seekstate = self.SEEK_STATE_PLAY
 
        def __serviceStarted(self):
                self.seekstate = self.SEEK_STATE_PLAY
+               self.__seekableStatusChanged()
 
        def setSeekState(self, state):
                service = self.session.nav.getCurrentService()
 
        def setSeekState(self, state):
                service = self.session.nav.getCurrentService()
@@ -673,26 +746,32 @@ class InfoBarSeek:
                if not self.isSeekable():
                        if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
                                state = self.SEEK_STATE_PLAY
                if not self.isSeekable():
                        if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
                                state = self.SEEK_STATE_PLAY
-               
+
                pauseable = service.pause()
 
                if pauseable is None:
                        print "not pauseable."
                        state = self.SEEK_STATE_PLAY
                pauseable = service.pause()
 
                if pauseable is None:
                        print "not pauseable."
                        state = self.SEEK_STATE_PLAY
-               
+
                oldstate = self.seekstate
                self.seekstate = state
                oldstate = self.seekstate
                self.seekstate = state
-               
+
                for i in range(3):
                        if oldstate[i] != self.seekstate[i]:
                                (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
 
                for c in self.onPlayStateChanged:
                        c(self.seekstate)
                for i in range(3):
                        if oldstate[i] != self.seekstate[i]:
                                (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
 
                for c in self.onPlayStateChanged:
                        c(self.seekstate)
-               
+
                self.checkSkipShowHideLock()
 
                return True
                self.checkSkipShowHideLock()
 
                return True
+       
+       def playpauseService(self):
+               if self.seekstate != self.SEEK_STATE_PLAY:
+                       self.unPauseService()
+               else:
+                       self.pauseService()
 
        def pauseService(self):
                if self.seekstate == self.SEEK_STATE_PAUSE:
 
        def pauseService(self):
                if self.seekstate == self.SEEK_STATE_PAUSE:
@@ -757,7 +836,8 @@ class InfoBarSeek:
                                self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
                                self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
                                self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
                                self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
                                self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
                                self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
-                               self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
+                               self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER,
+                               self.SEEK_STATE_EOF: self.SEEK_STATE_EOF,
                        }
                self.setSeekState(lookup[self.seekstate])
        
                        }
                self.setSeekState(lookup[self.seekstate])
        
@@ -784,7 +864,8 @@ class InfoBarSeek:
                                self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
                                self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
                                self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
                                self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
                                self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
                                self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
-                               self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
+                               self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE,
+                               self.SEEK_STATE_EOF: self.SEEK_STATE_BACK_16X,
                        }
                self.setSeekState(lookup[self.seekstate])
                
                        }
                self.setSeekState(lookup[self.seekstate])
                
@@ -829,13 +910,18 @@ class InfoBarSeek:
                                self.lockedBecauseOfSkipping = True
 
        def __evEOF(self):
                                self.lockedBecauseOfSkipping = True
 
        def __evEOF(self):
-               if self.seekstate != self.SEEK_STATE_PLAY:
-                       self.setSeekState(self.SEEK_STATE_PAUSE)
-                       # HACK
-                       #self.getSeek().seekRelative(1, -90000)
-                       self.setSeekState(self.SEEK_STATE_PLAY)
+               if self.seekstate == self.SEEK_STATE_EOF:
+                       return
+               if self.seekstate[1] < 0: # SEEK_STATE_BACK_*X
+                       print "end of stream while seeking back, ignoring."
+                       return
+
+               # if we are seeking, we try to end up ~1s before the end, and pause there.
+               if not self.seekstate in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
+                       self.setSeekState(self.SEEK_STATE_EOF)
+                       self.seekRelativeToEnd(-90000)
                else:
                else:
-                       self.setSeekState(self.SEEK_STATE_PAUSE)
+                       self.setSeekState(self.SEEK_STATE_EOF)
        
        def __evSOF(self):
                self.setSeekState(self.SEEK_STATE_PLAY)
        
        def __evSOF(self):
                self.setSeekState(self.SEEK_STATE_PLAY)
@@ -844,7 +930,21 @@ class InfoBarSeek:
        def seekRelative(self, diff):
                seekable = self.getSeek()
                if seekable is not None:
        def seekRelative(self, diff):
                seekable = self.getSeek()
                if seekable is not None:
-                       seekable.seekRelative(1, diff)
+                       print "seekRelative: res:", seekable.seekRelative(1, diff)
+               else:
+                       print "seek failed!"
+
+       def seekRelativeToEnd(self, diff):
+               assert diff <= 0, "diff is expected to be negative!"
+
+               # might sound like an evil hack, but:
+               # if we seekRelativeToEnd(0), we expect to be at the end, which is what we want,
+               # and we don't get that by passing 0 here (it would seek to begin).
+               if diff == 0:
+                       diff = -1
+
+               # relative-to-end seeking is implemented as absolutes seeks with negative time
+               self.seekAbsolute(diff)
 
        def seekAbsolute(self, abs):
                seekable = self.getSeek()
 
        def seekAbsolute(self, abs):
                seekable = self.getSeek()
@@ -857,22 +957,26 @@ class InfoBarPVRState:
        def __init__(self, screen=PVRState):
                self.onPlayStateChanged.append(self.__playStateChanged)
                self.pvrStateDialog = self.session.instantiateDialog(screen)
        def __init__(self, screen=PVRState):
                self.onPlayStateChanged.append(self.__playStateChanged)
                self.pvrStateDialog = self.session.instantiateDialog(screen)
-               self.onShow.append(self.__mayShow)
+               self.onShow.append(self._mayShow)
                self.onHide.append(self.pvrStateDialog.hide)
                self.onHide.append(self.pvrStateDialog.hide)
-       
-       def __mayShow(self):
-               if self.seekstate != self.SEEK_STATE_PLAY and self.execing:
+
+       def _mayShow(self):
+               if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
                        self.pvrStateDialog.show()
 
        def __playStateChanged(self, state):
                playstateString = state[3]
                self.pvrStateDialog["state"].setText(playstateString)
                        self.pvrStateDialog.show()
 
        def __playStateChanged(self, state):
                playstateString = state[3]
                self.pvrStateDialog["state"].setText(playstateString)
-               self.__mayShow()
+               self._mayShow()
 
 class InfoBarTimeshiftState(InfoBarPVRState):
        def __init__(self):
                InfoBarPVRState.__init__(self, screen=TimeshiftState)
 
 
 class InfoBarTimeshiftState(InfoBarPVRState):
        def __init__(self):
                InfoBarPVRState.__init__(self, screen=TimeshiftState)
 
+       def _mayShow(self):
+               if self.execing and self.timeshift_enabled:
+                       self.pvrStateDialog.show()
+
 class InfoBarShowMovies:
 
        # i don't really like this class. 
 class InfoBarShowMovies:
 
        # i don't really like this class. 
@@ -893,7 +997,7 @@ class InfoBarShowMovies:
 # Timeshift works the following way:
 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
 # Timeshift works the following way:
 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
-# - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
+# - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
@@ -923,21 +1027,21 @@ class InfoBarTimeshift:
                        }, prio=1)
                self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
                        {
                        }, prio=1)
                self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
                        {
-                               "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
-                               "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
+                               "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
+                               "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
                        }, prio=-1) # priority over record
 
                self.timeshift_enabled = 0
                self.timeshift_state = 0
                        }, prio=-1) # priority over record
 
                self.timeshift_enabled = 0
                self.timeshift_state = 0
-               self.ts_pause_timer = eTimer()
-               self.ts_pause_timer.timeout.get().append(self.pauseService)
+               self.ts_rewind_timer = eTimer()
+               self.ts_rewind_timer.timeout.get().append(self.rewindService)
 
                self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
                        {
                                iPlayableService.evStart: self.__serviceStarted,
                                iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
                        })
 
                self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
                        {
                                iPlayableService.evStart: self.__serviceStarted,
                                iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
                        })
-       
+
        def getTimeshift(self):
                service = self.session.nav.getCurrentService()
                return service and service.timeshift()
        def getTimeshift(self):
                service = self.session.nav.getCurrentService()
                return service and service.timeshift()
@@ -948,21 +1052,21 @@ class InfoBarTimeshift:
                if ts is None:
                        self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
                        print "no ts interface"
                if ts is None:
                        self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
                        print "no ts interface"
-                       return 0;
-               
+                       return 0
+
                if self.timeshift_enabled:
                        print "hu, timeshift already enabled?"
                else:
                        if not ts.startTimeshift():
                if self.timeshift_enabled:
                        print "hu, timeshift already enabled?"
                else:
                        if not ts.startTimeshift():
-                               import time
                                self.timeshift_enabled = 1
 
                                # we remove the "relative time" for now.
                                #self.pvrStateDialog["timeshift"].setRelative(time.time())
                                self.timeshift_enabled = 1
 
                                # we remove the "relative time" for now.
                                #self.pvrStateDialog["timeshift"].setRelative(time.time())
-                                       
+
                                # PAUSE.
                                # PAUSE.
-                               self.setSeekState(self.SEEK_STATE_PAUSE)
-                               
+                               #self.setSeekState(self.SEEK_STATE_PAUSE)
+                               self.activateTimeshiftEnd(False)
+
                                # enable the "TimeshiftEnableActions", which will override
                                # the startTimeshift actions
                                self.__seekableStatusChanged()
                                # enable the "TimeshiftEnableActions", which will override
                                # the startTimeshift actions
                                self.__seekableStatusChanged()
@@ -991,38 +1095,35 @@ class InfoBarTimeshift:
 
                # disable actions
                self.__seekableStatusChanged()
 
                # disable actions
                self.__seekableStatusChanged()
-       
+
        # activates timeshift, and seeks to (almost) the end
        # activates timeshift, and seeks to (almost) the end
-       def activateTimeshiftEnd(self):
+       def activateTimeshiftEnd(self, back = True):
                ts = self.getTimeshift()
                ts = self.getTimeshift()
-               
+               print "activateTimeshiftEnd"
+
                if ts is None:
                        return
                if ts is None:
                        return
-               
+
                if ts.isTimeshiftActive():
                        print "!! activate timeshift called - but shouldn't this be a normal pause?"
                        self.pauseService()
                else:
                if ts.isTimeshiftActive():
                        print "!! activate timeshift called - but shouldn't this be a normal pause?"
                        self.pauseService()
                else:
-                       self.setSeekState(self.SEEK_STATE_PLAY)
-                       ts.activateTimeshift()
-                       self.seekRelative(0)
-       
+                       print "play, ..."
+                       ts.activateTimeshift() # activate timeshift will automatically pause
+                       self.setSeekState(self.SEEK_STATE_PAUSE)
+                       self.seekRelativeToEnd(-90000) # seek approx. 1 sec before end
+
+               if back:
+                       self.ts_rewind_timer.start(200, 1)
+
+       def rewindService(self):
+               self.setSeekState(self.SEEK_STATE_BACK_16X)
+
        # same as activateTimeshiftEnd, but pauses afterwards.
        def activateTimeshiftEndAndPause(self):
        # same as activateTimeshiftEnd, but pauses afterwards.
        def activateTimeshiftEndAndPause(self):
-               state = self.seekstate
-               self.activateTimeshiftEnd()
-               
-               # well, this is "andPause", but it could be pressed from pause,
-               # when pausing on the (fake-)"live" picture, so an un-pause
-               # is perfectly ok.
-               
-               print "now, pauseService"
-               if state == self.SEEK_STATE_PLAY:
-                       print "is PLAYING, start pause timer"
-                       self.ts_pause_timer.start(200, 1)
-               else:
-                       print "unpause"
-                       self.unPauseService()
+               print "activateTimeshiftEndAndPause"
+               #state = self.seekstate
+               self.activateTimeshiftEnd(False)
        
        def __seekableStatusChanged(self):
                enabled = False
        
        def __seekableStatusChanged(self):
                enabled = False
@@ -1241,8 +1342,8 @@ class InfoBarInstantRecord:
                except:
                        pass
 
                except:
                        pass
 
-               begin = time.time()
-               end = time.time() + 3600 * 10
+               begin = time()
+               end = time() + 3600 * 10
                name = "instant record"
                description = ""
                eventid = None
                name = "instant record"
                description = ""
                eventid = None
@@ -1292,22 +1393,39 @@ class InfoBarInstantRecord:
                                self.changeDuration(0)
                        else:
                                self.session.openWithCallback(self.changeDuration, TimerSelection, list)
                                self.changeDuration(0)
                        else:
                                self.session.openWithCallback(self.changeDuration, TimerSelection, list)
+               elif answer[1] == "changeendtime":
+                       if len(self.recording) == 1:
+                               self.setEndtime(0)
+                       else:
+                               self.session.openWithCallback(self.setEndTime, TimerSelection, list)
                elif answer[1] == "stop":
                        if len(self.recording) == 1:
                                self.stopCurrentRecording(0)
                        else:
                                self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
                elif answer[1] == "stop":
                        if len(self.recording) == 1:
                                self.stopCurrentRecording(0)
                        else:
                                self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
-               if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
-                       limitEvent = False
-                       if answer[1] == "event":
-                               limitEvent = True
+               elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
+                       self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
                        if answer[1] == "manualduration":
                        if answer[1] == "manualduration":
-                               self.selectedEntry = len(self.recording)
-                               self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
-                       self.startInstantRecording(limitEvent = limitEvent)
-                       
+                               self.changeDuration(len(self.recording)-1)
+                       elif answer[1] == "manualendtime":
+                               self.setEndtime(len(self.recording)-1)
                print "after:\n", self.recording
 
                print "after:\n", self.recording
 
+       def setEndtime(self, entry):
+               if entry is not None:
+                       self.selectedEntry = entry
+                       self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
+                       dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
+                       dlg.setTitle(_("Please change recording endtime"))
+
+       def TimeDateInputClosed(self, ret):
+               if len(ret) > 1:
+                       if ret[0]:
+                               localendtime = localtime(ret[1])
+                               print "stopping recording at", strftime("%c", localendtime)
+                               self.recording[self.selectedEntry].end = ret[1]
+                               self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
+
        def changeDuration(self, entry):
                if entry is not None:
                        self.selectedEntry = entry
        def changeDuration(self, entry):
                if entry is not None:
                        self.selectedEntry = entry
@@ -1316,20 +1434,35 @@ class InfoBarInstantRecord:
        def inputCallback(self, value):
                if value is not None:
                        print "stopping recording after", int(value), "minutes."
        def inputCallback(self, value):
                if value is not None:
                        print "stopping recording after", int(value), "minutes."
-                       self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
+                       self.recording[self.selectedEntry].end = time() + 60 * int(value)
                        self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
 
        def instantRecord(self):
                try:
                        self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
 
        def instantRecord(self):
                try:
-                       stat = os.stat(resolveFilename(SCOPE_HDD))
+                       stat = os_stat(resolveFilename(SCOPE_HDD))
                except:
                        self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
                        return
 
                if self.isInstantRecordRunning():
                except:
                        self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
                        return
 
                if self.isInstantRecordRunning():
-                       self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("A recording is currently running.\nWhat do you want to do?"), list=[(_("stop recording"), "stop"), (_("change recording (duration)"), "changeduration"), (_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"), (_("do nothing"), "no")])
+                       self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
+                               title=_("A recording is currently running.\nWhat do you want to do?"), \
+                               list=[(_("stop recording"), "stop"), \
+                               (_("change recording (duration)"), "changeduration"), \
+                               (_("change recording (endtime)"), "changeendtime"), \
+                               (_("add recording (indefinitely)"), "indefinitely"), \
+                               (_("add recording (stop after current event)"), "event"), \
+                               (_("add recording (enter recording duration)"), "manualduration"), \
+                               (_("add recording (enter recording endtime)"), "manualendtime"), \
+                               (_("do nothing"), "no")])
                else:
                else:
-                       self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Start recording?"), list=[(_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"),(_("don't record"), "no")])
+                       self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
+                               title=_("Start recording?"), \
+                               list=[(_("add recording (indefinitely)"), "indefinitely"), \
+                               (_("add recording (stop after current event)"), "event"), \
+                               (_("add recording (enter recording duration)"), "manualduration"), \
+                               (_("add recording (enter recording endtime)"), "manualendtime"), \
+                               (_("don't record"), "no")])
 
 from Tools.ISO639 import LanguageCodes
 
 
 from Tools.ISO639 import LanguageCodes
 
@@ -1453,8 +1586,6 @@ class InfoBarSubserviceSelection:
                                if newservice.valid():
                                        del subservices
                                        del service
                                if newservice.valid():
                                        del subservices
                                        del service
-                                       if config.usage.show_infobar_on_zap.value:
-                                               self.doShow()
                                        self.session.nav.playService(newservice)
 
        def subserviceSelection(self):
                                        self.session.nav.playService(newservice)
 
        def subserviceSelection(self):
@@ -1495,8 +1626,6 @@ class InfoBarSubserviceSelection:
                                        self.session.open(SubservicesQuickzap, service[2])
                        else:
                                self["SubserviceQuickzapAction"].setEnabled(True)
                                        self.session.open(SubservicesQuickzap, service[2])
                        else:
                                self["SubserviceQuickzapAction"].setEnabled(True)
-                               if config.usage.show_infobar_on_zap.value:
-                                       self.doShow()
                                self.session.nav.playService(service[1])
 
        def addSubserviceToBouquetCallback(self, service):
                                self.session.nav.playService(service[1])
 
        def addSubserviceToBouquetCallback(self, service):
@@ -1537,8 +1666,8 @@ class InfoBarAdditionalInfo:
                self["ExtensionsAvailable"] = Boolean(fixed=1)
 
                self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
                self["ExtensionsAvailable"] = Boolean(fixed=1)
 
                self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
-               res_mgr = eDVBResourceManagerPtr()
-               if eDVBResourceManager.getInstance(res_mgr) == 0:
+               res_mgr = eDVBResourceManager.getInstance()
+               if res_mgr:
                        res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
 
        def tunerUseMaskChanged(self, mask):
                        res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
 
        def tunerUseMaskChanged(self, mask):
@@ -1552,7 +1681,7 @@ class InfoBarAdditionalInfo:
                        self["NimB_Active"].hide()
 
        def checkTunerState(self, service):
                        self["NimB_Active"].hide()
 
        def checkTunerState(self, service):
-               info = service.frontendInfo()
+               info = service and service.frontendInfo()
                feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
                if feNumber is None:
                        self["NimA"].hide()
                feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
                if feNumber is None:
                        self["NimA"].hide()
@@ -1566,7 +1695,7 @@ class InfoBarAdditionalInfo:
 
        def gotServiceEvent(self, ev):
                service = self.session.nav.getCurrentService()
 
        def gotServiceEvent(self, ev):
                service = self.session.nav.getCurrentService()
-               if ev == iPlayableService.evStart:
+               if ev == iPlayableService.evUpdatedInfo or ev == iPlayableService.evEnd:
                        self.checkTunerState(service)
 
 class InfoBarNotifications:
                        self.checkTunerState(service)
 
 class InfoBarNotifications:
@@ -1585,12 +1714,26 @@ class InfoBarNotifications:
        def checkNotifications(self):
                if len(Notifications.notifications):
                        n = Notifications.notifications[0]
        def checkNotifications(self):
                if len(Notifications.notifications):
                        n = Notifications.notifications[0]
+                       
                        Notifications.notifications = Notifications.notifications[1:]
                        cb = n[0]
                        Notifications.notifications = Notifications.notifications[1:]
                        cb = n[0]
+
+                       if n[3].has_key("onSessionOpenCallback"):
+                               n[3]["onSessionOpenCallback"]()
+                               del n[3]["onSessionOpenCallback"]
+
                        if cb is not None:
                        if cb is not None:
-                               self.session.openWithCallback(cb, n[1], *n[2], **n[3])
+                               dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
                        else:
                        else:
-                               self.session.open(n[1], *n[2], **n[3])
+                               dlg = self.session.open(n[1], *n[2], **n[3])
+                       
+                       # remember that this notification is currently active
+                       d = (n[4], dlg)
+                       Notifications.current_notifications.append(d)
+                       dlg.onClose.append(boundFunction(self.__notificationClosed, d))
+
+       def __notificationClosed(self, d):
+               Notifications.current_notifications.remove(d)
 
 class InfoBarServiceNotifications:
        def __init__(self):
 
 class InfoBarServiceNotifications:
        def __init__(self):
@@ -1721,7 +1864,7 @@ class InfoBarCueSheetSupport:
                        return None
 
        def addMark(self, point):
                        return None
 
        def addMark(self, point):
-               bisect.insort(self.cut_list, point)
+               insort(self.cut_list, point)
                self.uploadCuesheet()
 
        def removeMark(self, point):
                self.uploadCuesheet()
 
        def removeMark(self, point):
@@ -1803,6 +1946,7 @@ class InfoBarSubtitleSupport(object):
                                iPlayableService.evUpdatedInfo: self.__updatedInfo
                        })
                self.cached_subtitle_checked = False
                                iPlayableService.evUpdatedInfo: self.__updatedInfo
                        })
                self.cached_subtitle_checked = False
+               self.__selected_subtitle = None
 
        def __serviceStopped(self):
                self.subtitle_window.hide()
 
        def __serviceStopped(self):
                self.subtitle_window.hide()
@@ -1813,8 +1957,7 @@ class InfoBarSubtitleSupport(object):
                if not self.cached_subtitle_checked:
                        subtitle = self.getCurrentServiceSubtitle()
                        self.cached_subtitle_checked = True
                if not self.cached_subtitle_checked:
                        subtitle = self.getCurrentServiceSubtitle()
                        self.cached_subtitle_checked = True
-                       if subtitle:
-                               self.__selected_subtitle = subtitle.getCachedSubtitle()
+                       self.__selected_subtitle = subtitle and subtitle.getCachedSubtitle()
                        if self.__selected_subtitle:
                                subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
                                self.subtitle_window.show()
                        if self.__selected_subtitle:
                                subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
                                self.subtitle_window.show()
@@ -1842,3 +1985,45 @@ class InfoBarSubtitleSupport(object):
 
        subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
        selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
 
        subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
        selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
+
+class InfoBarServiceErrorPopupSupport:
+       def __init__(self):
+               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evTuneFailed: self.__tuneFailed,
+                               iPlayableService.evStart: self.__serviceStarted
+                       })
+               self.__serviceStarted()
+
+       def __serviceStarted(self):
+               self.last_error = None
+               Notifications.RemovePopup(id = "ZapError")
+
+       def __tuneFailed(self):
+               service = self.session.nav.getCurrentService()
+               info = service and service.info()
+               error = info and info.getInfo(iServiceInformation.sDVBState)
+               
+               if error == self.last_error:
+                       error = None
+               else:
+                       self.last_error = error
+
+               errors = {
+                       eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
+                       eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
+                       eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
+                       eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
+                       eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
+                       eDVBServicePMTHandler.eventNewProgramInfo: None,
+                       eDVBServicePMTHandler.eventTuned: None,
+                       eDVBServicePMTHandler.eventSOF: None,
+                       eDVBServicePMTHandler.eventEOF: None
+               }
+
+               error = errors.get(error) #this returns None when the key not exist in the dict
+
+               if error is not None:
+                       Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
+               else:
+                       Notifications.RemovePopup(id = "ZapError")