CutListEditor: always restart service on cutlist editor close
[enigma2.git] / lib / python / Plugins / Extensions / CutListEditor / plugin.py
index 241f43bb407fabc0e5e9b51f3422988b56bc14a1..ee75ec0957610159cd0bfa1ed50dd542df2fcdf5 100644 (file)
@@ -4,17 +4,20 @@ from Screens.Screen import Screen
 from Screens.MessageBox import MessageBox
 from Components.ServicePosition import ServicePositionGauge
 from Components.ActionMap import HelpableActionMap
-from Components.MenuList import MenuList
-from Components.MultiContent import MultiContentEntryText, RT_HALIGN_RIGHT
-from Components.ServiceEventTracker import ServiceEventTracker
+from Components.MultiContent import MultiContentEntryText
+from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
+from Components.VideoWindow import VideoWindow
 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport
 from Components.GUIComponent import GUIComponent
-from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService
+from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService, RT_HALIGN_RIGHT
 from Screens.FixedMenu import FixedMenu
+from Screens.HelpMenu import HelpableScreen
+from ServiceReference import ServiceReference
+from Components.Sources.List import List
+
 import bisect
 
 def CutListEntry(where, what):
-       res = [ (where, what) ]
        w = where / 90
        ms = w % 1000
        s = (w / 1000) % 60
@@ -22,14 +25,17 @@ def CutListEntry(where, what):
        h = w / 3600000
        if what == 0:
                type = "IN"
+               type_col = 0x004000
        elif what == 1:
                type = "OUT"
+               type_col = 0x400000
        elif what == 2:
                type = "MARK"
-       res.append(MultiContentEntryText(size=(400, 20), text = "%dh:%02dm:%02ds:%03d" % (h, m, s, ms)))
-       res.append(MultiContentEntryText(pos=(400,0), size=(130, 20), text = type, flags = RT_HALIGN_RIGHT))
-
-       return res
+               type_col = 0x000040
+       elif what == 3:
+               type = "LAST"
+               type_col = 0x000000
+       return ((where, what), "%dh:%02dm:%02ds:%03d" % (h, m, s, ms), type, type_col)
 
 class CutListContextMenu(FixedMenu):
        RET_STARTCUT = 0
@@ -39,11 +45,12 @@ class CutListContextMenu(FixedMenu):
        RET_DELETEMARK = 4
        RET_REMOVEBEFORE = 5
        RET_REMOVEAFTER = 6
-       
+       RET_GRABFRAME = 7
+
        SHOW_STARTCUT = 0
        SHOW_ENDCUT = 1
        SHOW_DELETECUT = 2
-       
+
        def __init__(self, session, state, nearmark):
                menu = [(_("back"), self.close)] #, (None, )]
 
@@ -72,6 +79,7 @@ class CutListContextMenu(FixedMenu):
                else:
                        menu.append((_("remove this mark"), self.removeMark))
 
+               menu.append((_("grab this frame as bitmap"), self.grabFrame))
                FixedMenu.__init__(self, session, _("Cut"), menu)
                self.skinName = "Menu"
 
@@ -89,82 +97,73 @@ class CutListContextMenu(FixedMenu):
 
        def removeMark(self):
                self.close(self.RET_DELETEMARK)
-       
+
        def removeBefore(self):
                self.close(self.RET_REMOVEBEFORE)
 
        def removeAfter(self):
                self.close(self.RET_REMOVEAFTER)
 
+       def grabFrame(self):
+               self.close(self.RET_GRABFRAME)
 
-class CutList(GUIComponent):
-       def __init__(self, list):
-               GUIComponent.__init__(self)
-               self.l = eListboxPythonMultiContent()
-               self.setList(list)
-               self.l.setFont(0, gFont("Regular", 20))
-               self.onSelectionChanged = [ ]
-       
-       def getCurrent(self):
-               return self.l.getCurrentSelection()
-       
-       def getCurrentIndex(self):
-               return self.l.getCurrentSelectionIndex()
-       
-       GUI_WIDGET = eListbox
-       
-       def postWidgetCreate(self, instance):
-               instance.setContent(self.l)
-               instance.setItemHeight(30)
-               instance.selectionChanged.get().append(self.selectionChanged)
-
-       def selectionChanged(self):
-               for x in self.onSelectionChanged:
-                       x()
-       
-       def invalidateEntry(self, index):
-               self.l.invalidateEntry(index)
-       
-       def setIndex(self, index, data):
-               self.list[index] = data
-               self.invalidateEntry(index)
-
-       def setList(self, list):
-               self.list = list
-               self.l.setList(self.list)
-       
-       def setSelection(self, index):
-               if self.instance is not None:
-                       self.instance.moveSelectionTo(index)
-
-class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport):
+class CutListEditor(Screen, InfoBarBase, InfoBarSeek, InfoBarCueSheetSupport, HelpableScreen):
        skin = """
-               <screen position="100,100" size="550,400" title="Test" >
-                       <widget name="Timeline" position="10,0" size="530,40" 
-                               pointer="/usr/share/enigma2/position_pointer.png:3,5" />
-                       <widget name="Cutlist" position="10,50" size="530,300" scrollbarMode="showOnDemand" />
-               </screen>"""
+       <screen position="0,0" size="720,576" title="Cutlist editor" flags="wfNoBorder">
+               <eLabel text="Cutlist editor" position="65,60" size="300,25" font="Regular;20" />
+               <widget source="global.CurrentTime" render="Label" position="268,60" size="394,20" font="Regular;20" halign="right">
+                       <convert type="ClockToText">Format:%A %B %d, %H:%M</convert>
+               </widget>
+               <eLabel position="268,98" size="394,304" backgroundColor="#505555" />
+               <widget name="Video" position="270,100" zPosition="1" size="390,300" backgroundColor="transparent" />
+               <widget source="session.CurrentService" render="Label" position="135,405" size="450,50" font="Regular;22" halign="center" valign="center">
+                       <convert type="ServiceName">Name</convert>
+               </widget>
+               <widget source="session.CurrentService" render="Label" position="50,450" zPosition="1" size="620,25" font="Regular;20" halign="center" valign="center">
+                       <convert type="ServicePosition">Position,Detailed</convert>
+               </widget>
+               <eLabel position="62,98" size="179,274" backgroundColor="#505555" />
+               <eLabel position="64,100" size="175,270" backgroundColor="#000000" />
+               <widget source="cutlist" position="64,100" zPosition="1" size="175,270" scrollbarMode="showOnDemand" transparent="1" render="Listbox" >
+                       <convert type="TemplatedMultiContent">
+                               {"template": [
+                                               MultiContentEntryText(size=(125, 20), text = 1, backcolor = MultiContentTemplateColor(3)),
+                                               MultiContentEntryText(pos=(125,0), size=(50, 20), text = 2, flags = RT_HALIGN_RIGHT, backcolor = MultiContentTemplateColor(3))
+                                       ],
+                                "fonts": [gFont("Regular", 18)],
+                                "itemHeight": 20
+                               }
+                       </convert>
+               </widget>
+               <widget name="Timeline" position="50,485" size="615,20" backgroundColor="#505555" pointer="skin_default/position_arrow.png:3,5" foregroundColor="black" />
+               <ePixmap pixmap="skin_default/icons/mp_buttons.png" position="305,515" size="109,13" alphatest="on" />
+       </screen>"""
+
        def __init__(self, session, service):
                self.skin = CutListEditor.skin
                Screen.__init__(self, session)
-               InfoBarSeek.__init__(self)
+               InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions")
                InfoBarCueSheetSupport.__init__(self)
+               InfoBarBase.__init__(self, steal_current_service = True)
+               HelpableScreen.__init__(self)
                self.old_service = session.nav.getCurrentlyPlayingServiceReference()
                session.nav.playService(service)
-               
+
                service = session.nav.getCurrentService()
                cue = service and service.cueSheet()
                if cue is not None:
                        # disable cutlists. we want to freely browse around in the movie
                        print "cut lists disabled!"
                        cue.setCutListEnable(0)
-               
+
                self.downloadCuesheet()
-       
+
                self["Timeline"] = ServicePositionGauge(self.session.nav)
-               self["Cutlist"] = CutList(self.getCutlist())
-               self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
-               
+               self["cutlist"] = List(self.getCutlist())
+               self["cutlist"].onSelectionChanged.append(self.selectionChanged)
+
+               self["Video"] = VideoWindow(decoder = 0)
+
                self["actions"] = HelpableActionMap(self, "CutListEditorActions",
                        {
                                "setIn": (self.setIn, _("Make this mark an 'in' point")),
@@ -173,11 +172,11 @@ class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport):
                                "addMark": (self.__addMark, _("Add a mark")),
                                "removeMark": (self.__removeMark, _("Remove a mark")),
                                "leave": (self.exit, _("Exit editor")),
-                               "showMenu": self.showMenu,
+                               "showMenu": (self.showMenu, _("menu")),
                        }, prio=-4)
-               
+
                self.tutorial_seen = False
-               
+
                self.onExecBegin.append(self.showTutorial)
                self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
                        {
@@ -187,52 +186,49 @@ class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport):
                # to track new entries we save the last version of the cutlist
                self.last_cuts = [ ]
                self.cut_start = None
-               
+               self.onClose.append(self.__onClose)
+
+       def __onClose(self):
+               self.session.nav.playService(self.old_service, forceRestart=True)
+
        def showTutorial(self):
                if not self.tutorial_seen:
                        self.tutorial_seen = True
-                       self.session.open(MessageBox, 
-                               """Welcome to the Cutlist editor. It's still a bit strange to use, but anyway:
-
-Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
+                       self.session.open(MessageBox,_("Welcome to the Cutlist editor.\n\nSeek to the start of the stuff you want to cut away. Press OK, select 'start cut'.\n\nThen seek to the end, press OK, select 'end cut'. That's it."), MessageBox.TYPE_INFO)
 
-Then seek to the end, press OK, select 'end cut'. That's it.
-                               """, MessageBox.TYPE_INFO)
-       
        def checkSkipShowHideLock(self):
                pass
-       
+
        def setType(self, index, type):
                if len(self.cut_list):
                        self.cut_list[index] = (self.cut_list[index][0], type)
-                       self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
-       
+                       self["cutlist"].modifyEntry(index, CutListEntry(*self.cut_list[index]))
+
        def setIn(self):
-               m = self["Cutlist"].getCurrentIndex()
+               m = self["cutlist"].getIndex()
                self.setType(m, 0)
                self.uploadCuesheet()
-       
+
        def setOut(self):
-               m = self["Cutlist"].getCurrentIndex()
+               m = self["cutlist"].getIndex()
                self.setType(m, 1)
                self.uploadCuesheet()
 
        def setMark(self):
-               m = self["Cutlist"].getCurrentIndex()
+               m = self["cutlist"].getIndex()
                self.setType(m, 2)
                self.uploadCuesheet()
-       
+
        def __addMark(self):
                self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
-       
+
        def __removeMark(self):
-               m = self["Cutlist"].getCurrent()
+               m = self["cutlist"].getCurrent()
                m = m and m[0]
                if m is not None:
                        self.removeMark(m)
-       
+
        def exit(self):
-               self.session.nav.playService(self.old_service)
                self.close()
 
        def getCutlist(self):
@@ -242,7 +238,7 @@ Then seek to the end, press OK, select 'end cut'. That's it.
                return r
 
        def selectionChanged(self):
-               where = self["Cutlist"].getCurrent()
+               where = self["cutlist"].getCurrent()
                if where is None:
                        print "no selection"
                        return
@@ -256,20 +252,20 @@ Then seek to the end, press OK, select 'end cut'. That's it.
        def refillList(self):
                print "cue sheet changed, refilling"
                self.downloadCuesheet()
-               
+
                # get the first changed entry, and select it
                new_list = self.getCutlist()
-               self["Cutlist"].setList(new_list)
-               
+               self["cutlist"].list = new_list
+
                for i in range(min(len(new_list), len(self.last_cuts))):
                        if new_list[i] != self.last_cuts[i]:
-                               self["Cutlist"].setSelection(i)
+                               self["cutlist"].setIndex(i)
                                break
                self.last_cuts = new_list
 
        def getStateForPosition(self, pos):
                state = 0 # in
-               
+
                # when first point is "in", the beginning is "out"
                if len(self.cut_list) and self.cut_list[0][1] == 0:
                        state = 1
@@ -286,11 +282,11 @@ Then seek to the end, press OK, select 'end cut'. That's it.
                curpos = self.cueGetCurrentPosition()
                if curpos is None:
                        return
-               
+
                self.setSeekState(self.SEEK_STATE_PAUSE)
-               
+
                self.context_position = curpos
-               
+
                self.context_nearest_mark = self.toggleMark(onlyreturn=True)
 
                cur_state = self.getStateForPosition(curpos)
@@ -303,28 +299,27 @@ Then seek to the end, press OK, select 'end cut'. That's it.
                else:
                        print "currently in 'OUT'"
                        state = CutListContextMenu.SHOW_DELETECUT
-               
+
                if self.context_nearest_mark is None:
                        nearmark = False
                else:
                        nearmark = True
-               
+
                self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
-       
+
        def menuCallback(self, *result):
-               self.setSeekState(self.SEEK_STATE_PLAY)
                if not len(result):
                        return
                result = result[0]
-               
+
                if result == CutListContextMenu.RET_STARTCUT:
                        self.cut_start = self.context_position
                elif result == CutListContextMenu.RET_ENDCUT:
                        # remove in/out marks between the new cut
                        for (where, what) in self.cut_list[:]:
-                               if self.cut_start <= where <= self.context_position and what in [0,1]:
+                               if self.cut_start <= where <= self.context_position and what in (0,1):
                                        self.cut_list.remove((where, what))
-                       
+
                        bisect.insort(self.cut_list, (self.cut_start, 1))
                        bisect.insort(self.cut_list, (self.context_position, 0))
                        self.uploadCuesheet()
@@ -332,7 +327,7 @@ Then seek to the end, press OK, select 'end cut'. That's it.
                elif result == CutListContextMenu.RET_DELETECUT:
                        out_before = None
                        in_after = None
-                       
+
                        for (where, what) in self.cut_list:
                                if what == 1 and where < self.context_position: # out
                                        out_before = (where, what)
@@ -340,10 +335,10 @@ Then seek to the end, press OK, select 'end cut'. That's it.
                                        out_before = None
                                elif what == 0 and where > self.context_position and in_after is None:
                                        in_after = (where, what)
-                       
+
                        if out_before is not None:
                                self.cut_list.remove(out_before)
-                       
+
                        if in_after is not None:
                                self.cut_list.remove(in_after)
                        self.uploadCuesheet()
@@ -355,7 +350,7 @@ Then seek to the end, press OK, select 'end cut'. That's it.
                elif result == CutListContextMenu.RET_REMOVEBEFORE:
                        # remove in/out marks before current position
                        for (where, what) in self.cut_list[:]:
-                               if where <= self.context_position and what in [0,1]:
+                               if where <= self.context_position and what in (0,1):
                                        self.cut_list.remove((where, what))
                        # add 'in' point
                        bisect.insort(self.cut_list, (self.context_position, 0))
@@ -363,11 +358,29 @@ Then seek to the end, press OK, select 'end cut'. That's it.
                elif result == CutListContextMenu.RET_REMOVEAFTER:
                        # remove in/out marks after current position
                        for (where, what) in self.cut_list[:]:
-                               if where >= self.context_position and what in [0,1]:
+                               if where >= self.context_position and what in (0,1):
                                        self.cut_list.remove((where, what))
                        # add 'out' point
                        bisect.insort(self.cut_list, (self.context_position, 1))
                        self.uploadCuesheet()
+               elif result == CutListContextMenu.RET_GRABFRAME:
+                       self.grabFrame()
+
+       # we modify the "play" behavior a bit:
+       # if we press pause while being in slowmotion, we will pause (and not play)
+       def playpauseService(self):
+               if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
+                       self.unPauseService()
+               else:
+                       self.pauseService()
+
+       def grabFrame(self):
+               path = self.session.nav.getCurrentlyPlayingServiceReference().getPath()
+               from Components.Console import Console
+               grabConsole = Console()
+               cmd = 'grab -vblpr%d "%s"' % (180, path.rsplit('.',1)[0] + ".png")
+               grabConsole.ePopen(cmd)
+               self.playpauseService()
 
 def main(session, service, **kwargs):
        session.open(CutListEditor, service)