show ChannelSelection on first run (after the start wizard)
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
index ddfac4a04e9538ca0f23bf1d7ceabebaf4d6c64f..7a7eb8ff78c3a9cf86cfe1de33d025980763c3c5 100644 (file)
@@ -1,99 +1,52 @@
 from Screen import Screen
 from Components.ActionMap import ActionMap, HelpableActionMap
 from Components.ActionMap import NumberActionMap
 from Screen import Screen
 from Components.ActionMap import ActionMap, HelpableActionMap
 from Components.ActionMap import NumberActionMap
-from Components.Label import Label
+from Components.Label import *
+from Components.ProgressBar import *
 from Components.config import configfile, configsequencearg
 from Components.config import configfile, configsequencearg
-from Components.config import config, configElement, ConfigSubsection, configSequence
-from ChannelSelection import ChannelSelection
+from Components.config import config, configElement, ConfigSubsection, configSequence, configElementBoolean
+from ChannelSelection import ChannelSelection, BouquetSelector
 
 from Components.Pixmap import Pixmap, PixmapConditional
 from Components.BlinkingPixmap import BlinkingPixmapConditional
 from Components.ServiceName import ServiceName
 
 from Components.Pixmap import Pixmap, PixmapConditional
 from Components.BlinkingPixmap import BlinkingPixmapConditional
 from Components.ServiceName import ServiceName
-from Components.EventInfo import EventInfo
+from Components.EventInfo import EventInfo, EventInfoProgress
+from Components.Clock import Clock
+from Components.Input import Input
 
 from ServiceReference import ServiceReference
 from EpgSelection import EPGSelection
 
 from Screens.MessageBox import MessageBox
 
 from ServiceReference import ServiceReference
 from EpgSelection import EPGSelection
 
 from Screens.MessageBox import MessageBox
-from Screens.Volume import Volume
-from Screens.Mute import Mute
+from Screens.ChoiceBox import ChoiceBox
+from Screens.InputBox import InputBox
 from Screens.Dish import Dish
 from Screens.Standby import Standby
 from Screens.Dish import Dish
 from Screens.Standby import Standby
-from Screens.EventView import EventView
+from Screens.EventView import EventViewEPGSelect, EventViewSimple
+from Screens.MinuteInput import MinuteInput
+from Components.Harddisk import harddiskmanager
+
+from Components.ServiceEventTracker import ServiceEventTracker
 
 from Tools import Notifications
 
 from Tools import Notifications
+from Tools.Directories import *
 
 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
 from enigma import *
 
 import time
 import os
 
 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
 from enigma import *
 
 import time
 import os
+import bisect
+
+from Components.config import config, currentConfigSelectionElement
 
 # hack alert!
 from Menu import MainMenu, mdom
 
 
 # hack alert!
 from Menu import MainMenu, mdom
 
-class InfoBarVolumeControl:
-       """Volume control, handles volUp, volDown, volMute actions and display 
-       a corresponding dialog"""
-       def __init__(self):
-               config.audio = ConfigSubsection()
-               config.audio.volume = configElement("config.audio.volume", configSequence, [5], configsequencearg.get("INTEGER", (0, 100)))
-
-               self["VolumeActions"] = ActionMap( ["InfobarVolumeActions"] ,
-                       {
-                               "volumeUp": self.volUp,
-                               "volumeDown": self.volDown,
-                               "volumeMute": self.volMute,
-                       })
-
-               self.volumeDialog = self.session.instantiateDialog(Volume)
-               self.muteDialog = self.session.instantiateDialog(Mute)
-
-               self.hideVolTimer = eTimer()
-               self.hideVolTimer.timeout.get().append(self.volHide)
-
-               vol = config.audio.volume.value[0]
-               self.volumeDialog.setValue(vol)
-               eDVBVolumecontrol.getInstance().setVolume(vol, vol)
-       
-       def volSave(self):
-               config.audio.volume.value = eDVBVolumecontrol.getInstance().getVolume()
-               config.audio.volume.save()
-               
-       def     volUp(self):
-               if (eDVBVolumecontrol.getInstance().isMuted()):
-                       self.volMute()
-               eDVBVolumecontrol.getInstance().volumeUp()
-               self.volumeDialog.instance.show()
-               self.volumeDialog.setValue(eDVBVolumecontrol.getInstance().getVolume())
-               self.volSave()
-               self.hideVolTimer.start(3000)
-
-       def     volDown(self):
-               if (eDVBVolumecontrol.getInstance().isMuted()):
-                       self.volMute()
-               eDVBVolumecontrol.getInstance().volumeDown()
-               self.volumeDialog.instance.show()
-               self.volumeDialog.setValue(eDVBVolumecontrol.getInstance().getVolume())
-               self.volSave()
-               self.hideVolTimer.start(3000)
-               
-       def volHide(self):
-               self.volumeDialog.instance.hide()
-
-       def     volMute(self):
-               eDVBVolumecontrol.getInstance().volumeToggleMute()
-               self.volumeDialog.setValue(eDVBVolumecontrol.getInstance().getVolume())
-               
-               if (eDVBVolumecontrol.getInstance().isMuted()):
-                       self.muteDialog.instance.show()
-               else:
-                       self.muteDialog.instance.hide()
-
 class InfoBarDish:
        def __init__(self):
                self.dishDialog = self.session.instantiateDialog(Dish)
 class InfoBarDish:
        def __init__(self):
                self.dishDialog = self.session.instantiateDialog(Dish)
-               self.onShown.append(self.dishDialog.instance.show)
+               self.onLayoutFinish.append(self.dishDialog.show)
 
 class InfoBarShowHide:
        """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
 
 class InfoBarShowHide:
        """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
@@ -110,50 +63,63 @@ class InfoBarShowHide:
                                "hide": self.hide,
                        })
 
                                "hide": self.hide,
                        })
 
-               self.state = self.STATE_SHOWN
+               self.__state = self.STATE_SHOWN
+               self.__locked = 0
                
                self.onExecBegin.append(self.show)
                
                self.onExecBegin.append(self.show)
-               self.onClose.append(self.delHideTimer)
                
                self.hideTimer = eTimer()
                self.hideTimer.timeout.get().append(self.doTimerHide)
                
                self.hideTimer = eTimer()
                self.hideTimer.timeout.get().append(self.doTimerHide)
-               self.hideTimer.start(5000)
+               self.hideTimer.start(5000, True)
+               
+               self.onShow.append(self.__onShow)
+               self.onHide.append(self.__onHide)
 
 
-       def delHideTimer(self):
-               del self.hideTimer
+       def __onShow(self):
+               self.__state = self.STATE_SHOWN
+               self.startHideTimer()
+       
+       def startHideTimer(self):
+               if self.__state == self.STATE_SHOWN and not self.__locked:
+                       self.hideTimer.start(5000, True)
 
 
-       def hide(self): 
-               self.instance.hide()
-               
-       def show(self):
-               self.state = self.STATE_SHOWN
-               self.hideTimer.stop()
-               self.hideTimer.start(5000)
+       def __onHide(self):
+               self.__state = self.STATE_HIDDEN
+
+       def doShow(self):
+               self.show()
+               self.startHideTimer()
 
        def doTimerHide(self):
                self.hideTimer.stop()
 
        def doTimerHide(self):
                self.hideTimer.stop()
-               if self.state == self.STATE_SHOWN:
-                       self.instance.hide()
-                       self.state = self.STATE_HIDDEN
+               if self.__state == self.STATE_SHOWN:
+                       self.hide()
 
        def toggleShow(self):
 
        def toggleShow(self):
-               if self.state == self.STATE_SHOWN:
-                       self.instance.hide()
-                       #pls check animation support, sorry
-#                      self.startHide()
+               if self.__state == self.STATE_SHOWN:
+                       self.hide()
                        self.hideTimer.stop()
                        self.hideTimer.stop()
-                       self.state = self.STATE_HIDDEN
-               elif self.state == self.STATE_HIDDEN:
-                       self.instance.show()
+               elif self.__state == self.STATE_HIDDEN:
                        self.show()
                        self.show()
-                       
-       def startShow(self):
-               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
-               self.state = self.STATE_SHOWN
+
+       def lockShow(self):
+               self.__locked = self.__locked + 1
+               if self.execing:
+                       self.show()
+                       self.hideTimer.stop()
        
        
-       def startHide(self):
-               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
-               self.state = self.STATE_HIDDEN
+       def unlockShow(self):
+               self.__locked = self.__locked - 1
+               if self.execing:
+                       self.startHideTimer()
+
+#      def startShow(self):
+#              self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
+#              self.__state = self.STATE_SHOWN
+#      
+#      def startHide(self):
+#              self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
+#              self.__state = self.STATE_HIDDEN
 
 class NumberZap(Screen):
        def quit(self):
 
 class NumberZap(Screen):
        def quit(self):
@@ -165,7 +131,7 @@ class NumberZap(Screen):
                self.close(int(self["number"].getText()))
 
        def keyNumberGlobal(self, number):
                self.close(int(self["number"].getText()))
 
        def keyNumberGlobal(self, number):
-               self.Timer.start(3000)          #reset timer
+               self.Timer.start(3000, True)            #reset timer
                self.field = self.field + str(number)
                self["number"].setText(self.field)
                if len(self.field) >= 4:
                self.field = self.field + str(number)
                self["number"].setText(self.field)
                if len(self.field) >= 4:
@@ -197,7 +163,7 @@ class NumberZap(Screen):
 
                self.Timer = eTimer()
                self.Timer.timeout.get().append(self.keyOK)
 
                self.Timer = eTimer()
                self.Timer.timeout.get().append(self.keyOK)
-               self.Timer.start(3000)
+               self.Timer.start(3000, True)
 
 class InfoBarPowerKey:
        """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
 
 class InfoBarPowerKey:
        """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
@@ -219,7 +185,7 @@ class InfoBarPowerKey:
        
        def powerdown(self):
                self.standbyblocked = 0
        
        def powerdown(self):
                self.standbyblocked = 0
-               self.powerKeyTimer.start(3000)
+               self.powerKeyTimer.start(3000, True)
 
        def powerup(self):
                self.powerKeyTimer.stop()
 
        def powerup(self):
                self.powerKeyTimer.stop()
@@ -237,7 +203,7 @@ class InfoBarPowerKey:
 class InfoBarNumberZap:
        """ Handles an initial number for NumberZapping """
        def __init__(self):
 class InfoBarNumberZap:
        """ Handles an initial number for NumberZapping """
        def __init__(self):
-               self["NumberZapActions"] = NumberActionMap( [ "NumberZapActions"],
+               self["NumberActions"] = NumberActionMap( [ "NumberActions"],
                        {
                                "1": self.keyNumberGlobal,
                                "2": self.keyNumberGlobal,
                        {
                                "1": self.keyNumberGlobal,
                                "2": self.keyNumberGlobal,
@@ -253,7 +219,11 @@ class InfoBarNumberZap:
 
        def keyNumberGlobal(self, number):
 #              print "You pressed number " + str(number)
 
        def keyNumberGlobal(self, number):
 #              print "You pressed number " + str(number)
-               self.session.openWithCallback(self.numberEntered, NumberZap, number)
+               if number == 0:
+                       self.servicelist.recallPrevService()
+                       self.doShow()
+               else:
+                       self.session.openWithCallback(self.numberEntered, NumberZap, number)
 
        def numberEntered(self, retval):
 #              print self.servicelist
 
        def numberEntered(self, retval):
 #              print self.servicelist
@@ -284,17 +254,22 @@ 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 = bouquetlist.getNext()
+                                       bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
                                        if not bouquet.valid(): #check end of list
                                                break
                                        if not bouquet.valid(): #check end of list
                                                break
-                                       if ((bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory):
+                                       if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
                                                continue
                                        service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
                if not service is None:
                                                continue
                                        service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
                if not service is None:
-                       self.session.nav.playService(service) #play service
                        if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
                        if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
-                               self.servicelist.setRoot(bouquet)
+                               self.servicelist.clearPath()
+                               if self.servicelist.bouquet_root != bouquet:
+                                       self.servicelist.enterPath(self.servicelist.bouquet_root)
+                               self.servicelist.enterPath(bouquet)
                        self.servicelist.setCurrentSelection(service) #select the service in servicelist
                        self.servicelist.setCurrentSelection(service) #select the service in servicelist
+                       self.servicelist.zap()
+
+config.misc.initialchannelselection = configElementBoolean("config.misc.initialchannelselection", 1);
 
 class InfoBarChannelSelection:
        """ ChannelSelection - handles the channelSelection dialog and the initial 
 
 class InfoBarChannelSelection:
        """ ChannelSelection - handles the channelSelection dialog and the initial 
@@ -302,35 +277,56 @@ class InfoBarChannelSelection:
        def __init__(self):
                #instantiate forever
                self.servicelist = self.session.instantiateDialog(ChannelSelection)
        def __init__(self):
                #instantiate forever
                self.servicelist = self.session.instantiateDialog(ChannelSelection)
+               
+               if config.misc.initialchannelselection.value == 1:
+                       self.onShown.append(self.firstRun)
 
                self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
                        {
                                "switchChannelUp": self.switchChannelUp,
                                "switchChannelDown": self.switchChannelDown,
 
                self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
                        {
                                "switchChannelUp": self.switchChannelUp,
                                "switchChannelDown": self.switchChannelDown,
-                               "zapUp": (self.zapUp, _("next channel")),
-                               "zapDown": (self.zapDown, _("previous channel")),
+                               "zapUp": (self.zapUp, _("previous channel")),
+                               "zapDown": (self.zapDown, _("next channel")),
+                               "historyBack": (self.historyBack, _("previous channel in history")),
+                               "historyNext": (self.historyNext, _("next channel in history"))
                        })
                        })
-                       
-       def switchChannelUp(self):      
+
+       def firstRun(self):
+               self.onShown.remove(self.firstRun)
+               config.misc.initialchannelselection.value = 0
+               config.misc.initialchannelselection.save()
+               self.switchChannelDown()
+               
+       def historyBack(self):
+               self.servicelist.historyBack()
+
+       def historyNext(self):
+               self.servicelist.historyNext()
+
+       def switchChannelUp(self):
                self.servicelist.moveUp()
                self.session.execDialog(self.servicelist)
 
                self.servicelist.moveUp()
                self.session.execDialog(self.servicelist)
 
-       def switchChannelDown(self):    
+       def switchChannelDown(self):
                self.servicelist.moveDown()
                self.session.execDialog(self.servicelist)
 
                self.servicelist.moveDown()
                self.session.execDialog(self.servicelist)
 
-       def     zapUp(self):
+       def zapUp(self):
+               if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
+                       if self.servicelist.inBouquet() and self.servicelist.atBegin():
+                               self.servicelist.prevBouquet()
                self.servicelist.moveUp()
                self.servicelist.zap()
                self.servicelist.moveUp()
                self.servicelist.zap()
-               self.instance.show()
-               self.show()
+               self.doShow()
 
 
-       def     zapDown(self):
-               self.servicelist.moveDown()
+       def zapDown(self):
+               if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
+                       self.servicelist.nextBouquet()
+               else:
+                       self.servicelist.moveDown()
                self.servicelist.zap()
                self.servicelist.zap()
-               self.instance.show()
-               self.show()
-               
+               self.doShow()
+
 class InfoBarMenu:
        """ Handles a menu action, to open the (main) menu """
        def __init__(self):
 class InfoBarMenu:
        """ Handles a menu action, to open the (main) menu """
        def __init__(self):
@@ -345,43 +341,171 @@ class InfoBarMenu:
                assert menu.tagName == "menu", "root element in menu must be 'menu'!"
                self.session.open(MainMenu, menu, menu.childNodes)
 
                assert menu.tagName == "menu", "root element in menu must be 'menu'!"
                self.session.open(MainMenu, menu, menu.childNodes)
 
+class InfoBarSimpleEventView:
+       """ Opens the Eventview for now/next """
+       def __init__(self):
+               self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
+                       {
+                               "showEventInfo": (self.openEventView, _("show event details")),
+                       })
+
+       def openEventView(self):
+               self.epglist = [ ]
+               service = self.session.nav.getCurrentService()
+               ref = self.session.nav.getCurrentlyPlayingServiceReference()
+               info = service.info()
+               ptr=info.getEvent(0)
+               if ptr:
+                       self.epglist.append(ptr)
+               ptr=info.getEvent(1)
+               if ptr:
+                       self.epglist.append(ptr)
+               if len(self.epglist) > 0:
+                       self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
+
+       def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
+               if len(self.epglist) > 1:
+                       tmp = self.epglist[0]
+                       self.epglist[0]=self.epglist[1]
+                       self.epglist[1]=tmp
+                       setEvent(self.epglist[0])
+
 class InfoBarEPG:
        """ EPG - Opens an EPG list when the showEPGList action fires """
        def __init__(self):
                self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
                        {
 class InfoBarEPG:
        """ EPG - Opens an EPG list when the showEPGList action fires """
        def __init__(self):
                self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
                        {
-                               "showEPGList": (self.showEPGList, _("show EPG...")),
+                               "showEventInfo": (self.openEventView, _("show EPG...")),
                        })
 
                        })
 
-       def showEPGList(self):
+       def zapToService(self, service):
+               if not service is None:
+                       if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
+                               self.servicelist.clearPath()
+                               if self.servicelist.bouquet_root != self.epg_bouquet:
+                                       self.servicelist.enterPath(self.servicelist.bouquet_root)
+                               self.servicelist.enterPath(self.epg_bouquet)
+                       self.servicelist.setCurrentSelection(service) #select the service in servicelist
+                       self.servicelist.zap()
+
+       def openBouquetEPG(self, bouquet, withCallback=True):
+               ptr=eEPGCache.getInstance()
+               services = [ ]
+               servicelist = eServiceCenter.getInstance().list(bouquet)
+               if not servicelist is None:
+                       while True:
+                               service = servicelist.getNext()
+                               if not service.valid(): #check if end of list
+                                       break
+                               if service.flags: #ignore non playable services
+                                       continue
+                               services.append(ServiceReference(service))
+               if len(services):
+                       self.epg_bouquet = bouquet
+                       if withCallback:
+                               self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
+                       else:
+                               self.session.open(EPGSelection, services, self.zapToService)
+
+       def closed(self, ret):
+               if ret:
+                       self.close(ret)
+
+       def openMultiServiceEPG(self, withCallback=True):
+               bouquets = self.servicelist.getBouquetList()
+               if bouquets is None:
+                       cnt = 0
+               else:
+                       cnt = len(bouquets)
+               if cnt > 1: # show bouquet list
+                       if withCallback:
+                               self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
+                       else:
+                               self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
+               elif cnt == 1: 
+                       self.openBouquetEPG(bouquets[0][1], withCallback)
+
+       def openSingleServiceEPG(self):
                ref=self.session.nav.getCurrentlyPlayingServiceReference()
                ptr=eEPGCache.getInstance()
                ref=self.session.nav.getCurrentlyPlayingServiceReference()
                ptr=eEPGCache.getInstance()
-               if ptr.startTimeQuery(ref) != -1:
-                       self.session.open(EPGSelection, ref)
-               else: # try to show now/next
-                       print 'no epg for service', ref.toString()
-                       try:
-                               self.epglist = [ ]
-                               service = self.session.nav.getCurrentService()
-                               info = service.info()
-                               ptr=info.getEvent(0)
-                               if ptr:
-                                       self.epglist.append(ptr)
-                               ptr=info.getEvent(1)
+               self.session.openWithCallback(self.closed, EPGSelection, ref)
+
+       def openEventView(self):
+               self.epglist = [ ]
+               service = self.session.nav.getCurrentService()
+               ref = self.session.nav.getCurrentlyPlayingServiceReference()
+               info = service.info()
+               ptr=info.getEvent(0)
+               if ptr:
+                       self.epglist.append(ptr)
+               ptr=info.getEvent(1)
+               if ptr:
+                       self.epglist.append(ptr)
+               if len(self.epglist) == 0:
+                       epg = eEPGCache.getInstance()
+                       ptr = epg.lookupEventTime(ref, -1)
+                       if ptr:
+                               self.epglist.append(ptr)
+                               ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
                                if ptr:
                                        self.epglist.append(ptr)
                                if ptr:
                                        self.epglist.append(ptr)
-                               if len(self.epglist) > 0:
-                                       self.session.open(EventView, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
-                       except:
-                               pass
+               if len(self.epglist) > 0:
+                       self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
+               else:
+                       print "no epg for the service avail.. so we show multiepg instead of eventinfo"
+                       self.openMultiServiceEPG(False)
 
 
-       def eventViewCallback(self, setEvent, val): #used for now/next displaying
+       def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
                if len(self.epglist) > 1:
                        tmp = self.epglist[0]
                        self.epglist[0]=self.epglist[1]
                        self.epglist[1]=tmp
                        setEvent(self.epglist[0])
 
                if len(self.epglist) > 1:
                        tmp = self.epglist[0]
                        self.epglist[0]=self.epglist[1]
                        self.epglist[1]=tmp
                        setEvent(self.epglist[0])
 
+from math import log
+
+class InfoBarTuner:
+       """provides a snr/agc/ber display"""
+       def __init__(self):
+               self["snr"] = Label()
+               self["agc"] = Label()
+               self["ber"] = Label()
+               self["snr_percent"] = Label()
+               self["agc_percent"] = Label()
+               self["ber_count"] = Label()
+               self["snr_progress"] = ProgressBar()
+               self["agc_progress"] = ProgressBar()
+               self["ber_progress"] = ProgressBar()
+               self.timer = eTimer()
+               self.timer.timeout.get().append(self.updateTunerInfo)
+               self.timer.start(1000)
+
+       def calc(self,val):
+               if not val:
+                       return 0
+               if val < 2500:
+                       return (long)(log(val)/log(2))
+               return val*100/65535
+
+       def updateTunerInfo(self):
+               if self.instance.isVisible():
+                       service = self.session.nav.getCurrentService()
+                       snr=0
+                       agc=0
+                       ber=0
+                       if service is not None:
+                               feinfo = service.frontendStatusInfo()
+                               if feinfo is not None:
+                                       ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
+                                       snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
+                                       agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
+                       self["snr_percent"].setText("%d%%"%(snr))
+                       self["agc_percent"].setText("%d%%"%(agc))
+                       self["ber_count"].setText("%d"%(ber))
+                       self["snr_progress"].setValue(snr)
+                       self["agc_progress"].setValue(agc)
+                       self["ber_progress"].setValue(self.calc(ber))
+
 class InfoBarEvent:
        """provides a current/next event info display"""
        def __init__(self):
 class InfoBarEvent:
        """provides a current/next event info display"""
        def __init__(self):
@@ -391,46 +515,500 @@ 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)
 
-               self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Duration)
+               self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
                self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
 
                self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
 
+               self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
+
 class InfoBarServiceName:
        def __init__(self):
                self["ServiceName"] = ServiceName(self.session.nav)
 
 class InfoBarServiceName:
        def __init__(self):
                self["ServiceName"] = ServiceName(self.session.nav)
 
-class InfoBarPVR:
-       """handles PVR specific actions like seeking, pause"""
+class InfoBarSeek:
+       """handles actions like seeking, pause"""
+       
+       # ispause, isff, issm
+       SEEK_STATE_PLAY = (0, 0, 0, ">")
+       SEEK_STATE_PAUSE = (1, 0, 0, "||")
+       SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
+       SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
+       SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
+       SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
+       SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
+       SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
+       
+       SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
+       SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
+       SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
+       SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
+       
+       SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
+       SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
+       SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
+       
        def __init__(self):
        def __init__(self):
-               self["PVRActions"] = HelpableActionMap(self, "InfobarPVRActions", 
+               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
+                               iPlayableService.evStart: self.__serviceStarted,
+                               
+                               iPlayableService.evEOF: self.__evEOF,
+                               iPlayableService.evSOF: self.__evSOF,
+                       })
+
+               class InfoBarSeekActionMap(HelpableActionMap):
+                       def __init__(self, screen, *args, **kwargs):
+                               HelpableActionMap.__init__(self, screen, *args, **kwargs)
+                               self.screen = screen
+                               
+                       def action(self, contexts, action):
+                               if action[:5] == "seek:":
+                                       time = int(action[5:])
+                                       self.screen.seekRelative(time * 90000)
+                                       return 1
+                               else:
+                                       return HelpableActionMap.action(self, contexts, action)
+
+               self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
                        {
                                "pauseService": (self.pauseService, "pause"),
                                "unPauseService": (self.unPauseService, "continue"),
                                
                                "seekFwd": (self.seekFwd, "skip forward"),
                        {
                                "pauseService": (self.pauseService, "pause"),
                                "unPauseService": (self.unPauseService, "continue"),
                                
                                "seekFwd": (self.seekFwd, "skip forward"),
+                               "seekFwdDown": self.seekFwdDown,
+                               "seekFwdUp": self.seekFwdUp,
                                "seekBack": (self.seekBack, "skip backward"),
                                "seekBack": (self.seekBack, "skip backward"),
-                       })
+                               "seekBackDown": self.seekBackDown,
+                               "seekBackUp": self.seekBackUp,
+                       }, prio=-1)
+                       # give them a little more priority to win over color buttons
+
+               self.seekstate = self.SEEK_STATE_PLAY
+               self.onClose.append(self.delTimer)
+               
+               self.fwdtimer = False
+               self.fwdKeyTimer = eTimer()
+               self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
+
+               self.rwdtimer = False
+               self.rwdKeyTimer = eTimer()
+               self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
+               
+               self.onPlayStateChanged = [ ]
+               
+               self.lockedBecauseOfSkipping = False
+       
+       def up(self):
+               pass
+       
+       def down(self):
+               pass
+       
+       def delTimer(self):
+               del self.fwdKeyTimer
+               del self.rwdKeyTimer
+       
+       def getSeek(self):
+               service = self.session.nav.getCurrentService()
+               if service is None:
+                       return False
+
+               seek = service.seek()
+
+               if seek is None or not seek.isCurrentlySeekable():
+                       return None
+               
+               return seek
+       
+       def isSeekable(self):
+               if self.getSeek() is None:
+                       return False
+               return True
+
+       def __seekableStatusChanged(self):
+               print "seekable status changed!"
+               if not self.isSeekable():
+                       self["SeekActions"].setEnabled(False)
+                       print "not seekable, return to play"
+                       self.setSeekState(self.SEEK_STATE_PLAY)
+               else:
+                       self["SeekActions"].setEnabled(True)
+                       print "seekable"
+
+       def __serviceStarted(self):
+               self.seekstate = self.SEEK_STATE_PLAY
+
+       def setSeekState(self, state):
+               service = self.session.nav.getCurrentService()
+               
+               if service is None:
+                       return False
+               
+               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
+               
+               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)
+               
+               self.checkSkipShowHideLock()
+
+               return True
                
        def pauseService(self):
                
        def pauseService(self):
-               self.session.nav.pause(1)
+               if self.seekstate == self.SEEK_STATE_PAUSE:
+                       print "pause, but in fact unpause"
+                       self.unPauseService()
+               else:
+                       if self.seekstate == self.SEEK_STATE_PLAY:
+                               print "yes, playing."
+                       else:
+                               print "no", self.seekstate
+                       print "pause"
+                       self.setSeekState(self.SEEK_STATE_PAUSE);
                
        def unPauseService(self):
                
        def unPauseService(self):
-               self.session.nav.pause(0)
+               print "unpause"
+               self.setSeekState(self.SEEK_STATE_PLAY);
        
        
-       def doSeek(self, dir, seektime):
+       def doSeek(self, seektime):
+               print "doseek", seektime
                service = self.session.nav.getCurrentService()
                if service is None:
                        return
                
                service = self.session.nav.getCurrentService()
                if service is None:
                        return
                
-               seekable = service.seek()
+               seekable = self.getSeek()
                if seekable is None:
                        return
                if seekable is None:
                        return
-               seekable.seekRelative(dir, 90 * seektime)
+               
+               seekable.seekTo(90 * seektime)
+
+       def seekFwdDown(self):
+               print "start fwd timer"
+               self.fwdtimer = True
+               self.fwdKeyTimer.start(1000)
+
+       def seekBackDown(self):
+               print "start rewind timer"
+               self.rwdtimer = True
+               self.rwdKeyTimer.start(1000)
+
+       def seekFwdUp(self):
+               print "seekFwdUp"
+               if self.fwdtimer:
+                       self.fwdKeyTimer.stop()
+                       self.fwdtimer = False
+                       self.seekFwd()
 
        def seekFwd(self):
 
        def seekFwd(self):
-               self.doSeek(+1, 60000)
+               lookup = {
+                               self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
+                               self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
+                               self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
+                               self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
+                               self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
+                               self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
+                               self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
+                               self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
+                               self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
+                               self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
+                               self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
+                               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.setSeekState(lookup[self.seekstate])
        
        
+       def seekBackUp(self):
+               print "seekBackUp"
+               if self.rwdtimer:
+                       self.rwdKeyTimer.stop()
+                       self.rwdtimer = False
+                       self.seekBack()
+               
        def seekBack(self):
        def seekBack(self):
-               self.doSeek(-1, 60000)
+               lookup = {
+                               self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
+                               self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
+                               self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
+                               self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
+                               self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
+                               self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
+                               self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
+                               self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
+                               self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
+                               self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
+                               self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
+                               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.setSeekState(lookup[self.seekstate])
+               
+               if self.seekstate == self.SEEK_STATE_PAUSE:
+                       seekable = self.getSeek()
+                       if seekable is not None:
+                               seekable.seekRelative(-1, 3)
+
+       def fwdTimerFire(self):
+               print "Display seek fwd"
+               self.fwdKeyTimer.stop()
+               self.fwdtimer = False
+               self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
+               
+       def fwdSeekTo(self, minutes):
+               print "Seek", minutes, "minutes forward"
+               if minutes != 0:
+                       seekable = self.getSeek()
+                       if seekable is not None:
+                               seekable.seekRelative(1, minutes * 60 * 90000)
+       
+       def rwdTimerFire(self):
+               print "rwdTimerFire"
+               self.rwdKeyTimer.stop()
+               self.rwdtimer = False
+               self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
+       
+       def rwdSeekTo(self, minutes):
+               print "rwdSeekTo"
+               self.fwdSeekTo(0 - minutes)
+       
+       def checkSkipShowHideLock(self):
+               wantlock = self.seekstate != self.SEEK_STATE_PLAY
+               
+               if self.lockedBecauseOfSkipping and not wantlock:
+                       self.unlockShow()
+                       self.lockedBecauseOfSkipping = False
+               
+               if wantlock and not self.lockedBecauseOfSkipping:
+                       self.lockShow()
+                       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)
+               else:
+                       self.setSeekState(self.SEEK_STATE_PAUSE)
+       
+       def __evSOF(self):
+               self.setSeekState(self.SEEK_STATE_PLAY)
+               self.doSeek(0)
+
+       def seekRelative(self, diff):
+               seekable = self.getSeek()
+               if seekable is not None:
+                       seekable.seekRelative(1, diff)
+
+from Screens.PVRState import PVRState, TimeshiftState
+
+class InfoBarPVRState:
+       def __init__(self, screen=PVRState):
+               self.onPlayStateChanged.append(self.__playStateChanged)
+               self.pvrStateDialog = self.session.instantiateDialog(screen)
+               self.onShow.append(self.__mayShow)
+               self.onHide.append(self.pvrStateDialog.hide)
+       
+       def __mayShow(self):
+               if self.seekstate != self.SEEK_STATE_PLAY:
+                       self.pvrStateDialog.show()
+
+       def __playStateChanged(self, state):
+               playstateString = state[3]
+               self.pvrStateDialog["state"].setText(playstateString)
+               self.__mayShow()
+
+class InfoBarTimeshiftState(InfoBarPVRState):
+       def __init__(self):
+               InfoBarPVRState.__init__(self, screen=TimeshiftState)
+
+
+class InfoBarShowMovies:
+
+       # i don't really like this class. 
+       # it calls a not further specified "movie list" on up/down/movieList,
+       # so this is not more than an action map
+       def __init__(self):
+               self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
+                       {
+                               "movieList": (self.showMovies, "movie list"),
+                               "up": (self.showMovies, "movie list"),
+                               "down": (self.showMovies, "movie list")
+                       })
+
+# InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
+
+# Hrmf.
+#
+# 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 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 backwards                        FILE     record      BACK  # !!         enable                disable              enable
+#
+
+# in other words:
+# - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
+# freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
+# now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
+# - the user can now PVR around
+# - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
+# the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
+# after!
+# the seek actions will be disabled, but the timeshiftActivateActions will be enabled
+# - if the user rewinds, or press pause, timeshift will be activated again
+
+# note that a timeshift can be enabled ("recording") and
+# activated (currently time-shifting).
+
+class InfoBarTimeshift:
+       def __init__(self):
+               self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
+                       {
+                               "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
+                               "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
+                       }, prio=1)
+               self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
+                       {
+                               "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
+                               "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
+                       }, 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.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evStart: self.__serviceStarted,
+                               iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
+                       })
+       
+       def getTimeshift(self):
+               service = self.session.nav.getCurrentService()
+               return service.timeshift()
+
+       def startTimeshift(self):
+               print "enable timeshift"
+               ts = self.getTimeshift()
+               if ts is None:
+                       self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
+                       print "no ts interface"
+                       return
+               
+               if self.timeshift_enabled:
+                       print "hu, timeshift already enabled?"
+               else:
+                       if not ts.startTimeshift():
+                               import time
+                               self.timeshift_enabled = 1
+                               self.pvrStateDialog["timeshift"].setRelative(time.time())
+                               
+                               # PAUSE.
+                               self.setSeekState(self.SEEK_STATE_PAUSE)
+                               
+                               # enable the "TimeshiftEnableActions", which will override
+                               # the startTimeshift actions
+                               self.__seekableStatusChanged()
+                       else:
+                               print "timeshift failed"
+
+       def stopTimeshift(self):
+               if not self.timeshift_enabled:
+                       return
+               print "disable timeshift"
+               ts = self.getTimeshift()
+               if ts is None:
+                       return
+               self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
+
+       def stopTimeshiftConfirmed(self, confirmed):
+               if not confirmed:
+                       return
+
+               ts = self.getTimeshift()
+               if ts is None:
+                       return
+
+               ts.stopTimeshift()
+               self.timeshift_enabled = 0
+
+               # disable actions
+               self.__seekableStatusChanged()
+       
+       # activates timeshift, and seeks to (almost) the end
+       def activateTimeshiftEnd(self):
+               ts = self.getTimeshift()
+               
+               if ts is None:
+                       return
+               
+               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)
+       
+       # 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()
+       
+       def __seekableStatusChanged(self):
+               enabled = False
+               
+               print "self.isSeekable", self.isSeekable()
+               print "self.timeshift_enabled", self.timeshift_enabled
+               
+               # when this service is not seekable, but timeshift
+               # is enabled, this means we can activate
+               # the timeshift
+               if not self.isSeekable() and self.timeshift_enabled:
+                       enabled = True
+
+               print "timeshift activate:", enabled
+               self["TimeshiftActivateActions"].setEnabled(enabled)
+
+       def __serviceStarted(self):
+               self.timeshift_enabled = False
+               self.__seekableStatusChanged()
+
+from RecordTimer import parseEvent
 
 class InfoBarInstantRecord:
        """Instant Record - handles the instantRecord action in order to 
 
 class InfoBarInstantRecord:
        """Instant Record - handles the instantRecord action in order to 
@@ -441,30 +1019,50 @@ class InfoBarInstantRecord:
                                "instantRecord": (self.instantRecord, "Instant Record..."),
                        })
                self.recording = None
                                "instantRecord": (self.instantRecord, "Instant Record..."),
                        })
                self.recording = None
-               
                self["BlinkingPoint"] = BlinkingPixmapConditional()
                self["BlinkingPoint"] = BlinkingPixmapConditional()
-               self.onShown.append(self["BlinkingPoint"].hidePixmap)
+               self["BlinkingPoint"].hide()
                self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
                self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
-               
+
        def stopCurrentRecording(self): 
                self.session.nav.RecordTimer.removeEntry(self.recording)
                self.recording = None
        def stopCurrentRecording(self): 
                self.session.nav.RecordTimer.removeEntry(self.recording)
                self.recording = None
-                       
-       def startInstantRecording(self):
+
+       def startInstantRecording(self, limitEvent = False):
                serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
                serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
-                       
+               
                # try to get event info
                # try to get event info
-               epg = None
+               event = None
                try:
                        service = self.session.nav.getCurrentService()
                try:
                        service = self.session.nav.getCurrentService()
-                       info = service.info()
-                       ev = info.getEvent(0)
-                       epg = ev
+                       epg = eEPGCache.getInstance()
+                       event = epg.lookupEventTime(serviceref, -1, 0)
+                       if event is None:
+                               info = service.info()
+                               ev = info.getEvent(0)
+                               event = ev
                except:
                        pass
                except:
                        pass
+
+               begin = time.time()
+               end = time.time() + 3600 * 10
+               name = "instant record"
+               description = ""
+               eventid = None
                
                
-               # fix me, description. 
-               self.recording = self.session.nav.recordWithTimer(time.time(), time.time() + 3600, serviceref, epg, "instant record")
+               if event is not None:
+                       curEvent = parseEvent(event)
+                       name = curEvent[2]
+                       description = curEvent[3]
+                       eventid = curEvent[4]
+                       if limitEvent:
+                               end = curEvent[1]
+               else:
+                       if limitEvent:
+                               self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
+                               
+               data = (begin, end, name, description, eventid)
+               
+               self.recording = self.session.nav.recordWithTimer(serviceref, *data)
                self.recording.dontSave = True
                
                #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
                self.recording.dontSave = True
                
                #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
@@ -476,25 +1074,42 @@ class InfoBarInstantRecord:
                return False
 
        def recordQuestionCallback(self, answer):
                return False
 
        def recordQuestionCallback(self, answer):
-               if answer == False:
+               if answer is None or answer[1] == "no":
                        return
                
                if self.isInstantRecordRunning():
                        return
                
                if self.isInstantRecordRunning():
-                       self.stopCurrentRecording()
+                       if answer[1] == "manualduration":
+                               self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)                             
+                       else:
+                               self.stopCurrentRecording()
                else:
                else:
-                       self.startInstantRecording()
+                       limitEvent = False
+                       if answer[1] == "event":
+                               limitEvent = True
+                       if answer[1] == "manualduration":
+                               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)
+
+       def inputCallback(self, value):
+               if value is not None:
+                       print "stopping recording after", int(value), "minutes."
+                       if self.recording is not None:
+                               self.recording.end = time.time() + 60 * int(value)
+                               self.session.nav.RecordTimer.timeChanged(self.recording)
 
        def instantRecord(self):
                try:
 
        def instantRecord(self):
                try:
-                       stat = os.stat("/hdd/movies")
+                       stat = os.stat(resolveFilename(SCOPE_HDD))
                except:
                except:
-                       self.session.open(MessageBox, "No HDD found!")
+                       self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
                        return
        
                if self.isInstantRecordRunning():
                        return
        
                if self.isInstantRecordRunning():
-                       self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
+                       self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("A recording is currently running.\nWhat do you want to do?"), list=[(_("stop recording"), "yes"), (_("enter recording duration"), "manualduration"), (_("do nothing"), "no")])
+#                      self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
                else:
                else:
-                       self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
+                       self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Start recording?"), list=[(_("record indefinitely"), "indefinitely"), (_("stop after current event"), "event"), (_("enter recording duration"), "manualduration"),(_("don't record"), "no")])
+                       #self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
 
 from Screens.AudioSelection import AudioSelection
 
 
 from Screens.AudioSelection import AudioSelection
 
@@ -512,25 +1127,119 @@ class InfoBarAudioSelection:
                if n > 0:
                        self.session.open(AudioSelection, audio)
 
                if n > 0:
                        self.session.open(AudioSelection, audio)
 
+from Screens.SubserviceSelection import SubserviceSelection
+
+class InfoBarSubserviceSelection:
+       def __init__(self):
+               self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
+                       {
+                               "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
+                       })
+
+       def subserviceSelection(self):
+               service = self.session.nav.getCurrentService()
+               subservices = service.subServices()
+               n = subservices.getNumberOfSubservices()
+               if n > 0:
+                       self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
+
+       def subserviceSelected(self, service):
+               if not service is None:
+                       self.session.nav.playService(service)
+
 class InfoBarAdditionalInfo:
        def __init__(self):
 class InfoBarAdditionalInfo:
        def __init__(self):
-               self["DolbyActive"] = PixmapConditional()
-               # TODO: get the info from c++ somehow
-               self["DolbyActive"].setConnect(lambda: False)
-               
-               self["CryptActive"] = PixmapConditional()
-               # TODO: get the info from c++ somehow
-               self["CryptActive"].setConnect(lambda: False)
-               
-               self["FormatActive"] = PixmapConditional()
-               # TODO: get the info from c++ somehow
-               self["FormatActive"].setConnect(lambda: False)
+               self["DolbyActive"] = Pixmap()
+               self["CryptActive"] = Pixmap()
+               self["FormatActive"] = Pixmap()
                
                
-               self["ButtonRed"] = Pixmap()
-               self["ButtonRedText"] = Label(_("Record"))
+               self["ButtonRed"] = PixmapConditional(withTimer = False)
+               self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
+               self.onLayoutFinish.append(self["ButtonRed"].update)
+               self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
+               self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
+               self.onLayoutFinish.append(self["ButtonRedText"].update)
+
                self["ButtonGreen"] = Pixmap()
                self["ButtonGreen"] = Pixmap()
-               self["ButtonYellow"] = Pixmap()
-               self["ButtonBlue"] = Pixmap()
+               self["ButtonGreenText"] = Label(_("Subservices"))
+
+               self["ButtonYellow"] = PixmapConditional(withTimer = False)
+               self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
+               self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
+               self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
+               self.onLayoutFinish.append(self["ButtonYellow"].update)
+               self.onLayoutFinish.append(self["ButtonYellowText"].update)
+
+               self["ButtonBlue"] = PixmapConditional(withTimer = False)
+               self["ButtonBlue"].setConnect(lambda: False)
+               self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
+               self["ButtonBlueText"].setConnect(lambda: False)
+               self.onLayoutFinish.append(self["ButtonBlue"].update)
+               self.onLayoutFinish.append(self["ButtonBlueText"].update)
+
+               self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
+
+       def hideSubServiceIndication(self):
+               self["ButtonGreen"].hide()
+               self["ButtonGreenText"].hide()
+
+       def showSubServiceIndication(self):
+               self["ButtonGreen"].show()
+               self["ButtonGreenText"].show()
+
+       def checkFormat(self, service):
+               info = service.info()
+               if info is not None:
+                       aspect = info.getInfo(iServiceInformation.sAspect)
+                       if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
+                               self["FormatActive"].show()
+                       else:
+                               self["FormatActive"].hide()
+
+       def checkSubservices(self, service):
+               if service.subServices().getNumberOfSubservices() > 0:
+                       self.showSubServiceIndication()
+               else:
+                       self.hideSubServiceIndication()
+
+       def checkDolby(self, service):
+               # FIXME
+               dolby = False
+               audio = service.audioTracks()
+               if audio is not None:
+                       n = audio.getNumberOfTracks()
+                       for x in range(n):
+                               i = audio.getTrackInfo(x)
+                               description = i.getDescription();
+                               if description.find("AC3") != -1 or description.find("DTS") != -1:
+                                       dolby = True
+                                       break
+               if dolby:
+                       self["DolbyActive"].show()
+               else:
+                       self["DolbyActive"].hide()
+
+       def checkCrypted(self, service):
+               info = service.info()
+               if info is not None:
+                       if info.getInfo(iServiceInformation.sIsCrypted) > 0:
+                               self["CryptActive"].show()
+                       else:
+                               self["CryptActive"].hide()
+
+       def gotServiceEvent(self, ev):
+               service = self.session.nav.getCurrentService()
+               if ev == iPlayableService.evUpdatedEventInfo:
+                       self.checkSubservices(service)
+                       self.checkFormat(service)
+               elif ev == iPlayableService.evUpdatedInfo:
+                       self.checkCrypted(service)
+                       self.checkDolby(service)
+               elif ev == iPlayableService.evEnd:
+                       self.hideSubServiceIndication()
+                       self["CryptActive"].hide()
+                       self["DolbyActive"].hide()
+                       self["FormatActive"].hide()
 
 class InfoBarNotifications:
        def __init__(self):
 
 class InfoBarNotifications:
        def __init__(self):
@@ -551,3 +1260,157 @@ class InfoBarNotifications:
                                self.session.openWithCallback(cb, *n[1:])
                        else:
                                self.session.open(*n[1:])
                                self.session.openWithCallback(cb, *n[1:])
                        else:
                                self.session.open(*n[1:])
+
+class InfoBarServiceNotifications:
+       def __init__(self):
+               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evEnd: self.serviceHasEnded
+                       })
+
+       def serviceHasEnded(self):
+               print "service end!"
+
+               try:
+                       self.setSeekState(self.SEEK_STATE_PLAY)
+               except:
+                       pass
+
+class InfoBarCueSheetSupport:
+       CUT_TYPE_IN = 0
+       CUT_TYPE_OUT = 1
+       CUT_TYPE_MARK = 2
+       
+       def __init__(self):
+               self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
+                       {
+                               "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
+                               "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
+                               "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
+                       }, prio=1) 
+               
+               self.cut_list = [ ]
+               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evStart: self.__serviceStarted,
+                       })
+
+       def __serviceStarted(self):
+               print "new service started! trying to download cuts!"
+               self.downloadCuesheet()
+
+       def __getSeekable(self):
+               service = self.session.nav.getCurrentService()
+               if service is None:
+                       return None
+               return service.seek()
+
+       def cueGetCurrentPosition(self):
+               seek = self.__getSeekable()
+               if seek is None:
+                       return None
+               r = seek.getPlayPosition()
+               if r[0]:
+                       return None
+               return long(r[1])
+
+       def jumpPreviousNextMark(self, cmp, alternative=None):
+               current_pos = self.cueGetCurrentPosition()
+               if current_pos is None:
+                       return
+               mark = self.getNearestCutPoint(current_pos, cmp=cmp)
+               if mark is not None:
+                       pts = mark[0]
+               elif alternative is not None:
+                       pts = alternative
+               else:
+                       return
+
+               seekable = self.__getSeekable()
+               if seekable is not None:
+                       seekable.seekTo(pts)
+
+       def jumpPreviousMark(self):
+               # we add 2 seconds, so if the play position is <2s after
+               # the mark, the mark before will be used
+               self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
+
+       def jumpNextMark(self):
+               self.jumpPreviousNextMark(lambda x: x)
+
+       def getNearestCutPoint(self, pts, cmp=abs):
+               # can be optimized
+               nearest = None
+               for cp in self.cut_list:
+                       diff = cmp(cp[0] - pts)
+                       if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
+                               nearest = cp
+               return nearest
+
+       def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
+               current_pos = self.cueGetCurrentPosition()
+               if current_pos is None:
+                       print "not seekable"
+                       return
+               
+               nearest_cutpoint = self.getNearestCutPoint(current_pos)
+               
+               if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
+                       if onlyreturn:
+                               return nearest_cutpoint
+                       if not onlyadd:
+                               self.removeMark(nearest_cutpoint)
+               elif not onlyremove and not onlyreturn:
+                       self.addMark((current_pos, self.CUT_TYPE_MARK))
+               
+               if onlyreturn:
+                       return None
+
+       def addMark(self, point):
+               bisect.insort(self.cut_list, point)
+               self.uploadCuesheet()
+
+       def removeMark(self, point):
+               self.cut_list.remove(point)
+               self.uploadCuesheet()
+
+       def __getCuesheet(self):
+               service = self.session.nav.getCurrentService()
+               if service is None:
+                       return None
+               return service.cueSheet()
+
+       def uploadCuesheet(self):
+               cue = self.__getCuesheet()
+
+               if cue is None:
+                       print "upload failed, no cuesheet interface"
+                       return
+               cue.setCutList(self.cut_list)
+
+       def downloadCuesheet(self):
+               cue = self.__getCuesheet()
+
+               if cue is None:
+                       print "upload failed, no cuesheet interface"
+                       return
+               self.cut_list = cue.getCutList()
+
+class InfoBarSummary(Screen):
+       skin = """
+       <screen position="0,0" size="132,64">
+               <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
+               <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
+       </screen>"""
+
+       def __init__(self, session, parent):
+               Screen.__init__(self, session)
+               self["CurrentService"] = ServiceName(self.session.nav)
+               self["Clock"] = Clock()
+
+class InfoBarSummarySupport:
+       def __init__(self):
+               pass
+       
+       def createSummary(self):
+               return InfoBarSummary