timer_select.patch by Moritz Venn, with minor fixes
authorFelix Domke <tmbinc@elitedvb.net>
Wed, 20 Feb 2008 00:36:00 +0000 (00:36 +0000)
committerFelix Domke <tmbinc@elitedvb.net>
Wed, 20 Feb 2008 00:36:00 +0000 (00:36 +0000)
data/skin_default.xml
lib/python/Screens/LocationBox.py [new file with mode: 0644]
lib/python/Screens/Makefile.am
lib/python/Screens/TimerEntry.py
lib/python/Tools/Directories.py

index 0288337ebd049f0cea2541ad58c0c413886f61ae..d0eabca083a5b3eeadd8dbd42b61d5add175ceba 100644 (file)
@@ -769,9 +769,11 @@ self.instance.move(ePoint(orgpos.x() + (orgwidth - newwidth)/2, orgpos.y()))
        <screen name="TimerEntry" position="90,95" size="560,430" title="Timer entry">
                <widget name="config" position="10,10" size="540,350" scrollbarMode="showOnDemand" />
                <widget name="cancel" pixmap="skin_default/key-red.png" position="10,380" zPosition="1" size="140,40" transparent="1" alphatest="on" />
+               <widget name="location" pixmap="skin_default/key-yellow.png" position="260,380" zPosition="1" size="140,40" transparent="1" alphatest="on" />
                <widget name="ok" pixmap="skin_default/key-green.png" position="410,380" zPosition="1" size="140,40" transparent="1" alphatest="on" />
-               <widget name="canceltext" position="10,380" zPosition="2" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" />
-               <widget name="oktext" position="410,380" zPosition="2" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" />
+               <widget name="canceltext" position="10,380" zPosition="2" size="140,40" valign="center" halign="center" font="Regular;21" backgroundColor="#9f1313" transparent="1" />
+               <widget name="locationtext" position="260,380" zPosition="2" size="140,40" valign="center" halign="center" font="Regular;21" backgroundColor="#a08500" transparent="1" />
+               <widget name="oktext" position="410,380" zPosition="2" size="140,40" valign="center" halign="center" font="Regular;21" backgroundColor="#1f771f" transparent="1" />
        </screen>
        <!-- Timer log -->
        <screen name="TimerLog" position="70,100" size="560,400" title="Timer log">
diff --git a/lib/python/Screens/LocationBox.py b/lib/python/Screens/LocationBox.py
new file mode 100644 (file)
index 0000000..b15fece
--- /dev/null
@@ -0,0 +1,292 @@
+#
+# Generic Screen to select a path/filename combination
+#
+
+# GUI (Screens)
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Screens.InputBox import InputBox
+
+# Generic
+from Tools.BoundFunction import boundFunction
+
+# Quickselect
+from Tools.NumericalTextInput import NumericalTextInput
+
+# GUI (Components)
+from Components.ActionMap import NumberActionMap
+from Components.Label import Label
+from Components.Pixmap import Pixmap
+from Components.Button import Button
+from Components.FileList import FileList
+
+# Timer
+from enigma import eTimer
+
+class LocationBox(Screen, NumericalTextInput):
+       """Simple Class similar to MessageBox / ChoiceBox but used to choose a folder/pathname combination"""
+
+       skin = """<screen name="LocationBox" position="100,130" size="540,340" >
+                       <widget name="text" position="0,2" size="540,22" font="Regular;22" />
+                       <widget name="filelist" position="0,25" size="540,235" />
+                       <widget name="target" position="0,260" size="540,40" valign="center" font="Regular;22" />
+                       <widget name="yellow" position="260,300" zPosition="1" size="140,40" pixmap="skin_default/key-yellow.png" transparent="1" alphatest="on" />
+                       <widget name="key_yellow" position="260,300" zPosition="2" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
+                       <widget name="green" position="400,300" zPosition="1" size="140,40" pixmap="skin_default/key-green.png" transparent="1" alphatest="on" />
+                       <widget name="key_green" position="400,300" zPosition="2" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
+               </screen>"""
+
+       def __init__(self, session, text = "", filename = "", currDir = None, windowTitle = _("Select Location"), minFree = None):
+               # Init parents
+               Screen.__init__(self, session)
+               NumericalTextInput.__init__(self, handleTimeout = False)
+
+               # Set useable chars
+               self.setUseableChars(u'1234567890abcdefghijklmnopqrstuvwxyz')
+
+               # Quickselect Timer
+               self.qs_timer = eTimer()
+               self.qs_timer.callback.append(self.timeout)
+               self.qs_timer_type = 0
+
+               # Initialize Quickselect
+               self.curr_pos = -1
+               self.quickselect = ""
+
+               # Set Text
+               self["text"] = Label(text)
+
+               # Save parameters locally
+               self.text = text
+               self.filename = filename
+               self.minFree = minFree
+
+               # Initialize FileList
+               self["filelist"] = FileList(currDir, showDirectories = True, showFiles = False)
+
+               # Buttons
+               self["key_green"] = Button(_("Confirm"))
+               self["key_yellow"] = Button(_("Rename"))
+
+               # Background for Buttons
+               self["green"] = Pixmap()
+               self["yellow"] = Pixmap()
+
+               # Initialize Target
+               self["target"] = Label()
+
+               # Custom Action Handler
+               class LocationBoxActionMap(NumberActionMap):
+                       def __init__(self, box, contexts = [ ], actions = { }, prio=0):                                                                                                                                                                                                                                    
+                               NumberActionMap.__init__(self, contexts, actions, prio)
+                               self.box = box
+
+                       def action(self, contexts, action):
+                               # Reset Quickselect
+                               self.box.timeout(force = True)
+
+                               return NumberActionMap.action(self, contexts, action)
+
+               # Actions that will reset quickselect
+               self["actions"] = LocationBoxActionMap(self, ["WizardActions", "ColorActions"],
+               {
+                       "ok": self.ok,
+                       "back": self.cancel,
+                       "green": self.select,
+                       "yellow": self.changeName,
+                       "left": self.left,
+                       "right": self.right,
+                       "up": self.up,
+                       "down": self.down,
+               }, -2)
+
+               # Actions used by quickselect
+               self["NumberActions"] = NumberActionMap(["NumberActions"],
+               {
+                       "1": self.keyNumberGlobal,
+                       "2": self.keyNumberGlobal,
+                       "3": self.keyNumberGlobal,
+                       "4": self.keyNumberGlobal,
+                       "5": self.keyNumberGlobal,
+                       "6": self.keyNumberGlobal,
+                       "7": self.keyNumberGlobal,
+                       "8": self.keyNumberGlobal,
+                       "9": self.keyNumberGlobal,
+                       "0": self.keyNumberGlobal
+               })
+
+               # Run some functions when shown
+               self.onShown.extend([
+                       boundFunction(self.setTitle, windowTitle),
+                       self.updateTarget,
+                       self.showHideRename
+               ])
+               # Make sure we remove our callback
+               self.onClose.append(self.disableTimer)
+
+       def disableTimer(self):
+               self.qs_timer.callback.remove(self.timeout)
+
+       def showHideRename(self):
+               # Don't allow renaming when filename is empty
+               if self.filename == "":
+                       self["yellow"].hide()
+                       self["key_yellow"].hide()
+
+       def up(self):
+               self["filelist"].up()
+
+       def down(self):
+               self["filelist"].down()
+
+       def left(self):
+               self["filelist"].pageUp()
+
+       def right(self):
+               self["filelist"].pageDown()
+
+       def ok(self):
+               if self["filelist"].canDescent():
+                       self["filelist"].descent()
+                       self.updateTarget()
+
+       def cancel(self):
+               self.close(None)
+
+       def selectConfirmed(self, res):
+               if res: 
+                       self.close(''.join([self["filelist"].getCurrentDirectory(), self.filename]))
+
+       def select(self):
+               # Do nothing unless current Directory is valid
+               if self["filelist"].getCurrentDirectory() is not None:
+                       # Check if we need to have a minimum of free Space available
+                       if self.minFree is not None:
+                               # Try to read fs stats
+                               try:
+                                       from os import statvfs
+
+                                       s = statvfs(self["filelist"].getCurrentDirectory())
+                                       if (s.f_bavail * s.f_bsize) / 1000000 > self.minFree:
+                                               # Automatically confirm if we have enough free disk Space available
+                                               return self.selectConfirmed(True)
+                               except OSError:
+                                       pass
+
+                               # Ask User if he really wants to select this folder
+                               self.session.openWithCallback(
+                                       self.selectConfirmed,
+                                       MessageBox,
+                                       _("There might not be enough Space on the selected Partition.\nDo you really want to continue?"),
+                                       type = MessageBox.TYPE_YESNO
+                               )
+                       # No minimum free Space means we can safely close
+                       else:   
+                               self.selectConfirmed(True)
+
+       def changeName(self):
+               if self.filename != "":
+                       # TODO: Add Information that changing extension is bad? disallow?
+                       # TODO: decide if using an inputbox is ok - we could also keep this in here
+                       self.session.openWithCallback(
+                               self.nameChanged,
+                               InputBox,
+                               title = _("Please enter a new filename"),
+                               text = self.filename
+                       )
+
+       def nameChanged(self, res):
+               if res is not None:
+                       if len(res):
+                               self.filename = res
+                               self.updateTarget()
+                       else:
+                               self.session.open(
+                                       MessageBox,
+                                       _("An empty filename is illegal."),
+                                       type = MessageBox.TYPE_ERROR,
+                                       timeout = 5
+                               )
+
+       def updateTarget(self):
+               # Write Combination of Folder & Filename when Folder is valid
+               if self["filelist"].getCurrentDirectory() is not None:
+                       self["target"].setText(''.join([self["filelist"].getCurrentDirectory(), self.filename]))
+               # Warning else
+               else:
+                       self["target"].setText(_("Invalid Location"))
+
+       def keyNumberGlobal(self, number):
+               # Cancel Timeout
+               self.qs_timer.stop()
+
+               # See if another key was pressed before
+               if number != self.lastKey:
+                       # Reset lastKey again so NumericalTextInput triggers its keychange
+                       self.nextKey()
+
+                       # Try to select what was typed
+                       self.selectByStart()
+
+                       # Increment position
+                       self.curr_pos += 1
+
+               # Get char and append to text
+               char = self.getKey(number)
+               self.quickselect = self.quickselect[:self.curr_pos] + unicode(char)
+
+               # Start Timeout
+               self.qs_timer_type = 0
+               self.qs_timer.start(1000, 1)
+
+       def selectByStart(self):
+               # Don't do anything on initial call
+               if not len(self.quickselect):
+                       return
+
+               # Don't select if no dir
+               if self["filelist"].getCurrentDirectory():
+                       # TODO: implement proper method in Components.FileList
+                       files = self["filelist"].getFileList()
+
+                       # Initialize index
+                       idx = 0
+
+                       # We select by filename which is absolute
+                       lookfor = self["filelist"].getCurrentDirectory() + self.quickselect
+
+                       # Select file starting with generated text
+                       for file in files:
+                               if file[0][0] and file[0][0].lower().startswith(lookfor):
+                                       self["filelist"].instance.moveSelectionTo(idx)
+                                       break
+                               idx += 1
+
+       def timeout(self, force = False):
+               # Timeout Key
+               if not force and self.qs_timer_type == 0:
+                       # Try to select what was typed
+                       self.selectByStart()
+
+                       # Reset Key
+                       self.lastKey = -1
+
+                       # Change type
+                       self.qs_timer_type = 1
+
+                       # Start timeout again
+                       self.qs_timer.start(1000, 1)
+               # Timeout Quickselect
+               else:
+                       # Eventually stop Timer
+                       self.qs_timer.stop()
+
+                       # Invalidate
+                       self.lastKey = -1
+                       self.curr_pos = -1
+                       self.quickselect = ""
+
+       def __repr__(self):
+               return str(type(self)) + "(" + self.text + ")"
+
index aeff31513936ccd710e61826b8ed364e3b5d40df..068ce4427fb799ccdc57681c43625fe440ffb6b4 100644 (file)
@@ -12,4 +12,4 @@ install_PYTHON = \
        Console.py InputBox.py ChoiceBox.py SimpleSummary.py ImageWizard.py \
        TimerSelection.py PictureInPicture.py TimeDateInput.py \
        SubtitleDisplay.py SubservicesQuickzap.py ParentalControlSetup.py NumericalTextInputHelpDialog.py \
-       SleepTimerEdit.py Ipkg.py RdsDisplay.py Globals.py SessionGlobals.py
+       SleepTimerEdit.py Ipkg.py RdsDisplay.py Globals.py SessionGlobals.py LocationBox.py
index 3541d36d07fa3196cc1dae08f29bc9890a519f50..d11c5c2a766228ce855d851c5bc1269c76f6af87 100644 (file)
@@ -1,7 +1,8 @@
 from Screen import Screen
+from LocationBox import LocationBox
 import ChannelSelection
 from ServiceReference import ServiceReference
-from Components.config import ConfigSelection, ConfigText, ConfigSubList, ConfigDateTime, ConfigClock, ConfigYesNo, getConfigListEntry
+from Components.config import config, ConfigSelection, ConfigText, ConfigSubList, ConfigDateTime, ConfigClock, ConfigYesNo, getConfigListEntry
 from Components.ActionMap import NumberActionMap
 from Components.ConfigList import ConfigListScreen
 from Components.MenuList import MenuList
@@ -25,22 +26,32 @@ class TimerEntry(Screen, ConfigListScreen):
                
                self["oktext"] = Label(_("OK"))
                self["canceltext"] = Label(_("Cancel"))
+               self["locationtext"] = Label(_("Choose Location"))
                self["ok"] = Pixmap()
                self["cancel"] = Pixmap()
+               self["location"] = Pixmap()
 
                self.createConfig()
 
-               self["actions"] = NumberActionMap(["SetupActions"],
+               self["actions"] = NumberActionMap(["SetupActions", "ColorActions"],
                {
                        "ok": self.keySelect,
                        "save": self.keyGo,
                        "cancel": self.keyCancel,
+                       "yellow": self.selectPath,
                }, -2)
 
                self.list = []
                ConfigListScreen.__init__(self, self.list, session = session)
                self.createSetup("config")
 
+               self.onLayoutFinish.append(self.handleLocation)
+
+       def handleLocation(self):
+               if config.usage.setup_level.index < 2: # -expert
+                       self["locationtext"].hide()
+                       self["location"].hide()
+
        def createConfig(self):
                        justplay = self.timer.justplay
                                
@@ -95,6 +106,8 @@ class TimerEntry(Screen, ConfigListScreen):
                        self.timerentry_enddate = ConfigDateTime(default = self.timer.end, formatstring =  _("%d.%B %Y"), increment = 86400)
                        self.timerentry_endtime = ConfigClock(default = self.timer.end)
 
+                       self.timerentry_dirname = ConfigSelection(choices = [self.timer.dirname or "/hdd/movie/"])
+
                        self.timerentry_repeatedbegindate = ConfigDateTime(default = self.timer.repeatedbegindate, formatstring = _("%d.%B %Y"), increment = 86400)
 
                        self.timerentry_weekday = ConfigSelection(default = weekday_table[weekday], choices = [("mon",_("Monday")), ("tue", _("Tuesday")), ("wed",_("Wednesday")), ("thu", _("Thursday")), ("fri", _("Friday")), ("sat", _("Saturday")), ("sun", _("Sunday"))])
@@ -177,6 +190,8 @@ class TimerEntry(Screen, ConfigListScreen):
                                self.list.append(getConfigListEntry(_("EndTime"), self.timerentry_endtime))
 
                if self.timerentry_justplay.value != "zap":
+                       if config.usage.setup_level.index >= 2: # expert+
+                               self.list.append(getConfigListEntry(_("Location"), self.timerentry_dirname))
                        self.list.append(getConfigListEntry(_("After event"), self.timerentry_afterevent))
 
                self.channelEntry = getConfigListEntry(_("Channel"), self.timerentry_service)
@@ -255,6 +270,12 @@ class TimerEntry(Screen, ConfigListScreen):
                self.timer.afterEvent = {"nothing": AFTEREVENT.NONE, "deepstandby": AFTEREVENT.DEEPSTANDBY, "standby": AFTEREVENT.STANDBY}[self.timerentry_afterevent.value]
                self.timer.service_ref = self.timerentry_service_ref
 
+               # TODO: fix that thing with none (this might as well just be ignored)
+               if self.timerentry_dirname.value == "/hdd/movie/":
+                       self.timer.dirname = None
+               else:
+                       self.timer.dirname = self.timerentry_dirname.value
+
                if self.timerentry_type.value == "once":
                        self.timer.begin, self.timer.end = self.getBeginEnd()
                if self.timerentry_type.value == "repeated":
@@ -313,7 +334,25 @@ class TimerEntry(Screen, ConfigListScreen):
 
        def keyCancel(self):
                self.close((False,))
-               
+
+       def selectPath(self):
+               if config.usage.setup_level.index < 2: #-expert
+                       return
+               self.session.openWithCallback(
+                       self.pathSelected,
+                       LocationBox,
+                       text = _("Choose target folder"),
+                       filename = "",
+                       currDir = None, # TODO: fix FileList to correctly determine mountpoint
+                       minFree = 100
+               )
+
+       def pathSelected(self, res):
+               if res is not None:
+                       self.timerentry_dirname.choices.append(res)
+                       self.timerentry_dirname.description[res] = res
+                       self.timerentry_dirname.value = res
+
 class TimerLog(Screen):
        def __init__(self, session, timer):
                Screen.__init__(self, session)
index 975d3ade64f1f746a8c9fc976b42771f4d14d2ea..0096f3f4426d90f29eb12c78dcc00571967fddbd 100644 (file)
@@ -114,8 +114,8 @@ def fileExists(f):
                exists = 1
        return exists
 
-def getRecordingFilename(basename):
-               # filter out non-allowed characters
+def getRecordingFilename(basename, dirname = None):
+       # filter out non-allowed characters
        non_allowed_characters = "/.\\:*?<>|\""
        filename = ""
        
@@ -125,7 +125,10 @@ def getRecordingFilename(basename):
                if c in non_allowed_characters:
                        c = "_"
                filename += c
-       
+
+       if dirname is not None:
+               filename = ''.join([dirname, filename])
+
        i = 0
        while True:
                path = resolveFilename(SCOPE_HDD, filename)