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, res):
330 ret = ''.join([self.getPreferredFolder(), self.filename])
331 if self.realBookmarks and self.autoAdd and not ret in self.bookmarks:
332 self.bookmarks.append(self.getPreferredFolder())
333 self.bookmarks.sort()
337 currentFolder = self.getPreferredFolder()
338 # Do nothing unless current Directory is valid
339 if currentFolder is not None:
340 # Check if we need to have a minimum of free Space available
341 if self.minFree is not None:
342 # Try to read fs stats
344 s = os.statvfs(currentFolder)
345 if (s.f_bavail * s.f_bsize) / 1000000 > self.minFree:
346 # Automatically confirm if we have enough free disk Space available
347 return self.selectConfirmed(True)
351 # Ask User if he really wants to select this folder
352 self.session.openWithCallback(
353 self.selectConfirmed,
355 _("There might not be enough Space on the selected Partition.\nDo you really want to continue?"),
356 type = MessageBox.TYPE_YESNO
358 # No minimum free Space means we can safely close
360 self.selectConfirmed(True)
362 def close(self, ret):
363 if ret and self.realBookmarks and self.bookmarks != self.realBookmarks.value:
364 self.realBookmarks.value = self.bookmarks
365 self.realBookmarks.save()
366 Screen.close(self, ret)
368 def changeName(self):
369 if self.filename != "":
370 # TODO: Add Information that changing extension is bad? disallow?
371 self.session.openWithCallback(
374 title = _("Please enter a new filename"),
378 def nameChanged(self, res):
386 _("An empty filename is illegal."),
387 type = MessageBox.TYPE_ERROR,
391 def updateTarget(self):
392 # Write Combination of Folder & Filename when Folder is valid
393 currFolder = self.getPreferredFolder()
394 if currFolder is not None:
395 self["target"].setText(''.join([currFolder, self.filename]))
396 # Display a Warning otherwise
398 self["target"].setText(_("Invalid Location"))
401 if not self.userMode and self.realBookmarks:
403 if self.currList == "filelist":
404 menu.append((_("switch to bookmarks"), self.switchToBookList))
405 menu.append((_("add bookmark"), self.AddRemoveBookmark))
407 menu.append((_("create directory"), self.createDir))
408 menu.append((_("remove directory"), self.removeDir))
410 menu.append((_("switch to filelist"), self.switchToFileList))
411 menu.append((_("remove bookmark"), self.AddRemoveBookmark))
413 self.session.openWithCallback(
420 def menuCallback(self, choice):
424 def usermodeOn(self):
425 self.switchToBookList()
426 self["filelist"].hide()
427 self["key_blue"].hide()
429 def keyNumberGlobal(self, number):
433 # See if another key was pressed before
434 if number != self.lastKey:
435 # Reset lastKey again so NumericalTextInput triggers its keychange
438 # Try to select what was typed
444 # Get char and append to text
445 char = self.getKey(number)
446 self.quickselect = self.quickselect[:self.curr_pos] + unicode(char)
449 self.qs_timer_type = 0
450 self.qs_timer.start(1000, 1)
452 def selectByStart(self):
453 # Don't do anything on initial call
454 if not len(self.quickselect):
457 # Don't select if no dir
458 if self["filelist"].getCurrentDirectory():
459 # TODO: implement proper method in Components.FileList
460 files = self["filelist"].getFileList()
465 # We select by filename which is absolute
466 lookfor = self["filelist"].getCurrentDirectory() + self.quickselect
468 # Select file starting with generated text
470 if file[0][0] and file[0][0].lower().startswith(lookfor):
471 self["filelist"].instance.moveSelectionTo(idx)
475 def timeout(self, force = False):
477 if not force and self.qs_timer_type == 0:
478 # Try to select what was typed
485 self.qs_timer_type = 1
487 # Start timeout again
488 self.qs_timer.start(1000, 1)
489 # Timeout Quickselect
491 # Eventually stop Timer
497 self.quickselect = ""
500 return str(type(self)) + "(" + self.text + ")"
502 class MovieLocationBox(LocationBox):
503 def __init__(self, session, text, dir, minFree = None):
505 if config.usage.setup_level.index < 2: # -expert
506 inhibitMounts.append("/")
507 LocationBox.__init__(self, session, text = text, currDir = dir, bookmarks = config.movielist.videodirs, autoAdd = True, editDir = True, inhibitMounts = inhibitMounts, minFree = minFree)