increase keypress time for skip window a bit, add hack for EOF when seeking
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
index ee51eb9feb2a8deaa61eb93f742a550e5d3e45a3..57c56725d92f0f40ea06968be39bb75d0d3b7057 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 """
@@ -325,7 +344,7 @@ class InfoBarEPG:
                        self.servicelist.setCurrentSelection(service) #select the service in servicelist
                        self.servicelist.zap()
 
-       def openBouquetEPG(self, bouquet):
+       def openBouquetEPG(self, bouquet, withCallback=True):
                ptr=eEPGCache.getInstance()
                services = [ ]
                servicelist = eServiceCenter.getInstance().list(bouquet)
@@ -339,22 +358,28 @@ class InfoBarEPG:
                                services.append(ServiceReference(service))
                if len(services):
                        self.epg_bouquet = bouquet
-                       self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
+                       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):
+       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
-                       self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
+                       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])
+                       self.openBouquetEPG(bouquets[0][1], withCallback)
 
        def openSingleServiceEPG(self):
                ref=self.session.nav.getCurrentlyPlayingServiceReference()
@@ -384,7 +409,7 @@ class InfoBarEPG:
                        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()
+                       self.openMultiServiceEPG(False)
 
        def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
                if len(self.epglist) > 1:
@@ -446,7 +471,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)
@@ -614,12 +639,12 @@ class InfoBarSeek:
        def seekFwd(self):
                print "start fwd timer"
                self.fwdtimer = True
-               self.fwdKeyTimer.start(500)
+               self.fwdKeyTimer.start(1000)
 
        def seekBack(self):
                print "start rewind timer"
                self.rwdtimer = True
-               self.rwdKeyTimer.start(500)
+               self.rwdKeyTimer.start(1000)
 
        def seekFwdUp(self):
                print "seekFwdUp"
@@ -705,7 +730,13 @@ class InfoBarSeek:
                        self.lockedBecauseOfSkipping = True
 
        def __evEOF(self):
-               self.setSeekState(self.SEEK_STATE_PAUSE)
+               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)
@@ -826,10 +857,22 @@ class InfoBarTimeshift:
                                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
 
@@ -894,15 +937,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()
                
@@ -918,15 +960,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:
@@ -1009,19 +1044,27 @@ class InfoBarAdditionalInfo:
                
                self["ButtonRed"] = PixmapConditional(withTimer = False)
                self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
-               self.onShown.append(self["ButtonRed"].update)
+               self.onLayoutFinish.append(self["ButtonRed"].update)
                self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
                self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
-               self.onShown.append(self["ButtonRedText"].update)
+               self.onLayoutFinish.append(self["ButtonRedText"].update)
 
                self["ButtonGreen"] = Pixmap()
                self["ButtonGreenText"] = Label(_("Subservices"))
 
                self["ButtonYellow"] = PixmapConditional(withTimer = False)
-               self["ButtonYellow"].setConnect(lambda: 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
 
@@ -1121,3 +1164,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