add zapping history.. useable with < > buttons
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
index f1267afa0b5206a885d4dbc9e820ba56e447932b..2f3685379dfb0c5f2b375686d172021279cc97a2 100644 (file)
@@ -32,6 +32,7 @@ from enigma import *
 
 import time
 import os
+import bisect
 
 from Components.config import config, currentConfigSelectionElement
 
@@ -41,7 +42,7 @@ from Menu import MainMenu, mdom
 class InfoBarDish:
        def __init__(self):
                self.dishDialog = self.session.instantiateDialog(Dish)
-               self.onShown.append(self.dishDialog.show)
+               self.onLayoutFinish.append(self.dishDialog.show)
 
 class InfoBarShowHide:
        """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
@@ -81,6 +82,10 @@ class InfoBarShowHide:
        def __onHide(self):
                self.__state = self.STATE_HIDDEN
 
+       def doShow(self):
+               self.show()
+               self.startHideTimer()
+
        def doTimerHide(self):
                self.hideTimer.stop()
                if self.__state == self.STATE_SHOWN:
@@ -212,7 +217,7 @@ class InfoBarNumberZap:
 #              print "You pressed number " + str(number)
                if number == 0:
                        self.servicelist.recallPrevService()
-                       self.show()
+                       self.doShow()
                else:
                        self.session.openWithCallback(self.numberEntered, NumberZap, number)
 
@@ -273,8 +278,16 @@ class InfoBarChannelSelection:
                                "switchChannelDown": self.switchChannelDown,
                                "zapUp": (self.zapUp, _("next channel")),
                                "zapDown": (self.zapDown, _("previous channel")),
+                               "historyBack": (self.historyBack, _("previous channel in history")),
+                               "historyNext": (self.historyNext, _("next channel in history"))
                        })
 
+       def historyBack(self):
+               self.servicelist.historyBack()
+
+       def historyNext(self):
+               self.servicelist.historyNext()
+
        def switchChannelUp(self):
                self.servicelist.moveUp()
                self.session.execDialog(self.servicelist)
@@ -283,15 +296,21 @@ class InfoBarChannelSelection:
                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.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.show()
+               self.doShow()
 
 class InfoBarMenu:
        """ Handles a menu action, to open the (main) menu """
@@ -446,7 +465,7 @@ class InfoBarEvent:
                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["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
@@ -523,14 +542,22 @@ class InfoBarSeek:
                del self.fwdKeyTimer
                del self.rwdKeyTimer
        
-       def isSeekable(self):
+       def getSeek(self):
                service = self.session.nav.getCurrentService()
                if service is None:
                        return False
-               if service.seek() is None:
+
+               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
-               else:
-                       return True
+               return True
 
        def __seekableStatusChanged(self):
                print "seekable status changed!"
@@ -551,7 +578,7 @@ class InfoBarSeek:
                if service is None:
                        return False
                
-               if service.seek() is None:
+               if not self.isSeekable():
                        if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
                                state = self.SEEK_STATE_PLAY
                
@@ -597,9 +624,10 @@ class InfoBarSeek:
                if service is None:
                        return
                
-               seekable = service.seek()
+               seekable = self.getSeek()
                if seekable is None:
                        return
+               
                seekable.seekTo(90 * seektime)
 
        def seekFwd(self):
@@ -670,13 +698,9 @@ class InfoBarSeek:
        def fwdSeekTo(self, minutes):
                print "Seek", minutes, "minutes forward"
                if minutes != 0:
-                       service = self.session.nav.getCurrentService()
-                       if service is None:
-                               return
-                       seekable = service.seek()
-                       if seekable is None:
-                               return
-                       seekable.seekRelative(1, minutes * 60 * 90000)
+                       seekable = self.getSeek()
+                       if seekable is not None:
+                               seekable.seekRelative(1, minutes * 60 * 90000)
        
        def rwdTimerFire(self):
                print "rwdTimerFire"
@@ -704,6 +728,12 @@ class InfoBarSeek:
        
        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(0, diff)
 
 from Screens.PVRState import PVRState
 
@@ -775,7 +805,7 @@ class InfoBarTimeshift:
                        {
                                "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
@@ -792,9 +822,6 @@ class InfoBarTimeshift:
                return service.timeshift()
 
        def startTimeshift(self):
-               # TODO: check for harddisk! (or do this in the interface? would make
-               # more sense... for example radio could be timeshifted in memory,
-               # and the decision can't be made here)
                print "enable timeshift"
                ts = self.getTimeshift()
                if ts is None:
@@ -817,12 +844,23 @@ class InfoBarTimeshift:
                        else:
                                print "timeshift failed"
 
-       # nyi
        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
 
@@ -842,6 +880,7 @@ class InfoBarTimeshift:
                else:
                        self.setSeekState(self.SEEK_STATE_PLAY)
                        ts.activateTimeshift()
+                       self.seekRelative(0)
        
        # same as activateTimeshiftEnd, but pauses afterwards.
        def activateTimeshiftEndAndPause(self):
@@ -886,15 +925,14 @@ class InfoBarInstantRecord:
                                "instantRecord": (self.instantRecord, "Instant Record..."),
                        })
                self.recording = None
-               
                self["BlinkingPoint"] = BlinkingPixmapConditional()
-               self.onShown.append(self["BlinkingPoint"].hideWidget)
+               self.onLayoutFinish.append(self["BlinkingPoint"].hideWidget)
                self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
-               
+
        def stopCurrentRecording(self): 
                self.session.nav.RecordTimer.removeEntry(self.recording)
                self.recording = None
-                       
+
        def startInstantRecording(self):
                serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
                
@@ -910,15 +948,8 @@ class InfoBarInstantRecord:
                
                if event is not None:
                        data = parseEvent(event)
-                       begin = data[0]
-                       if begin < time.time():
-                               begin = time.time()
-                       
-                       end = data[1]
-                       if end < begin:
-                               end = begin
-                       
-                       end += 3600 * 10
+                       begin = time.time()
+                       end = begin + 3600 * 10
                        
                        data = (begin, end, data[2], data[3], data[4])
                else:
@@ -1113,3 +1144,119 @@ class InfoBarServiceNotifications:
                        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 __getCurrentPosition(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.__getCurrentPosition()
+               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):
+               print "jumpPreviousMark"
+               # 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):
+               print "jumpNextMark"
+               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):
+               print "toggleMark"
+               current_pos = self.__getCurrentPosition()
+               if current_pos is None:
+                       print "not seekable"
+                       return
+               
+               print "current position: ", current_pos
+
+               nearest_cutpoint = self.getNearestCutPoint(current_pos)
+               print "nearest_cutpoint: ", nearest_cutpoint
+               
+               if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < 5*90000:
+                       self.cut_list.remove(nearest_cutpoint)
+               else:
+                       bisect.insort(self.cut_list, (current_pos, self.CUT_TYPE_MARK))
+               
+               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()
+
+               print "cuts:", self.cut_list