2 # Generic Screen to select a path/filename combination
6 from Screens.Screen import Screen
7 from Screens.MessageBox import MessageBox
8 from Screens.InputBox import InputBox
9 from Screens.HelpMenu import HelpableScreen
10 from Screens.ChoiceBox import ChoiceBox
13 from Tools.BoundFunction import boundFunction
14 from Tools.Directories import *
15 from Components.config import config, configfile, ConfigSubList, ConfigSubsection, \
16 ConfigText, ConfigNumber, ConfigBoolean
20 from Tools.NumericalTextInput import NumericalTextInput
23 from Components.ActionMap import NumberActionMap, HelpableActionMap
24 from Components.Label import Label
25 from Components.Pixmap import Pixmap
26 from Components.Button import Button
27 from Components.FileList import FileList
28 from Components.MenuList import MenuList
31 from enigma import eTimer
33 class LocationBox(Screen, NumericalTextInput, HelpableScreen):
34 """Simple Class similar to MessageBox / ChoiceBox but used to choose a folder/pathname combination"""
36 skin = """<screen name="LocationBox" position="100,75" size="540,460" >
37 <widget name="text" position="0,2" size="540,22" font="Regular;22" />
38 <widget name="target" position="0,23" size="540,22" valign="center" font="Regular;22" />
39 <widget name="filelist" position="0,55" zPosition="1" size="540,210" scrollbarMode="showOnDemand" selectionDisabled="1" />
40 <widget name="textbook" position="0,272" size="540,22" font="Regular;22" />
41 <widget name="booklist" position="5,302" zPosition="2" size="535,100" scrollbarMode="showOnDemand" />
42 <widget name="red" position="0,415" zPosition="1" size="135,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
43 <widget name="key_red" position="0,415" zPosition="2" size="135,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
44 <widget name="green" position="135,415" zPosition="1" size="135,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
45 <widget name="key_green" position="135,415" zPosition="2" size="135,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
46 <widget name="yellow" position="270,415" zPosition="1" size="135,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
47 <widget name="key_yellow" position="270,415" zPosition="2" size="135,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
48 <widget name="blue" position="405,415" zPosition="1" size="135,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
49 <widget name="key_blue" position="405,415" zPosition="2" size="135,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
52 def __init__(self, session, text = "", filename = "", currDir = None, bookmarks = None, userMode = False, windowTitle = _("Select Location"), minFree = None, autoAdd = False, editDir = False, inhibitDirs = [], inhibitMounts = []):
54 Screen.__init__(self, session)
55 NumericalTextInput.__init__(self, handleTimeout = False)
56 HelpableScreen.__init__(self)
59 self.setUseableChars(u'1234567890abcdefghijklmnopqrstuvwxyz')
62 self.qs_timer = eTimer()
63 self.qs_timer.callback.append(self.timeout)
64 self.qs_timer_type = 0
66 # Initialize Quickselect
71 self["text"] = Label(text)
72 self["textbook"] = Label(_("Bookmarks"))
74 # Save parameters locally
76 self.filename = filename
77 self.minFree = minFree
78 self.realBookmarks = bookmarks
79 self.bookmarks = bookmarks and bookmarks.value[:] or []
80 self.userMode = userMode
81 self.autoAdd = autoAdd
82 self.editDir = editDir
83 self.inhibitDirs = inhibitDirs
86 self["filelist"] = FileList(currDir, showDirectories = True, showFiles = False, inhibitMounts = inhibitMounts, inhibitDirs = inhibitDirs)
89 self["booklist"] = MenuList(self.bookmarks)
92 self["key_green"] = Button(_("OK"))
93 self["key_yellow"] = Button(_("Rename"))
94 self["key_blue"] = Button(_("Remove Bookmark"))
95 self["key_red"] = Button(_("Cancel"))
97 # Background for Buttons
98 self["green"] = Pixmap()
99 self["yellow"] = Pixmap()
100 self["blue"] = Pixmap()
101 self["red"] = Pixmap()
104 self["target"] = Label()
109 # Custom Action Handler
110 class LocationBoxActionMap(HelpableActionMap):
111 def __init__(self, parent, context, actions = { }, prio=0):
112 HelpableActionMap.__init__(self, parent, context, actions, prio)
115 def action(self, contexts, action):
117 self.box.timeout(force = True)
119 return HelpableActionMap.action(self, contexts, action)
121 # Actions that will reset quickselect
122 self["WizardActions"] = LocationBoxActionMap(self, "WizardActions",
128 "ok": (self.ok, _("select")),
129 "back": (self.cancel, _("cancel")),
132 self["ColorActions"] = LocationBoxActionMap(self, "ColorActions",
135 "green": self.select,
136 "yellow": self.changeName,
137 "blue": self.addRemoveBookmark,
140 self["EPGSelectActions"] = LocationBoxActionMap(self, "EPGSelectActions",
142 "prevBouquet": (self.switchToBookList, _("switch to bookmarks")),
143 "nextBouquet": (self.switchToFileList, _("switch to filelist")),
146 self["MenuActions"] = LocationBoxActionMap(self, "MenuActions",
148 "menu": (self.showMenu, _("menu")),
151 # Actions used by quickselect
152 self["NumberActions"] = NumberActionMap(["NumberActions"],
154 "1": self.keyNumberGlobal,
155 "2": self.keyNumberGlobal,
156 "3": self.keyNumberGlobal,
157 "4": self.keyNumberGlobal,
158 "5": self.keyNumberGlobal,
159 "6": self.keyNumberGlobal,
160 "7": self.keyNumberGlobal,
161 "8": self.keyNumberGlobal,
162 "9": self.keyNumberGlobal,
163 "0": self.keyNumberGlobal
166 # Run some functions when shown
167 self.onShown.extend([
168 boundFunction(self.setTitle, windowTitle),
173 self.onLayoutFinish.append(self.switchToFileListOnStart)
175 # Make sure we remove our callback
176 self.onClose.append(self.disableTimer)
178 def switchToFileListOnStart(self):
179 if self.realBookmarks and self.realBookmarks.value:
180 self.currList = "booklist"
181 currDir = self["filelist"].current_directory
182 if currDir in self.bookmarks:
183 self["booklist"].moveToIndex(self.bookmarks.index(currDir))
185 self.switchToFileList()
187 def disableTimer(self):
188 self.qs_timer.callback.remove(self.timeout)
190 def showHideRename(self):
191 # Don't allow renaming when filename is empty
192 if self.filename == "":
193 self["key_yellow"].hide()
195 def switchToFileList(self):
196 if not self.userMode:
197 self.currList = "filelist"
198 self["filelist"].selectionEnabled(1)
199 self["booklist"].selectionEnabled(0)
200 self["key_blue"].text = _("Add Bookmark")
203 def switchToBookList(self):
204 self.currList = "booklist"
205 self["filelist"].selectionEnabled(0)
206 self["booklist"].selectionEnabled(1)
207 self["key_blue"].text = _("Remove Bookmark")
210 def addRemoveBookmark(self):
211 if self.currList == "filelist":
213 folder = self["filelist"].getSelection()[0]
214 if folder is not None and not folder in self.bookmarks:
215 self.bookmarks.append(folder)
216 self.bookmarks.sort()
217 self["booklist"].setList(self.bookmarks)
220 if not self.userMode:
221 name = self["booklist"].getCurrent()
222 self.session.openWithCallback(
223 boundFunction(self.removeBookmark, name),
225 _("Do you really want to remove your bookmark of %s?") % (name),
228 def removeBookmark(self, name, ret):
231 if name in self.bookmarks:
232 self.bookmarks.remove(name)
233 self["booklist"].setList(self.bookmarks)
236 if self["filelist"].current_directory != None:
237 self.session.openWithCallback(
238 self.createDirCallback,
240 title = _("Please enter name of the new directory"),
244 def createDirCallback(self, res):
245 if res is not None and len(res):
246 path = os.path.join(self["filelist"].current_directory, res)
247 if not pathExists(path):
248 if not createDir(path):
251 _("Creating directory %s failed.") % (path),
252 type = MessageBox.TYPE_ERROR,
255 self["filelist"].refresh()
259 _("The path %s already exists.") % (path),
260 type = MessageBox.TYPE_ERROR,
265 sel = self["filelist"].getSelection()
266 if sel and pathExists(sel[0]):
267 self.session.openWithCallback(
268 boundFunction(self.removeDirCallback, sel[0]),
270 _("Do you really want to remove directory %s from the disk?") % (sel[0]),
271 type = MessageBox.TYPE_YESNO
276 _("Invalid directory selected: %s") % (sel[0]),
277 type = MessageBox.TYPE_ERROR,
281 def removeDirCallback(self, name, res):
283 if not removeDir(name):
286 _("Removing directory %s failed. (Maybe not empty.)") % (name),
287 type = MessageBox.TYPE_ERROR,
291 self["filelist"].refresh()
292 self.removeBookmark(name, True)
295 self[self.currList].up()
299 self[self.currList].down()
303 self[self.currList].pageUp()
307 self[self.currList].pageDown()
311 if self.currList == "filelist":
312 if self["filelist"].canDescent():
313 self["filelist"].descent()
321 def getPreferredFolder(self):
322 if self.currList == "filelist":
323 # XXX: We might want to change this for parent folder...
324 return self["filelist"].getSelection()[0]
326 return self["booklist"].getCurrent()
328 def selectConfirmed(self, ret):
330 ret = ''.join([self.getPreferredFolder(), self.filename])
331 if self.realBookmarks:
332 if self.autoAdd and not ret in self.bookmarks:
333 self.bookmarks.append(self.getPreferredFolder())
334 self.bookmarks.sort()
336 if self.bookmarks != self.realBookmarks.value:
337 self.realBookmarks.value = self.bookmarks
338 self.realBookmarks.save()
342 currentFolder = self.getPreferredFolder()
343 # Do nothing unless current Directory is valid
344 if currentFolder is not None:
345 # Check if we need to have a minimum of free Space available
346 if self.minFree is not None:
347 # Try to read fs stats
349 s = os.statvfs(currentFolder)
350 if (s.f_bavail * s.f_bsize) / 1000000 > self.minFree:
351 # Automatically confirm if we have enough free disk Space available
352 return self.selectConfirmed(True)
356 # Ask User if he really wants to select this folder
357 self.session.openWithCallback(
358 self.selectConfirmed,
360 _("There might not be enough Space on the selected Partition.\nDo you really want to continue?"),
361 type = MessageBox.TYPE_YESNO
363 # No minimum free Space means we can safely close
365 self.selectConfirmed(True)
367 def changeName(self):
368 if self.filename != "":
369 # TODO: Add Information that changing extension is bad? disallow?
370 self.session.openWithCallback(
373 title = _("Please enter a new filename"),
377 def nameChanged(self, res):
385 _("An empty filename is illegal."),
386 type = MessageBox.TYPE_ERROR,
390 def updateTarget(self):
391 # Write Combination of Folder & Filename when Folder is valid
392 currFolder = self.getPreferredFolder()
393 if currFolder is not None:
394 self["target"].setText(''.join([currFolder, self.filename]))
395 # Display a Warning otherwise
397 self["target"].setText(_("Invalid Location"))
400 if not self.userMode and self.realBookmarks:
402 if self.currList == "filelist":
403 menu.append((_("switch to bookmarks"), self.switchToBookList))
404 menu.append((_("add bookmark"), self.addRemoveBookmark))
406 menu.append((_("create directory"), self.createDir))
407 menu.append((_("remove directory"), self.removeDir))
409 menu.append((_("switch to filelist"), self.switchToFileList))
410 menu.append((_("remove bookmark"), self.addRemoveBookmark))
412 self.session.openWithCallback(
419 def menuCallback(self, choice):
423 def usermodeOn(self):
424 self.switchToBookList()
425 self["filelist"].hide()
426 self["key_blue"].hide()
428 def keyNumberGlobal(self, number):
432 # See if another key was pressed before
433 if number != self.lastKey:
434 # Reset lastKey again so NumericalTextInput triggers its keychange
437 # Try to select what was typed
443 # Get char and append to text
444 char = self.getKey(number)
445 self.quickselect = self.quickselect[:self.curr_pos] + unicode(char)
448 self.qs_timer_type = 0
449 self.qs_timer.start(1000, 1)
451 def selectByStart(self):
452 # Don't do anything on initial call
453 if not len(self.quickselect):
456 # Don't select if no dir
457 if self["filelist"].getCurrentDirectory():
458 # TODO: implement proper method in Components.FileList
459 files = self["filelist"].getFileList()
464 # We select by filename which is absolute
465 lookfor = self["filelist"].getCurrentDirectory() + self.quickselect
467 # Select file starting with generated text
469 if file[0][0] and file[0][0].lower().startswith(lookfor):
470 self["filelist"].instance.moveSelectionTo(idx)
474 def timeout(self, force = False):
476 if not force and self.qs_timer_type == 0:
477 # Try to select what was typed
484 self.qs_timer_type = 1
486 # Start timeout again
487 self.qs_timer.start(1000, 1)
488 # Timeout Quickselect
490 # Eventually stop Timer
496 self.quickselect = ""
499 return str(type(self)) + "(" + self.text + ")"
501 class MovieLocationBox(LocationBox):
502 def __init__(self, session, text, dir, minFree = None):
503 inhibitDirs = ["/bin", "/boot", "/dev", "/etc", "/lib", "/proc", "/sbin", "/sys", "/usr", "/var"]
504 LocationBox.__init__(self, session, text = text, currDir = dir, bookmarks = config.movielist.videodirs, autoAdd = True, editDir = True, inhibitDirs = inhibitDirs, minFree = minFree)