fixes bug #556
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
index e64081e6924db11166e8a997f98af79ae17ff91c..f98d4c3398814653deea13416b28fbba943ea0b7 100644 (file)
@@ -10,6 +10,7 @@ from Components.ServiceEventTracker import ServiceEventTracker
 from Components.Sources.Boolean import Boolean
 from Components.config import config, ConfigBoolean, ConfigClock
 from Components.SystemInfo import SystemInfo
+from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
 from EpgSelection import EPGSelection
 from Plugins.Plugin import PluginDescriptor
 
@@ -24,15 +25,15 @@ 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.TimeDateInput import TimeDateInput
+from Screens.UnhandledKey import UnhandledKey
 from ServiceReference import ServiceReference
 
 from Tools import Notifications
-from Tools.Directories import SCOPE_HDD, resolveFilename, fileExists
+from Tools.Directories import fileExists
 
 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
-       iPlayableService, eServiceReference, eEPGCache
+       iPlayableService, eServiceReference, eEPGCache, eActionMap
 
 from time import time, localtime, strftime
 from os import stat as os_stat
@@ -47,6 +48,39 @@ class InfoBarDish:
        def __init__(self):
                self.dishDialog = self.session.instantiateDialog(Dish)
 
+class InfoBarUnhandledKey:
+       def __init__(self):
+               self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
+               self.hideUnhandledKeySymbolTimer = eTimer()
+               self.hideUnhandledKeySymbolTimer.callback.append(self.unhandledKeyDialog.hide)
+               self.checkUnusedTimer = eTimer()
+               self.checkUnusedTimer.callback.append(self.checkUnused)
+               self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
+               eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
+               eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
+               self.flags = (1<<1);
+               self.uflags = 0;
+
+       #this function is called on every keypress!
+       def actionA(self, key, flag):
+               if flag != 4:
+                       if self.flags & (1<<1):
+                               self.flags = self.uflags = 0
+                       self.flags |= (1<<flag)
+                       if flag == 1: # break
+                               self.checkUnusedTimer.start(0, True)
+               return 0
+
+       #this function is only called when no other action has handled this key
+       def actionB(self, key, flag):
+               if flag != 4:
+                       self.uflags |= (1<<flag)
+
+       def checkUnused(self):
+               if self.flags == self.uflags:
+                       self.unhandledKeyDialog.show()
+                       self.hideUnhandledKeySymbolTimer.start(2000, True)
+
 class InfoBarShowHide:
        """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
        fancy animations. """
@@ -366,28 +400,77 @@ class InfoBarSimpleEventView:
                self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
                        {
                                "showEventInfo": (self.openEventView, _("show event details")),
+                               "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
                        })
 
+       def showEventInfoWhenNotVisible(self):
+               if self.shown:
+                       self.openEventView()
+               else:
+                       self.toggleShow()
+                       return 1
+
        def openEventView(self):
-               self.epglist = [ ]
+               epglist = [ ]
+               self.epglist = epglist
                service = self.session.nav.getCurrentService()
                ref = self.session.nav.getCurrentlyPlayingServiceReference()
                info = service.info()
                ptr=info.getEvent(0)
                if ptr:
-                       self.epglist.append(ptr)
+                       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)
+                       epglist.append(ptr)
+               if epglist:
+                       self.session.open(EventViewSimple, 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])
+               epglist = self.epglist
+               if len(epglist) > 1:
+                       tmp = epglist[0]
+                       epglist[0] = epglist[1]
+                       epglist[1] = tmp
+                       setEvent(epglist[0])
+
+class SimpleServicelist:
+       def __init__(self, services):
+               self.services = services
+               self.length = len(services)
+               self.current = 0
+
+       def selectService(self, service):
+               if not self.length:
+                       self.current = -1
+                       return False
+               else:
+                       self.current = 0
+                       while self.services[self.current].ref != service:
+                               self.current += 1
+                               if self.current >= self.length:
+                                       return False
+               return True
+
+       def nextService(self):
+               if not self.length:
+                       return
+               if self.current+1 < self.length:
+                       self.current += 1
+               else:
+                       self.current = 0
+
+       def prevService(self):
+               if not self.length:
+                       return
+               if self.current-1 > -1:
+                       self.current -= 1
+               else:
+                       self.current = self.length - 1
+
+       def currentService(self):
+               if not self.length or self.current >= self.length:
+                       return None
+               return self.services[self.current]
 
 class InfoBarEPG:
        """ EPG - Opens an EPG list when the showEPGList action fires """
@@ -404,7 +487,7 @@ class InfoBarEPG:
                self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
                        {
                                "showEventInfo": (self.openEventView, _("show EPG...")),
-                               "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
+                               "showEventInfoPlugin": (self.showEventInfoPlugins, _("list of EPG views...")),
                                "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
                        })
 
@@ -440,7 +523,7 @@ class InfoBarEPG:
 
        def openBouquetEPG(self, bouquet, withCallback=True):
                services = self.getBouquetServices(bouquet)
-               if len(services):
+               if services:
                        self.epg_bouquet = bouquet
                        if withCallback:
                                self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
@@ -455,7 +538,7 @@ class InfoBarEPG:
                                self.bouquetSel.up()
                        bouquet = self.bouquetSel.getCurrent()
                        services = self.getBouquetServices(bouquet)
-                       if len(services):
+                       if services:
                                self.epg_bouquet = bouquet
                                epg.setServices(services)
 
@@ -485,20 +568,40 @@ class InfoBarEPG:
                elif cnt == 1:
                        self.openBouquetEPG(bouquets[0][1], withCallback)
 
+       def changeServiceCB(self, direction, epg):
+               if self.serviceSel:
+                       if direction > 0:
+                               self.serviceSel.nextService()
+                       else:
+                               self.serviceSel.prevService()
+                       epg.setService(self.serviceSel.currentService())
+
+       def SingleServiceEPGClosed(self, ret=False):
+               self.serviceSel = None
+
        def openSingleServiceEPG(self):
                ref=self.session.nav.getCurrentlyPlayingServiceReference()
-               self.session.open(EPGSelection, ref)
+               if ref:
+                       if self.servicelist.getMutableList() is not None: # bouquet in channellist
+                               current_path = self.servicelist.getRoot()
+                               services = self.getBouquetServices(current_path)
+                               self.serviceSel = SimpleServicelist(services)
+                               if self.serviceSel.selectService(ref):
+                                       self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
+                               else:
+                                       self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
+                       else:
+                               self.session.open(EPGSelection, ref)
 
        def showEventInfoPlugins(self):
-               list = []
-               for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO):
-                       list.append((p.name, boundFunction(self.runPlugin, p)))
-               if len(list):
+               list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
+
+               if list:
                        list.append((_("show single service EPG..."), self.openSingleServiceEPG))
-                       self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list)
+                       self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
                else:
                        self.openSingleServiceEPG()
-                       
+
        def runPlugin(self, plugin):
                plugin(session = self.session, servicelist = self.servicelist)
                
@@ -510,38 +613,40 @@ class InfoBarEPG:
                self.session.open(EPGSelection, refstr, None, eventid)
 
        def getNowNext(self):
-               self.epglist = [ ]
+               epglist = [ ]
                service = self.session.nav.getCurrentService()
                info = service and service.info()
                ptr = info and info.getEvent(0)
                if ptr:
-                       self.epglist.append(ptr)
+                       epglist.append(ptr)
                ptr = info and info.getEvent(1)
                if ptr:
-                       self.epglist.append(ptr)
+                       epglist.append(ptr)
+               self.epglist = epglist
 
        def __evEventInfoChanged(self):
                if self.is_now_next and len(self.dlg_stack) == 1:
                        self.getNowNext()
                        assert self.eventView
-                       if len(self.epglist):
+                       if self.epglist:
                                self.eventView.setEvent(self.epglist[0])
 
        def openEventView(self):
                ref = self.session.nav.getCurrentlyPlayingServiceReference()
                self.getNowNext()
-               if len(self.epglist) == 0:
+               epglist = self.epglist
+               if not epglist:
                        self.is_now_next = False
                        epg = eEPGCache.getInstance()
                        ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
                        if ptr:
-                               self.epglist.append(ptr)
+                               epglist.append(ptr)
                                ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
                                if ptr:
-                                       self.epglist.append(ptr)
+                                       epglist.append(ptr)
                else:
                        self.is_now_next = True
-               if len(self.epglist) > 0:
+               if epglist:
                        self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
                        self.dlg_stack.append(self.eventView)
                else:
@@ -549,11 +654,12 @@ class InfoBarEPG:
                        self.openMultiServiceEPG(False)
 
        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])
+               epglist = self.epglist
+               if len(epglist) > 1:
+                       tmp = epglist[0]
+                       epglist[0]=epglist[1]
+                       epglist[1]=tmp
+                       setEvent(epglist[0])
 
 class InfoBarRdsDecoder:
        """provides RDS and Rass support/display"""
@@ -619,13 +725,7 @@ class InfoBarSeek:
                                iPlayableService.evEOF: self.__evEOF,
                                iPlayableService.evSOF: self.__evSOF,
                        })
-               self.eofState = 0
-               self.eofTimer = eTimer()
-               self.eofTimer.timeout.get().append(self.doEof)
-               self.eofInhibitTimer = eTimer()
-               self.eofInhibitTimer.timeout.get().append(self.inhibitEof)
-
-               self.minSpeedBackward = useSeekBackHack and 16 or 0
+               self.fast_winding_hint_message_showed = False
 
                class InfoBarSeekActionMap(HelpableActionMap):
                        def __init__(self, screen, *args, **kwargs):
@@ -640,9 +740,9 @@ class InfoBarSeek:
                                        return 1
                                elif action[:8] == "seekdef:":
                                        key = int(action[8:])
-                                       time = [-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
+                                       time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
                                                -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
-                                               -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value][key-1]
+                                               -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
                                        self.screen.doSeekRelative(time * 90000)
                                        return 1                                        
                                else:
@@ -673,24 +773,19 @@ class InfoBarSeek:
                self.__seekableStatusChanged()
 
        def makeStateForward(self, n):
-               minspeed = config.seek.stepwise_minspeed.value
-               repeat = int(config.seek.stepwise_repeat.value)
-               if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
-                       return (0, n * repeat, repeat, ">> %dx" % n)
-               else:
+#              minspeed = config.seek.stepwise_minspeed.value
+#              repeat = int(config.seek.stepwise_repeat.value)
+#              if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
+#                      return (0, n * repeat, repeat, ">> %dx" % n)
+#              else:
                        return (0, n, 0, ">> %dx" % n)
 
        def makeStateBackward(self, n):
-               minspeed = config.seek.stepwise_minspeed.value
-               repeat = int(config.seek.stepwise_repeat.value)
-               if self.minSpeedBackward and n < self.minSpeedBackward:
-                       r = (self.minSpeedBackward - 1)/ n + 1
-                       if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
-                               r = max(r, repeat)
-                       return (0, -n * r, r, "<< %dx" % n)
-               elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
-                       return (0, -n * repeat, repeat, "<< %dx" % n)
-               else:
+#              minspeed = config.seek.stepwise_minspeed.value
+#              repeat = int(config.seek.stepwise_repeat.value)
+#              if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
+#                      return (0, -n * repeat, repeat, "<< %dx" % n)
+#              else:
                        return (0, -n, 0, "<< %dx" % n)
 
        def makeStateSlowMotion(self, n):
@@ -712,7 +807,7 @@ class InfoBarSeek:
                return False
 
        def getLower(self, n, lst):
-               lst = lst+[]
+               lst = lst[:]
                lst.reverse()
                for x in lst:
                        if x < n:
@@ -757,11 +852,9 @@ class InfoBarSeek:
 #                      print "seekable"
 
        def __serviceStarted(self):
+               self.fast_winding_hint_message_showed = False
                self.seekstate = self.SEEK_STATE_PLAY
                self.__seekableStatusChanged()
-               if self.eofState != 0:
-                       self.eofTimer.stop()
-               self.eofState = 0
 
        def setSeekState(self, state):
                service = self.session.nav.getCurrentService()
@@ -770,7 +863,7 @@ class InfoBarSeek:
                        return False
 
                if not self.isSeekable():
-                       if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
+                       if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
                                state = self.SEEK_STATE_PLAY
 
                pauseable = service.pause()
@@ -813,7 +906,7 @@ class InfoBarSeek:
                        if config.seek.on_pause.value == "play":
                                self.unPauseService()
                        elif config.seek.on_pause.value == "step":
-                               self.doSeekRelative(0)
+                               self.doSeekRelative(1)
                        elif config.seek.on_pause.value == "last":
                                self.setSeekState(self.lastseekstate)
                                self.lastseekstate = self.SEEK_STATE_PLAY
@@ -832,16 +925,6 @@ class InfoBarSeek:
                seekable = self.getSeek()
                if seekable is None:
                        return
-               prevstate = self.seekstate
-               if self.eofState == 1:
-                       self.eofState = 2
-                       self.inhibitEof()
-               if self.seekstate == self.SEEK_STATE_EOF:
-                       if prevstate == self.SEEK_STATE_PAUSE:
-                               self.setSeekState(self.SEEK_STATE_PAUSE)
-                       else:
-                               self.setSeekState(self.SEEK_STATE_PLAY)
-               self.eofInhibitTimer.start(200, True)
                seekable.seekTo(pts)
 
        def doSeekRelative(self, pts):
@@ -849,20 +932,24 @@ class InfoBarSeek:
                if seekable is None:
                        return
                prevstate = self.seekstate
-               if self.eofState == 1:
-                       self.eofState = 2
-                       self.inhibitEof()
+
                if self.seekstate == self.SEEK_STATE_EOF:
                        if prevstate == self.SEEK_STATE_PAUSE:
                                self.setSeekState(self.SEEK_STATE_PAUSE)
                        else:
                                self.setSeekState(self.SEEK_STATE_PLAY)
-               self.eofInhibitTimer.start(200, True)
                seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
                if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
                        self.showAfterSeek()
 
        def seekFwd(self):
+               seek = self.getSeek()
+               if seek and not (seek.isCurrentlySeekable() & 2):
+                       if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
+                               self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
+                               self.fast_winding_hint_message_showed = True
+                               return
+                       return 0 # trade as unhandled action
                if self.seekstate == self.SEEK_STATE_PLAY:
                        self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
                elif self.seekstate == self.SEEK_STATE_PAUSE:
@@ -892,30 +979,38 @@ class InfoBarSeek:
                        self.setSeekState(self.makeStateSlowMotion(speed))
 
        def seekBack(self):
-               if self.seekstate == self.SEEK_STATE_PLAY:
+               seek = self.getSeek()
+               if seek and not (seek.isCurrentlySeekable() & 2):
+                       if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
+                               self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
+                               self.fast_winding_hint_message_showed = True
+                               return
+                       return 0 # trade as unhandled action
+               seekstate = self.seekstate
+               if seekstate == self.SEEK_STATE_PLAY:
                        self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
-               elif self.seekstate == self.SEEK_STATE_EOF:
+               elif seekstate == self.SEEK_STATE_EOF:
                        self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
                        self.doSeekRelative(-6)
-               elif self.seekstate == self.SEEK_STATE_PAUSE:
-                       self.doSeekRelative(-3)
-               elif self.isStateForward(self.seekstate):
-                       speed = self.seekstate[1]
-                       if self.seekstate[2]:
-                               speed /= self.seekstate[2]
+               elif seekstate == self.SEEK_STATE_PAUSE:
+                       self.doSeekRelative(-1)
+               elif self.isStateForward(seekstate):
+                       speed = seekstate[1]
+                       if seekstate[2]:
+                               speed /= seekstate[2]
                        speed = self.getLower(speed, config.seek.speeds_forward.value)
                        if speed:
                                self.setSeekState(self.makeStateForward(speed))
                        else:
                                self.setSeekState(self.SEEK_STATE_PLAY)
-               elif self.isStateBackward(self.seekstate):
-                       speed = -self.seekstate[1]
-                       if self.seekstate[2]:
-                               speed /= self.seekstate[2]
+               elif self.isStateBackward(seekstate):
+                       speed = -seekstate[1]
+                       if seekstate[2]:
+                               speed /= seekstate[2]
                        speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
                        self.setSeekState(self.makeStateBackward(speed))
-               elif self.isStateSlowMotion(self.seekstate):
-                       speed = self.getHigher(self.seekstate[2], config.seek.speeds_slowmotion.value)
+               elif self.isStateSlowMotion(seekstate):
+                       speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
                        if speed:
                                self.setSeekState(self.makeStateSlowMotion(speed))
                        else:
@@ -968,44 +1063,19 @@ class InfoBarSeek:
                return False
                
        def __evEOF(self):
-               if self.eofState == 0 and self.seekstate != self.SEEK_STATE_EOF:
-                       self.eofState = 1
-                       time = self.calcRemainingTime()
-                       if not time:
-                               time = 3000   # Failed to calc, use default
-                       elif time == 0:
-                               time = 300    # Passed end, shortest wait
-                       elif time > 15000:
-                               self.eofState = -2  # Too long, block eof
-                               time = 15000
-                       else:
-                               time += 1000  # Add margin
-                       self.eofTimer.start(time, True)
-
-       def inhibitEof(self):
-               if self.eofState >= 1:
-                       self.eofState = -self.eofState
-                       self.eofTimer.stop()
-                       self.doEof()
-
-       def doEof(self):
                if self.seekstate == self.SEEK_STATE_EOF:
                        return
-               if self.eofState == -2 or self.isStateBackward(self.seekstate):
-                       self.eofState = 0
-                       return
 
-               # if we are seeking, we try to end up ~1s before the end, and pause there.
-               eofstate = self.eofState
+               # if we are seeking forward, we try to end up ~1s before the end, and pause there.
                seekstate = self.seekstate
-               self.eofState = 0
-               if not self.seekstate == self.SEEK_STATE_PAUSE:
+               if self.seekstate != self.SEEK_STATE_PAUSE:
                        self.setSeekState(self.SEEK_STATE_EOF)
-               if eofstate == -1 or not seekstate in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
+
+               if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
                        seekable = self.getSeek()
                        if seekable is not None:
                                seekable.seekTo(-1)
-               if eofstate == 1 and seekstate == self.SEEK_STATE_PLAY:
+               if seekstate == self.SEEK_STATE_PLAY: # regular EOF
                        self.doEofInternal(True)
                else:
                        self.doEofInternal(False)
@@ -1020,11 +1090,12 @@ class InfoBarSeek:
 from Screens.PVRState import PVRState, TimeshiftState
 
 class InfoBarPVRState:
-       def __init__(self, screen=PVRState):
+       def __init__(self, screen=PVRState, force_show = False):
                self.onPlayStateChanged.append(self.__playStateChanged)
                self.pvrStateDialog = self.session.instantiateDialog(screen)
                self.onShow.append(self._mayShow)
                self.onHide.append(self.pvrStateDialog.hide)
+               self.force_show = force_show
 
        def _mayShow(self):
                if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
@@ -1035,19 +1106,25 @@ class InfoBarPVRState:
                self.pvrStateDialog["state"].setText(playstateString)
                
                # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
-               if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY:
+               if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
                        self.pvrStateDialog.hide()
                else:
                        self._mayShow()
-                       
 
 class InfoBarTimeshiftState(InfoBarPVRState):
        def __init__(self):
-               InfoBarPVRState.__init__(self, screen=TimeshiftState)
+               InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
+               self.__hideTimer = eTimer()
+               self.__hideTimer.callback.append(self.__hideTimeshiftState)
 
        def _mayShow(self):
-               if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
+               if self.execing and self.timeshift_enabled:
                        self.pvrStateDialog.show()
+                       if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
+                               self.__hideTimer.start(5*1000, True)
+
+       def __hideTimeshiftState(self):
+               self.pvrStateDialog.hide()
 
 class InfoBarShowMovies:
 
@@ -1185,10 +1262,7 @@ class InfoBarTimeshift:
                        self.setSeekState(self.SEEK_STATE_PAUSE)
 
                if back:
-                       self.doSeek(-5) # seek some gops before end
                        self.ts_rewind_timer.start(200, 1)
-               else:
-                       self.doSeek(-1) # seek 1 gop before end
 
        def rewindService(self):
                self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
@@ -1278,10 +1352,10 @@ class InfoBarExtensions:
                                        extensionsList.remove(extension)
                                else:
                                        extensionsList.remove(extension)
-               for x in extensionsList:
-                       list.append((x[0](), x))
+               list.extend([(x[0](), x) for x in extensionsList])
+
                keys += [""] * len(extensionsList)
-               self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys)
+               self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
 
        def extensionCallback(self, answer):
                if answer is not None:
@@ -1299,13 +1373,15 @@ class InfoBarPlugins:
                return name
 
        def getPluginList(self):
-               list = []
-               for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
-                       list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
+               list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
+               list.sort(key = lambda e: e[2]) # sort by name
                return list
 
        def runPlugin(self, plugin):
-               plugin(session = self.session, servicelist = self.servicelist)
+               if isinstance(self, InfoBarChannelSelection):
+                       plugin(session = self.session, servicelist = self.servicelist)
+               else:
+                       plugin(session = self.session)
 
 from Components.Task import job_manager
 class InfoBarJobman:
@@ -1313,10 +1389,7 @@ class InfoBarJobman:
                self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
 
        def getJobList(self):
-               list = []
-               for job in job_manager.getPendingJobs():
-                       list.append(((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None))
-               return list
+               return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
 
        def getJobName(self, job):
                return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
@@ -1329,25 +1402,21 @@ class InfoBarJobman:
        def JobViewCB(self, in_background):
                job_manager.in_background = in_background
 
-# depends on InfoBarExtensions
-class InfoBarSleepTimer:
-       def __init__(self):
-               self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, lambda: True), "1")
-
-       def getSleepTimerName(self):
-               return _("Sleep Timer")
-
-       def showSleepTimerSetup(self):
-               self.session.open(SleepTimerEdit)
-
 # depends on InfoBarExtensions
 class InfoBarPiP:
        def __init__(self):
-               self.session.pipshown = False
+               try:
+                       self.session.pipshown
+               except:
+                       self.session.pipshown = False
                if SystemInfo.get("NumVideoDecoders", 1) > 1:
-                       self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
-                       self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
-                       self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
+                       if (self.allowPiP):
+                               self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
+                               self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
+                               self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
+                       else:
+                               self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
+                               self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
 
        def pipShown(self):
                return self.session.pipshown
@@ -1462,7 +1531,7 @@ class InfoBarInstantRecord:
                if isinstance(serviceref, eServiceReference):
                        serviceref = ServiceReference(serviceref)
 
-               recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
+               recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
                recording.dontSave = True
                
                if event is None or limitEvent == False:
@@ -1474,7 +1543,7 @@ class InfoBarInstantRecord:
                                simulTimerList = self.session.nav.RecordTimer.record(recording)
                                if simulTimerList is not None:  # conflict with other recording
                                        name = simulTimerList[1].name
-                                       name_date = name + strftime(" %c", localtime(simulTimerList[1].begin))
+                                       name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
                                        print "[TIMER] conflicts with", name_date
                                        recording.autoincrease = True   # start with max available length, then increment
                                        if recording.setAutoincreaseEnd():
@@ -1489,7 +1558,7 @@ class InfoBarInstantRecord:
 
        def isInstantRecordRunning(self):
                print "self.recording:", self.recording
-               if len(self.recording) > 0:
+               if self.recording:
                        for x in self.recording:
                                if x.isRunning():
                                        return True
@@ -1532,7 +1601,7 @@ class InfoBarInstantRecord:
                print "after:\n", self.recording
 
        def setEndtime(self, entry):
-               if entry is not None:
+               if entry is not None and entry >= 0:
                        self.selectedEntry = entry
                        self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
                        dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
@@ -1549,22 +1618,23 @@ class InfoBarInstantRecord:
                                self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
 
        def changeDuration(self, entry):
-               if entry is not None:
+               if entry is not None and entry >= 0:
                        self.selectedEntry = entry
                        self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
 
        def inputCallback(self, value):
                if value is not None:
                        print "stopping recording after", int(value), "minutes."
+                       entry = self.recording[self.selectedEntry]
                        if int(value) != 0:
-                               self.recording[self.selectedEntry].autoincrease = False
-                       self.recording[self.selectedEntry].end = int(time()) + 60 * int(value)
-                       self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
+                               entry.autoincrease = False
+                       entry.end = int(time()) + 60 * int(value)
+                       self.session.nav.RecordTimer.timeChanged(entry)
 
        def instantRecord(self):
-               dir = config.movielist.last_videodir.value
-               if not fileExists(dir, 'w'):
-                       dir = resolveFilename(SCOPE_HDD)
+               dir = preferredInstantRecordPath()
+               if not dir or not fileExists(dir, 'w'):
+                       dir = defaultMoviePath()
                try:
                        stat = os_stat(dir)
                except:
@@ -1575,22 +1645,22 @@ class InfoBarInstantRecord:
                if self.isInstantRecordRunning():
                        self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
                                title=_("A recording is currently running.\nWhat do you want to do?"), \
-                               list=[(_("add recording (stop after current event)"), "event"), \
+                               list=((_("stop recording"), "stop"), \
+                               (_("add recording (stop after current event)"), "event"), \
+                               (_("add recording (indefinitely)"), "indefinitely"), \
                                (_("add recording (enter recording duration)"), "manualduration"), \
                                (_("add recording (enter recording endtime)"), "manualendtime"), \
-                               (_("add recording (indefinitely)"), "indefinitely"), \
                                (_("change recording (duration)"), "changeduration"), \
                                (_("change recording (endtime)"), "changeendtime"), \
-                               (_("stop recording"), "stop"), \
-                               (_("do nothing"), "no")])
+                               (_("do nothing"), "no")))
                else:
                        self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
                                title=_("Start recording?"), \
-                               list=[(_("add recording (stop after current event)"), "event"), \
+                               list=((_("add recording (stop after current event)"), "event"), \
+                               (_("add recording (indefinitely)"), "indefinitely"), \
                                (_("add recording (enter recording duration)"), "manualduration"), \
                                (_("add recording (enter recording endtime)"), "manualendtime"), \
-                               (_("add recording (indefinitely)"), "indefinitely"), \
-                               (_("don't record"), "no")])
+                               (_("don't record"), "no")))
 
 from Tools.ISO639 import LanguageCodes
 
@@ -1609,20 +1679,30 @@ class InfoBarAudioSelection:
                if n > 0:
                        self.audioChannel = service.audioChannel()
 
-                       for x in range(n):
-                               i = audio.getTrackInfo(x)
-                               language = i.getLanguage()
+                       idx = 0
+                       while idx < n:
+                               cnt = 0
+                               i = audio.getTrackInfo(idx)
+                               languages = i.getLanguage().split('/')
                                description = i.getDescription()
+                               language = ""
 
-                               if LanguageCodes.has_key(language):
-                                       language = LanguageCodes[language][0]
+                               for lang in languages:
+                                       if cnt:
+                                               language += ' / '
+                                       if LanguageCodes.has_key(lang):
+                                               language += LanguageCodes[lang][0]
+                                       else:
+                                               language += lang
+                                       cnt += 1
 
                                if len(description):
                                        description += " (" + language + ")"
                                else:
                                        description = language
 
-                               tlist.append((description, x))
+                               tlist.append((description, idx))
+                               idx += 1
 
                        tlist.sort(key=lambda x: x[0])
 
@@ -1636,17 +1716,46 @@ class InfoBarAudioSelection:
                                else:
                                        break
 
+                       availableKeys = []
+                       usedKeys = []
+
                        if SystemInfo["CanDownmixAC3"]:
-                               tlist = [(_("AC3 downmix") + " - " +[_("Off"), _("On")][config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
-                                       ([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"),
-                                       ("--", "")] + tlist
-                               keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
-                               selection += 3
-                       else:
-                               tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
-                               keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
+                               flist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
+                                       ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
+                               usedKeys.extend(["red", "green"])
+                               availableKeys.extend(["yellow", "blue"])
                                selection += 2
-                       self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
+                       else:
+                               flist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
+                               usedKeys.extend(["red"])
+                               availableKeys.extend(["green", "yellow", "blue"])
+                               selection += 1
+
+                       if hasattr(self, "runPlugin"):
+                               class PluginCaller:
+                                       def __init__(self, fnc, *args):
+                                               self.fnc = fnc
+                                               self.args = args
+                                       def __call__(self, *args, **kwargs):
+                                               self.fnc(*self.args)
+
+                               Plugins = [ (p.name, PluginCaller(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_AUDIOMENU) ]
+
+                               for p in Plugins:
+                                       selection += 1
+                                       flist.append((p[0], "CALLFUNC", p[1]))
+                                       if availableKeys:
+                                               usedKeys.append(availableKeys[0])
+                                               del availableKeys[0]
+                                       else:
+                                               usedKeys.append("")
+
+                       flist.append(("--", ""))
+                       usedKeys.append("")
+                       selection += 1
+
+                       keys = usedKeys + [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" ] + [""] * n
+                       self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = flist + tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
                else:
                        del self.audioTracks
 
@@ -1655,7 +1764,7 @@ class InfoBarAudioSelection:
                list = choicelist.list
                t = list[0][1]
                list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
-                       _("AC3 downmix") + " - " +[_("On"), _("Off")][config.av.downmix_ac3.value and 1 or 0])
+                       _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
                choicelist.setList(list)
                if config.av.downmix_ac3.value:
                        config.av.downmix_ac3.value = False
@@ -1669,8 +1778,8 @@ class InfoBarAudioSelection:
                                if audio[1] == "mode":
                                        keys = ["red", "green", "yellow"]
                                        selection = self.audioChannel.getCurrentChannel()
-                                       tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
-                                       self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
+                                       tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
+                                       self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
                        else:
                                del self.audioChannel
                                if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
@@ -1724,9 +1833,12 @@ class InfoBarSubserviceSelection:
                if n and n > 0:
                        selection = -1
                        ref = self.session.nav.getCurrentlyPlayingServiceReference()
-                       for x in range(n):
-                               if subservices.getSubservice(x).toString() == ref.toString():
-                                       selection = x
+                       idx = 0
+                       while idx < n:
+                               if subservices.getSubservice(idx).toString() == ref.toString():
+                                       selection = idx
+                                       break
+                               idx += 1
                        if selection != -1:
                                selection += direction
                                if selection >= n:
@@ -1748,11 +1860,13 @@ class InfoBarSubserviceSelection:
                if n and n > 0:
                        ref = self.session.nav.getCurrentlyPlayingServiceReference()
                        tlist = []
-                       for x in range(n):
-                               i = subservices.getSubservice(x)
+                       idx = 0
+                       while idx < n:
+                               i = subservices.getSubservice(idx)
                                if i.toString() == ref.toString():
-                                       selection = x
+                                       selection = idx
                                tlist.append((i.getName(), i))
+                               idx += 1
 
                        if self.bouquets and len(self.bouquets):
                                keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
@@ -1766,7 +1880,7 @@ class InfoBarSubserviceSelection:
                                keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
                                selection += 2
 
-                       self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
+                       self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
 
        def subserviceSelected(self, service):
                del self.bouquets
@@ -1829,10 +1943,11 @@ class InfoBarNotifications:
                        self.checkNotifications()
 
        def checkNotifications(self):
-               if len(Notifications.notifications):
-                       n = Notifications.notifications[0]
+               notifications = Notifications.notifications
+               if notifications:
+                       n = notifications[0]
 
-                       Notifications.notifications = Notifications.notifications[1:]
+                       del notifications[0]
                        cb = n[0]
 
                        if n[3].has_key("onSessionOpenCallback"):
@@ -1905,8 +2020,10 @@ class InfoBarCueSheetSupport:
 
                        if last is not None:
                                self.resume_point = last
+                               
+                               l = last / 90000
                                if config.usage.on_movie_start.value == "ask":
-                                       Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
+                                       Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?") + "\n" + (_("Resume position at %s") % ("%d:%02d:%02d" % (l/3600, l%3600/60, l%60))), timeout=10)
                                elif config.usage.on_movie_start.value == "resume":
 # TRANSLATORS: The string "Resuming playback" flashes for a moment
 # TRANSLATORS: at the start of a movie, when the user has selected
@@ -1985,7 +2102,7 @@ class InfoBarCueSheetSupport:
                        if bestdiff >= 0:
                                nearest = [0, False]
                for cp in self.cut_list:
-                       if beforecut and cp[1] in [self.CUT_TYPE_IN, self.CUT_TYPE_OUT]:
+                       if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
                                beforecut = False
                                if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
                                        diff = cmp(cp[0] - pts)
@@ -1994,7 +2111,7 @@ class InfoBarCueSheetSupport:
                                                bestdiff = diff
                                        else:
                                                nearest = None
-                       if cp[1] in [self.CUT_TYPE_MARK, self.CUT_TYPE_LAST]:
+                       if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
                                diff = cmp(cp[0] - pts)
                                if diff >= 0 and (nearest is None or bestdiff > diff):
                                        nearest = cp
@@ -2080,9 +2197,6 @@ class InfoBarSummary(Screen):
 #                      <convert type="ServiceName">Reference</convert>
 #              </widget>
 
-       def __init__(self, session, parent):
-               Screen.__init__(self, session, parent = parent)
-
 class InfoBarSummarySupport:
        def __init__(self):
                pass
@@ -2108,9 +2222,6 @@ class InfoBarMoviePlayerSummary(Screen):
                </widget>
        </screen>"""
 
-       def __init__(self, session, parent):
-               Screen.__init__(self, session)
-
 class InfoBarMoviePlayerSummarySupport:
        def __init__(self):
                pass
@@ -2151,19 +2262,19 @@ class InfoBarSubtitleSupport(object):
                self.__selected_subtitle = None
 
        def __serviceStopped(self):
-               self.subtitle_window.hide()
-               self.__subtitles_enabled = False
                self.cached_subtitle_checked = False
+               if self.__subtitles_enabled:
+                       self.subtitle_window.hide()
+                       self.__subtitles_enabled = False
+                       self.__selected_subtitle = None
 
        def __updatedInfo(self):
                if not self.cached_subtitle_checked:
-                       subtitle = self.getCurrentServiceSubtitle()
                        self.cached_subtitle_checked = True
-                       self.__selected_subtitle = subtitle and subtitle.getCachedSubtitle()
+                       subtitle = self.getCurrentServiceSubtitle()
+                       self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
                        if self.__selected_subtitle:
-                               subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
-                               self.subtitle_window.show()
-                               self.__subtitles_enabled = True
+                               self.setSubtitlesEnable(True)
 
        def getCurrentServiceSubtitle(self):
                service = self.session.nav.getCurrentService()
@@ -2171,14 +2282,16 @@ class InfoBarSubtitleSupport(object):
 
        def setSubtitlesEnable(self, enable=True):
                subtitle = self.getCurrentServiceSubtitle()
-               if enable and self.__selected_subtitle is not None:
-                       if subtitle and not self.__subtitles_enabled:
-                               subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
-                               self.subtitle_window.show()
-                               self.__subtitles_enabled = True
+               if enable:
+                       if self.__selected_subtitle:
+                               if subtitle and not self.__subtitles_enabled:
+                                       subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
+                                       self.subtitle_window.show()
+                                       self.__subtitles_enabled = True
                else:
                        if subtitle:
                                subtitle.disableSubtitles(self.subtitle_window.instance)
+                       self.__selected_subtitle = False
                        self.__subtitles_enabled = False
                        self.subtitle_window.hide()
 
@@ -2211,7 +2324,7 @@ class InfoBarServiceErrorPopupSupport:
                else:
                        self.last_error = error
 
-               errors = {
+               error = {
                        eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
                        eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
                        eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
@@ -2222,9 +2335,7 @@ class InfoBarServiceErrorPopupSupport:
                        eDVBServicePMTHandler.eventSOF: None,
                        eDVBServicePMTHandler.eventEOF: None,
                        eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
-               }
-
-               error = errors.get(error) #this returns None when the key not exist in the dict
+               }.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")