c435eb5f1d963fee21da31a01f74e4372762adbf
[openblackhole/openblackhole-enigma2.git] / lib / python / Screens / MovieSelection.py
1 from Screen import Screen
2 from Components.Button import Button
3 from Components.ActionMap import HelpableActionMap, ActionMap, NumberActionMap
4 from Components.ChoiceList import ChoiceList, ChoiceEntryComponent
5 from Components.MenuList import MenuList
6 from Components.MovieList import MovieList, resetMoviePlayState, AUDIO_EXTENSIONS, DVD_EXTENSIONS, IMAGE_EXTENSIONS
7 from Components.DiskInfo import DiskInfo
8 from Components.Pixmap import Pixmap, MultiPixmap
9 from Components.Label import Label
10 from Components.PluginComponent import plugins
11 from Components.config import config, ConfigSubsection, ConfigText, ConfigInteger, ConfigLocations, ConfigSet, ConfigYesNo, ConfigSelection, getConfigListEntry
12 from Components.ConfigList import ConfigListScreen
13 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
14 from Components.Sources.ServiceEvent import ServiceEvent
15 from Components.Sources.StaticText import StaticText
16 import Components.Harddisk
17 from Components.UsageConfig import preferredTimerPath
18
19 from Plugins.Plugin import PluginDescriptor
20
21 from Screens.MessageBox import MessageBox
22 from Screens.ChoiceBox import ChoiceBox
23 from Screens.LocationBox import MovieLocationBox
24 from Screens.HelpMenu import HelpableScreen
25 import Screens.InfoBar
26
27 from Tools import NumericalTextInput
28 from Tools.Directories import resolveFilename, SCOPE_HDD
29 from Tools.BoundFunction import boundFunction
30 import Tools.Trashcan
31 import NavigationInstance
32 import RecordTimer
33
34 from enigma import eServiceReference, eServiceCenter, eTimer, eSize, iPlayableService, iServiceInformation, getPrevAsciiCode, eRCInput
35 import os
36 import time
37 import cPickle as pickle
38
39 config.movielist = ConfigSubsection()
40 config.movielist.moviesort = ConfigInteger(default=MovieList.SORT_GROUPWISE)
41 config.movielist.listtype = ConfigInteger(default=MovieList.LISTTYPE_MINIMAL)
42 config.movielist.description = ConfigInteger(default=MovieList.SHOW_DESCRIPTION)
43 config.movielist.last_videodir = ConfigText(default=resolveFilename(SCOPE_HDD))
44 config.movielist.last_timer_videodir = ConfigText(default=resolveFilename(SCOPE_HDD))
45 config.movielist.videodirs = ConfigLocations(default=[resolveFilename(SCOPE_HDD)])
46 config.movielist.last_selected_tags = ConfigSet([], default=[])
47 config.movielist.play_audio_internal = ConfigYesNo(default=True)
48 config.movielist.settings_per_directory = ConfigYesNo(default=True)
49 config.movielist.root = ConfigSelection(default="/media", choices=["/","/media","/media/hdd","/media/hdd/movie","/media/usb","/media/usb/movie"])
50 config.movielist.hide_extensions = ConfigYesNo(default=False)
51 config.movielist.stop_service = ConfigYesNo(default=True)
52
53 userDefinedButtons = None
54 last_selected_dest = []
55 preferredTagEditor = None
56
57 # this kludge is needed because ConfigSelection only takes numbers
58 # and someone appears to be fascinated by 'enums'.
59 l_moviesort = [
60         (str(MovieList.SORT_GROUPWISE), _("default") , '02/01 & A-Z'),
61         (str(MovieList.SORT_RECORDED), _("by date"), '03/02/01'),
62         (str(MovieList.SORT_ALPHANUMERIC), _("alphabetic"), 'A-Z'),
63         (str(MovieList.SORT_ALPHANUMERIC_FLAT), _("flat alphabetic"), 'A-Z Flat'),
64         (str(MovieList.SHUFFLE), _("shuffle"), '?'),
65         (str(MovieList.SORT_RECORDED_REVERSE), _("reverse by date"), '01/02/03'),
66         (str(MovieList.SORT_ALPHANUMERIC_REVERSE), _("alphabetic reverse"), 'Z-A'),
67         (str(MovieList.SORT_ALPHANUMERIC_FLAT_REVERSE), _("flat alphabetic reverse"), 'Z-A Flat')]
68 l_listtype = [(str(MovieList.LISTTYPE_ORIGINAL), _("list style default")),
69         (str(MovieList.LISTTYPE_COMPACT_DESCRIPTION), _("list style compact with description")),
70         (str(MovieList.LISTTYPE_COMPACT), _("list style compact")),
71         (str(MovieList.LISTTYPE_MINIMAL), _("list style single line"))]
72
73 def defaultMoviePath():
74         result = config.usage.default_path.value
75         if not os.path.isdir(result):
76                 from Tools import Directories
77                 return Directories.defaultRecordingLocation()
78         return result
79
80 def setPreferredTagEditor(te):
81         global preferredTagEditor
82         if preferredTagEditor is None:
83                 preferredTagEditor = te
84                 print "Preferred tag editor changed to", preferredTagEditor
85         else:
86                 print "Preferred tag editor already set to", preferredTagEditor, "ignoring", te
87
88 def getPreferredTagEditor():
89         global preferredTagEditor
90         return preferredTagEditor
91
92 def isTrashFolder(ref):
93         if not config.usage.movielist_trashcan.value or not ref.flags & eServiceReference.mustDescent:
94                 return False
95         path = os.path.realpath(ref.getPath())
96         return path.endswith('.Trash') and path.startswith(Tools.Trashcan.getTrashFolder(path))
97
98 def isInTrashFolder(ref):
99         if not config.usage.movielist_trashcan.value or not ref.flags & eServiceReference.mustDescent:
100                 return False
101         path = os.path.realpath(ref.getPath())
102         return path.startswith(Tools.Trashcan.getTrashFolder(path))
103
104 def isSimpleFile(item):
105         if not item:
106                 return False
107         if not item[0] or not item[1]:
108                 return False
109         return (item[0].flags & eServiceReference.mustDescent) == 0
110
111 def isFolder(item):
112         if not item:
113                 return False
114         if not item[0] or not item[1]:
115                 return False
116         return (item[0].flags & eServiceReference.mustDescent) != 0
117
118
119 def canMove(item):
120         if not item:
121                 return False
122         if not item[0] or not item[1]:
123                 return False
124         if item[0].flags & eServiceReference.mustDescent:
125                 return not isTrashFolder(item[0])
126         return True
127
128 canDelete = canMove
129
130 def canCopy(item):
131         if not item:
132                 return False
133         if not item[0] or not item[1]:
134                 return False
135         if item[0].flags & eServiceReference.mustDescent:
136                 return False
137         return True
138
139 def createMoveList(serviceref, dest):
140         #normpath is to remove the trailing '/' from directories
141         src = os.path.normpath(serviceref.getPath())
142         srcPath, srcName = os.path.split(src)
143         if os.path.normpath(srcPath) == dest:
144                 # move file to itself is allowed, so we have to check it
145                 raise Exception, "Refusing to move to the same directory"
146         # Make a list of items to move
147         moveList = [(src, os.path.join(dest, srcName))]
148         if not serviceref.flags & eServiceReference.mustDescent:
149                 # Real movie, add extra files...
150                 srcBase = os.path.splitext(src)[0]
151                 baseName = os.path.split(srcBase)[1]
152                 eitName =  srcBase + '.eit'
153                 if os.path.exists(eitName):
154                         moveList.append((eitName, os.path.join(dest, baseName+'.eit')))
155                 baseName = os.path.split(src)[1]
156                 for ext in ('.ap', '.cuts', '.meta', '.sc'):
157                         candidate = src + ext
158                         if os.path.exists(candidate):
159                                 moveList.append((candidate, os.path.join(dest, baseName+ext)))
160         return moveList
161
162 def moveServiceFiles(serviceref, dest, name=None, allowCopy=True):
163         moveList = createMoveList(serviceref, dest)
164         # Try to "atomically" move these files
165         movedList = []
166         try:
167                 try:
168                         for item in moveList:
169                                 os.rename(item[0], item[1])
170                                 movedList.append(item)
171                 except OSError, e:
172                         if e.errno == 18 and allowCopy:
173                                 print "[MovieSelection] cannot rename across devices, trying slow move"
174                                 import CopyFiles
175                                 # start with the smaller files, do the big one later.
176                                 moveList.reverse()
177                                 if name is None:
178                                         name = os.path.split(moveList[-1][0])[1]
179                                 CopyFiles.moveFiles(moveList, name)
180                                 print "[MovieSelection] Moving in background..."
181                         else:
182                                 raise
183         except Exception, e:
184                 print "[MovieSelection] Failed move:", e
185                 for item in movedList:
186                         try:
187                                 os.rename(item[1], item[0])
188                         except:
189                                 print "[MovieSelection] Failed to undo move:", item
190                 # rethrow exception
191                 raise
192
193 def copyServiceFiles(serviceref, dest, name=None):
194         # current should be 'ref' type, dest a simple path string
195         moveList = createMoveList(serviceref, dest)
196         # Try to "atomically" move these files
197         movedList = []
198         try:
199                 for item in moveList:
200                         os.link(item[0], item[1])
201                         movedList.append(item)
202                 # this worked, we're done
203                 return
204         except Exception, e:
205                 print "[MovieSelection] Failed copy using link:", e
206                 for item in movedList:
207                         try:
208                                 os.unlink(item[1])
209                         except:
210                                 print "[MovieSelection] Failed to undo copy:", item
211         #Link failed, really copy.
212         import CopyFiles
213         # start with the smaller files, do the big one later.
214         moveList.reverse()
215         if name is None:
216                 name = os.path.split(moveList[-1][0])[1]
217         CopyFiles.copyFiles(moveList, name)
218         print "[MovieSelection] Copying in background..."
219
220 # Appends possible destinations to the bookmarks object. Appends tuples
221 # in the form (description, path) to it.
222 def buildMovieLocationList(bookmarks):
223         inlist = []
224         for d in config.movielist.videodirs.value:
225                 d = os.path.normpath(d)
226                 bookmarks.append((d,d))
227                 inlist.append(d)
228         for p in Components.Harddisk.harddiskmanager.getMountedPartitions():
229                 d = os.path.normpath(p.mountpoint)
230                 if d in inlist:
231                         # improve shortcuts to mountpoints
232                         try:
233                                 bookmarks[bookmarks.index((d,d))] = (p.tabbedDescription(), d)
234                         except:
235                                 pass # When already listed as some "friendly" name
236                 else:
237                         bookmarks.append((p.tabbedDescription(), d))
238                 inlist.append(d)
239         for d in last_selected_dest:
240                 if d not in inlist:
241                         bookmarks.append((d,d))
242
243 class MovieBrowserConfiguration(ConfigListScreen,Screen):
244         skin = """
245 <screen position="center,center" size="560,400" title="Movie Browser Configuration" >
246         <ePixmap name="red"    position="0,0"   zPosition="2" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
247         <ePixmap name="green"  position="140,0" zPosition="2" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
248
249         <widget name="key_red" position="0,0" size="140,40" valign="center" halign="center" zPosition="4"  foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
250         <widget name="key_green" position="140,0" size="140,40" valign="center" halign="center" zPosition="4"  foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
251         <widget name="config" position="10,40" size="540,340" scrollbarMode="showOnDemand" />
252
253         <ePixmap alphatest="on" pixmap="skin_default/icons/clock.png" position="480,383" size="14,14" zPosition="3"/>
254         <widget font="Regular;18" halign="left" position="505,380" render="Label" size="55,20" source="global.CurrentTime" transparent="1" valign="center" zPosition="3">
255                 <convert type="ClockToText">Default</convert>
256         </widget>
257 </screen>"""
258
259         def __init__(self, session, args = 0):
260                 self.session = session
261                 self.setup_title = _("Movie list configuration")
262                 Screen.__init__(self, session)
263                 cfg = ConfigSubsection()
264                 self.cfg = cfg
265                 cfg.moviesort = ConfigSelection(default=str(config.movielist.moviesort.value), choices = l_moviesort)
266                 cfg.listtype = ConfigSelection(default=str(config.movielist.listtype.value), choices = l_listtype)
267                 cfg.description = ConfigYesNo(default=(config.movielist.description.value != MovieList.HIDE_DESCRIPTION))
268                 configList = [
269                         getConfigListEntry(_("Sort"), cfg.moviesort),
270                         getConfigListEntry(_("Show extended description"), cfg.description),
271                         getConfigListEntry(_("Type"), cfg.listtype),
272                         getConfigListEntry(_("Use individual settings for each directory"), config.movielist.settings_per_directory),
273                         getConfigListEntry(_("Behavior when a movie reaches the end"), config.usage.on_movie_eof),
274                         getConfigListEntry(_("Stop service on return to movie list"), config.movielist.stop_service),
275                         getConfigListEntry(_("Load length of movies in movie list"), config.usage.load_length_of_movies_in_moviellist),
276                         getConfigListEntry(_("Show status icons in movie list"), config.usage.show_icons_in_movielist),
277                         getConfigListEntry(_("Show icon for new/unseen items"), config.usage.movielist_unseen),
278                         getConfigListEntry(_("Play audio in background"), config.movielist.play_audio_internal),
279                         getConfigListEntry(_("Root directory"), config.movielist.root),
280                         getConfigListEntry(_("Hide known extensions"), config.movielist.hide_extensions),
281                         ]
282                 for btn in ('red', 'green', 'yellow', 'blue', 'TV', 'Radio', 'Text', 'F1', 'F2', 'F3'):
283                         configList.append(getConfigListEntry(_(btn), userDefinedButtons[btn]))
284                 ConfigListScreen.__init__(self, configList, session=session, on_change = self.changedEntry)
285                 self["key_red"] = Button(_("Cancel"))
286                 self["key_green"] = Button(_("Ok"))
287                 self["setupActions"] = ActionMap(["SetupActions", "ColorActions",  "MenuActions"],
288                 {
289                         "red": self.cancel,
290                         "green": self.save,
291                         "save": self.save,
292                         "cancel": self.cancel,
293                         "ok": self.save,
294                         "menu": self.cancel,
295                 }, -2)
296                 self.onChangedEntry = []
297
298         # for summary:
299         def changedEntry(self):
300                 for x in self.onChangedEntry:
301                         x()
302         def getCurrentEntry(self):
303                 return self["config"].getCurrent()[0]
304         def getCurrentValue(self):
305                 return str(self["config"].getCurrent()[1].getText())
306         def createSummary(self):
307                 from Screens.Setup import SetupSummary
308                 return SetupSummary
309
310         def save(self):
311                 self.saveAll()
312                 cfg = self.cfg
313                 config.movielist.moviesort.value = int(cfg.moviesort.value)
314                 config.movielist.listtype.value = int(cfg.listtype.value)
315                 if cfg.description.value:
316                         config.movielist.description.value = MovieList.SHOW_DESCRIPTION
317                 else:
318                         config.movielist.description.value = MovieList.HIDE_DESCRIPTION
319                 if not config.movielist.settings_per_directory.value:
320                         config.movielist.moviesort.save()
321                         config.movielist.listtype.save()
322                         config.movielist.description.save()
323                         config.usage.on_movie_eof.save()
324                 self.close(True)
325
326         def cancel(self):
327                 if self["config"].isChanged():
328                         self.session.openWithCallback(self.cancelCallback, MessageBox, _("Really close without saving settings?"))
329                 else:
330                         self.cancelCallback(True)
331
332         def cancelCallback(self, answer):
333                 if answer:
334                         for x in self["config"].list:
335                                 x[1].cancel()
336                         self.close(False)
337
338 class MovieContextMenuSummary(Screen):
339         def __init__(self, session, parent):
340                 Screen.__init__(self, session, parent = parent)
341                 self["selected"] = StaticText("")
342                 self.onShow.append(self.__onShow)
343                 self.onHide.append(self.__onHide)
344
345         def __onShow(self):
346                 self.parent["menu"].onSelectionChanged.append(self.selectionChanged)
347                 self.selectionChanged()
348
349         def __onHide(self):
350                 self.parent["menu"].onSelectionChanged.remove(self.selectionChanged)
351
352         def selectionChanged(self):
353                 item = self.parent["menu"].getCurrent()
354                 self["selected"].text = item[0][0]
355
356 class MovieContextMenu(Screen):
357         # Contract: On OK returns a callable object (e.g. delete)
358         def __init__(self, session, csel, service):
359                 Screen.__init__(self, session)
360                 self.csel = csel
361                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "MenuActions"],
362                         {
363                                 "ok": self.okbuttonClick,
364                                 "cancel": self.cancelClick,
365                                 "yellow": self.do_showNetworkSetup,
366                                 "menu": self.do_configure,
367                                 "2": self.do_rename,
368                                 "5": self.do_copy,
369                                 "6": self.do_move,
370                                 "7": self.do_createdir,
371                                 "8": self.do_delete
372                         })
373
374                 def append_to_menu(menu, args, key=""):
375                         menu.append(ChoiceEntryComponent(key, args))
376
377                 menu = []
378                 if service:
379                         if (service.flags & eServiceReference.mustDescent):
380                                 if isTrashFolder(service):
381                                         append_to_menu(menu, (_("Permanently remove all deleted items"), csel.purgeAll), key="8")
382                                 else:
383                                         append_to_menu(menu, (_("Delete"), csel.do_delete), key="8")
384                                         append_to_menu(menu, (_("Move"), csel.do_move), key="6")
385                                         append_to_menu(menu, (_("Rename"), csel.do_rename), key="2")
386                         else:
387                                 append_to_menu(menu, (_("Delete"), csel.do_delete), key="8")
388                                 append_to_menu(menu, (_("Move"), csel.do_move), key="6")
389                                 append_to_menu(menu, (_("Copy"), csel.do_copy), key="5")
390                                 append_to_menu(menu, (_("Reset playback position"), csel.do_reset))
391                                 append_to_menu(menu, (_("Rename"), csel.do_rename), key="2")
392                                 append_to_menu(menu, (_("Start offline decode"), csel.do_decode))
393
394                                 # Plugins expect a valid selection, so only include them if we selected a non-dir
395                                 for p in plugins.getPlugins(PluginDescriptor.WHERE_MOVIELIST):
396                                         append_to_menu( menu, (p.description, boundFunction(p, session, service)), key="bullet")
397                 if csel.exist_bookmark():
398                         append_to_menu(menu, (_("Remove bookmark"), csel.do_addbookmark))
399                 else:
400                         append_to_menu(menu, (_("Add bookmark"), csel.do_addbookmark))
401                 append_to_menu(menu, (_("create directory"), csel.do_createdir), key="7")
402                 append_to_menu(menu, (_("Sort by") + "...", csel.selectSortby))
403                 append_to_menu(menu, (_("Network") + "...", csel.showNetworkSetup), key="yellow")
404                 append_to_menu(menu, (_("Settings") + "...", csel.configure), key="menu")
405                 self["menu"] = ChoiceList(menu)
406
407         def createSummary(self):
408                 return MovieContextMenuSummary
409
410         def okbuttonClick(self):
411                 self.close(self["menu"].getCurrent()[0][1])
412
413         def do_rename(self):
414                 self.close(self.csel.do_rename())
415         def do_copy(self):
416                 self.close(self.csel.do_copy())
417         def do_move(self):
418                 self.close(self.csel.do_move())
419         def do_createdir(self):
420                 self.close(self.csel.do_createdir())
421         def do_delete(self):
422                 self.close(self.csel.do_delete())
423         def do_configure(self):
424                 self.close(self.csel.configure())
425         def do_showNetworkSetup(self):
426                 self.close(self.csel.showNetworkSetup())
427
428         def cancelClick(self):
429                 self.close(None)
430
431 class SelectionEventInfo:
432         def __init__(self):
433                 self["Service"] = ServiceEvent()
434                 self.list.connectSelChanged(self.__selectionChanged)
435                 self.timer = eTimer()
436                 self.timer.callback.append(self.updateEventInfo)
437                 self.onShown.append(self.__selectionChanged)
438
439         def __selectionChanged(self):
440                 if self.execing and self.settings["description"] == MovieList.SHOW_DESCRIPTION:
441                         self.timer.start(100, True)
442
443         def updateEventInfo(self):
444                 serviceref = self.getCurrent()
445                 self["Service"].newService(serviceref)
446
447 class MovieSelectionSummary(Screen):
448         # Kludgy component to display current selection on LCD. Should use
449         # parent.Service as source for everything, but that seems to have a
450         # performance impact as the MovieSelection goes through hoops to prevent
451         # this when the info is not selected
452         def __init__(self, session, parent):
453                 Screen.__init__(self, session, parent = parent)
454                 self["name"] = StaticText("")
455                 self.onShow.append(self.__onShow)
456                 self.onHide.append(self.__onHide)
457
458         def __onShow(self):
459                 self.parent.list.connectSelChanged(self.selectionChanged)
460                 self.selectionChanged()
461
462         def __onHide(self):
463                 self.parent.list.disconnectSelChanged(self.selectionChanged)
464
465         def selectionChanged(self):
466                 item = self.parent.getCurrentSelection()
467                 if item and item[0]:
468                         data = item[3]
469                         if (data is not None) and (data != -1):
470                                 name = data.txt
471                         elif not item[1]:
472                                 # special case, one up
473                                 name = ".."
474                         else:
475                                 name = item[1].getName(item[0])
476                         if (item[0].flags & eServiceReference.mustDescent):
477                                 if len(name) > 12:
478                                         name = os.path.split(os.path.normpath(name))[1]
479                                 name = "> " + name
480                         self["name"].text = name
481                 else:
482                         self["name"].text = ""
483
484 from Screens.ParentalControlSetup import ProtectedScreen
485
486 class MovieSelection(Screen, HelpableScreen, SelectionEventInfo, InfoBarBase, ProtectedScreen):
487         # SUSPEND_PAUSES actually means "please call my pauseService()"
488         ALLOW_SUSPEND = Screen.SUSPEND_PAUSES
489
490         def __init__(self, session, selectedmovie = None, timeshiftEnabled = False):
491                 Screen.__init__(self, session)
492                 HelpableScreen.__init__(self)
493                 if not timeshiftEnabled:
494                         InfoBarBase.__init__(self) # For ServiceEventTracker
495                 ProtectedScreen.__init__(self)
496
497                 self.initUserDefinedActions()
498                 self.tags = {}
499                 if selectedmovie:
500                         self.selected_tags = config.movielist.last_selected_tags.value
501                 else:
502                         self.selected_tags = None
503                 self.selected_tags_ele = None
504                 self.nextInBackground = None
505
506                 self.movemode = False
507                 self.bouquet_mark_edit = False
508
509                 self.feedbackTimer = None
510                 self.pathselectEnabled = False
511
512                 self.numericalTextInput = NumericalTextInput.NumericalTextInput(mapping=NumericalTextInput.MAP_SEARCH_UPCASE)
513                 self["chosenletter"] = Label("")
514                 self["chosenletter"].visible = False
515
516                 self["waitingtext"] = Label(_("Please wait... Loading list..."))
517
518                 # create optional description border and hide immediately
519                 self["DescriptionBorder"] = Pixmap()
520                 self["DescriptionBorder"].hide()
521
522                 if not os.path.isdir(config.movielist.last_videodir.value):
523                         config.movielist.last_videodir.value = defaultMoviePath()
524                         config.movielist.last_videodir.save()
525                 self.setCurrentRef(config.movielist.last_videodir.value)
526
527                 self.settings = {\
528                         "listtype": config.movielist.listtype.value,
529                         "moviesort": config.movielist.moviesort.value,
530                         "description": config.movielist.description.value,
531                         "movieoff": config.usage.on_movie_eof.value
532                 }
533                 self.movieOff = self.settings["movieoff"]
534
535                 self["list"] = MovieList(None, list_type=self.settings["listtype"], sort_type=self.settings["moviesort"], descr_state=self.settings["description"])
536
537                 self.loadLocalSettings()
538
539                 self.list = self["list"]
540                 self.selectedmovie = selectedmovie
541
542                 self.playGoTo = None #1 - preview next item / -1 - preview previous
543
544                 title = _("Movie selection")
545                 self.setTitle(title)
546
547                 # Need list for init
548                 SelectionEventInfo.__init__(self)
549
550                 self["key_red"] = Button("")
551                 self["key_green"] = Button("")
552                 self["key_yellow"] = Button("")
553                 self["key_blue"] = Button("")
554                 self._updateButtonTexts()
555
556                 self["movie_off"] = MultiPixmap()
557                 self["movie_off"].hide()
558
559                 self["movie_sort"] = MultiPixmap()
560                 self["movie_sort"].hide()
561
562                 self["freeDiskSpace"] = self.diskinfo = DiskInfo(config.movielist.last_videodir.value, DiskInfo.FREE, update=False)
563
564                 self["InfobarActions"] = HelpableActionMap(self, "InfobarActions",
565                         {
566                                 "showMovies": (self.doPathSelect, _("Select the movie path")),
567                                 "showRadio": (self.btn_radio, boundFunction(self.getinitUserDefinedActionsDescription, "btn_radio")),
568                                 "showTv": (self.btn_tv, boundFunction(self.getinitUserDefinedActionsDescription, "btn_tv")),
569                                 "showText": (self.btn_text, boundFunction(self.getinitUserDefinedActionsDescription, "btn_text")),
570                         })
571
572                 self["NumberActions"] =  NumberActionMap(["NumberActions", "InputAsciiActions"],
573                         {
574                                 "gotAsciiCode": self.keyAsciiCode,
575                                 "0": self.keyNumberGlobal,
576                                 "1": self.keyNumberGlobal,
577                                 "2": self.keyNumberGlobal,
578                                 "3": self.keyNumberGlobal,
579                                 "4": self.keyNumberGlobal,
580                                 "5": self.keyNumberGlobal,
581                                 "6": self.keyNumberGlobal,
582                                 "7": self.keyNumberGlobal,
583                                 "8": self.keyNumberGlobal,
584                                 "9": self.keyNumberGlobal
585                         })
586
587                 self["playbackActions"] = HelpableActionMap(self, "MoviePlayerActions",
588                         {
589                                 "leavePlayer": (self.playbackStop, _("Stop")),
590                                 "moveNext": (self.playNext, _("Play next")),
591                                 "movePrev": (self.playPrev, _("Play previous")),
592                                 "channelUp": (self.moveToFirstOrFirstFile, _("Go to first movie or top of list")),
593                                 "channelDown": (self.moveToLastOrFirstFile, _("Go to first movie or last item")),
594                         })
595                 self["MovieSelectionActions"] = HelpableActionMap(self, "MovieSelectionActions",
596                         {
597                                 "contextMenu": (self.doContext, _("Menu")),
598                                 "showEventInfo": (self.showEventInformation, _("Show event details")),
599                         })
600
601                 self["ColorActions"] = HelpableActionMap(self, "ColorActions",
602                         {
603                                 "red": (self.btn_red, boundFunction(self.getinitUserDefinedActionsDescription, "btn_red")),
604                                 "green": (self.btn_green, boundFunction(self.getinitUserDefinedActionsDescription, "btn_green")),
605                                 "yellow": (self.btn_yellow, boundFunction(self.getinitUserDefinedActionsDescription, "btn_yellow")),
606                                 "blue": (self.btn_blue, boundFunction(self.getinitUserDefinedActionsDescription, "btn_blue")),
607                         })
608                 self["FunctionKeyActions"] = HelpableActionMap(self, "FunctionKeyActions",
609                         {
610                                 "f1": (self.btn_F1, boundFunction(self.getinitUserDefinedActionsDescription, "btn_F1")),
611                                 "f2": (self.btn_F2, boundFunction(self.getinitUserDefinedActionsDescription, "btn_F2")),
612                                 "f3": (self.btn_F3, boundFunction(self.getinitUserDefinedActionsDescription, "btn_F3")),
613                         })
614                 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
615                         {
616                                 "cancel": (self.abort, _("Exit movie list")),
617                                 "ok": (self.itemSelected, _("Select movie")),
618                         })
619                 self["DirectionActions"] = HelpableActionMap(self, "DirectionActions",
620                         {
621                                 "up": (self.keyUp, _("Go up the list")),
622                                 "down": (self.keyDown, _("Go down the list"))
623                         }, prio = -2)
624
625                 tPreview = _("Preview")
626                 tFwd = _("skip forward") + " (" + tPreview +")"
627                 tBack= _("skip backward") + " (" + tPreview +")"
628                 sfwd = lambda: self.seekRelative(1, config.seek.selfdefined_46.value * 90000)
629                 ssfwd = lambda: self.seekRelative(1, config.seek.selfdefined_79.value * 90000)
630                 sback = lambda: self.seekRelative(-1, config.seek.selfdefined_46.value * 90000)
631                 ssback = lambda: self.seekRelative(-1, config.seek.selfdefined_79.value * 90000)
632                 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions",
633                         {
634                                 "playpauseService": (self.preview, _("Preview")),
635                                 "seekFwd": (sfwd, tFwd),
636                                 "seekFwdManual": (ssfwd, tFwd),
637                                 "seekBack": (sback, tBack),
638                                 "seekBackManual": (ssback, tBack),
639                         }, prio=5)
640                 self.onShown.append(self.onFirstTimeShown)
641                 self.onLayoutFinish.append(self.saveListsize)
642                 self.list.connectSelChanged(self.updateButtons)
643                 self.onClose.append(self.__onClose)
644                 NavigationInstance.instance.RecordTimer.on_state_change.append(self.list.updateRecordings)
645                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
646                         {
647                                 #iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
648                                 iPlayableService.evStart: self.__serviceStarted,
649                                 iPlayableService.evEOF: self.__evEOF,
650                                 #iPlayableService.evSOF: self.__evSOF,
651                         })
652                 self.onExecBegin.append(self.asciiOn)
653
654         def isProtected(self):
655                 return config.ParentalControl.setuppinactive.value and config.ParentalControl.config_sections.movie_list.value
656
657         def asciiOn(self):
658                 rcinput = eRCInput.getInstance()
659                 rcinput.setKeyboardMode(rcinput.kmAscii)
660
661         def asciiOff(self):
662                 rcinput = eRCInput.getInstance()
663                 rcinput.setKeyboardMode(rcinput.kmNone)
664
665         def initUserDefinedActions(self):
666                 global userDefinedButtons, userDefinedActions, config
667                 if userDefinedButtons is None:
668                         userDefinedActions = {
669                                 'delete': _("Delete"),
670                                 'move': _("Move"),
671                                 'copy': _("Copy"),
672                                 'reset': _("Reset"),
673                                 'tags': _("Tags"),
674                                 'addbookmark': _("Add bookmark"),
675                                 'bookmarks': _("Location"),
676                                 'rename': _("Rename"),
677                                 'gohome': _("Home"),
678                                 'sort': _("Sort"),
679                                 'sortby': _("Sort by"),
680                                 'listtype': _("List type"),
681                                 'preview': _("Preview"),
682                                 'movieoff': _("On end of movie"),
683                                 'movieoff_menu': _("On end of movie (as menu)")
684                         }
685                         for p in plugins.getPlugins(PluginDescriptor.WHERE_MOVIELIST):
686                                 userDefinedActions['@' + p.name] = p.description
687                         locations = []
688                         buildMovieLocationList(locations)
689                         prefix = _("Goto") + ": "
690                         for d,p in locations:
691                                 if p and p.startswith('/'):
692                                         userDefinedActions[p] = prefix + d
693                         config.movielist.btn_red = ConfigSelection(default='delete', choices=userDefinedActions)
694                         config.movielist.btn_green = ConfigSelection(default='move', choices=userDefinedActions)
695                         config.movielist.btn_yellow = ConfigSelection(default='bookmarks', choices=userDefinedActions)
696                         config.movielist.btn_blue = ConfigSelection(default='sort', choices=userDefinedActions)
697                         config.movielist.btn_radio = ConfigSelection(default='tags', choices=userDefinedActions)
698                         config.movielist.btn_tv = ConfigSelection(default='gohome', choices=userDefinedActions)
699                         config.movielist.btn_text = ConfigSelection(default='movieoff', choices=userDefinedActions)
700                         config.movielist.btn_F1 = ConfigSelection(default='movieoff_menu', choices=userDefinedActions)
701                         config.movielist.btn_F2 = ConfigSelection(default='preview', choices=userDefinedActions)
702                         config.movielist.btn_F3 = ConfigSelection(default='/media', choices=userDefinedActions)
703                         userDefinedButtons ={
704                                 'red': config.movielist.btn_red,
705                                 'green': config.movielist.btn_green,
706                                 'yellow': config.movielist.btn_yellow,
707                                 'blue': config.movielist.btn_blue,
708                                 'Radio': config.movielist.btn_radio,
709                                 'TV': config.movielist.btn_tv,
710                                 'Text': config.movielist.btn_text,
711                                 'F1': config.movielist.btn_F1,
712                                 'F2': config.movielist.btn_F2,
713                                 'F3': config.movielist.btn_F3
714                         }
715
716         def getinitUserDefinedActionsDescription(self, key):
717                 return _(userDefinedActions.get(eval("config.movielist." + key + ".value"), _("Not Defined")))
718
719         def _callButton(self, name):
720                 if name.startswith('@'):
721                         item = self.getCurrentSelection()
722                         if isSimpleFile(item):
723                                 name = name[1:]
724                                 for p in plugins.getPlugins(PluginDescriptor.WHERE_MOVIELIST):
725                                         if name == p.name:
726                                                 p(self.session, item[0])
727                 elif name.startswith('/'):
728                         self.gotFilename(name)
729                 else:
730                         try:
731                                 a = getattr(self, 'do_' + name)
732                         except Exception:
733                                 # Undefined action
734                                 return
735                         a()
736
737         def btn_red(self):
738                 self._callButton(config.movielist.btn_red.value)
739         def btn_green(self):
740                 self._callButton(config.movielist.btn_green.value)
741         def btn_yellow(self):
742                 self._callButton(config.movielist.btn_yellow.value)
743         def btn_blue(self):
744                 self._callButton(config.movielist.btn_blue.value)
745         def btn_radio(self):
746                 self._callButton(config.movielist.btn_radio.value)
747         def btn_tv(self):
748                 self._callButton(config.movielist.btn_tv.value)
749         def btn_text(self):
750                 self._callButton(config.movielist.btn_text.value)
751         def btn_F1(self):
752                 self._callButton(config.movielist.btn_F1.value)
753         def btn_F2(self):
754                 self._callButton(config.movielist.btn_F2.value)
755         def btn_F3(self):
756                 self._callButton(config.movielist.btn_F3.value)
757
758         def keyUp(self):
759                 if self["list"].getCurrentIndex() < 1:
760                         self["list"].moveToLast()
761                 else:
762                         self["list"].moveUp()
763
764         def keyDown(self):
765                 if self["list"].getCurrentIndex() == len(self["list"]) - 1:
766                         self["list"].moveToFirst()
767                 else:
768                         self["list"].moveDown()
769
770         def moveToFirstOrFirstFile(self):
771                 if self.list.getCurrentIndex() <= self.list.firstFileEntry: #selection above or on first movie
772                         if self.list.getCurrentIndex() < 1:
773                                 self.list.moveToLast()
774                         else:
775                                 self.list.moveToFirst()
776                 else:
777                         self.list.moveToFirstMovie()
778
779         def moveToLastOrFirstFile(self):
780                 if self.list.getCurrentIndex() >= self.list.firstFileEntry or self.list.firstFileEntry == len(self.list): #selection below or on first movie or no files
781                         if self.list.getCurrentIndex() == len(self.list) - 1:
782                                 self.list.moveToFirst()
783                         else:
784                                 self.list.moveToLast()
785                 else:
786                         self.list.moveToFirstMovie()
787
788         def keyNumberGlobal(self, number):
789                 unichar = self.numericalTextInput.getKey(number)
790                 charstr = unichar.encode("utf-8")
791                 if len(charstr) == 1:
792                         self.list.moveToChar(charstr[0], self["chosenletter"])
793
794         def keyAsciiCode(self):
795                 unichar = unichr(getPrevAsciiCode())
796                 charstr = unichar.encode("utf-8")
797                 if len(charstr) == 1:
798                         self.list.moveToString(charstr[0], self["chosenletter"])
799
800         def isItemPlayable(self, index):
801                 item = self.list.getItem(index)
802                 if item:
803                         path = item.getPath()
804                         if not item.flags & eServiceReference.mustDescent:
805                                 ext = os.path.splitext(path)[1].lower()
806                                 if ext in IMAGE_EXTENSIONS:
807                                         return False
808                                 else:
809                                         return True
810                 return False
811
812         def goToPlayingService(self):
813                 service = self.session.nav.getCurrentlyPlayingServiceOrGroup()
814                 if service:
815                         path = service.getPath()
816                         if path:
817                                 path = os.path.split(os.path.normpath(path))[0]
818                                 if not path.endswith('/'):
819                                         path += '/'
820                                 self.gotFilename(path, selItem = service)
821                                 return True
822                 return False
823
824         def playNext(self):
825                 if self.list.playInBackground:
826                         if self.list.moveTo(self.list.playInBackground):
827                                 if self.isItemPlayable(self.list.getCurrentIndex() + 1):
828                                         self.list.moveDown()
829                                         self.callLater(self.preview)
830                         else:
831                                 self.playGoTo = 1
832                                 self.goToPlayingService()
833                 else:
834                         self.preview()
835
836         def playPrev(self):
837                 if self.list.playInBackground:
838                         if self.list.moveTo(self.list.playInBackground):
839                                 if self.isItemPlayable(self.list.getCurrentIndex() - 1):
840                                         self.list.moveUp()
841                                         self.callLater(self.preview)
842                         else:
843                                 self.playGoTo = -1
844                                 self.goToPlayingService()
845
846         def __onClose(self):
847                 try:
848                         NavigationInstance.instance.RecordTimer.on_state_change.remove(self.list.updateRecordings)
849                 except Exception, e:
850                         print "[ML] failed to unsubscribe:", e
851                         pass
852
853         def createSummary(self):
854                 return MovieSelectionSummary
855
856         def updateDescription(self):
857                 if self.settings["description"] == MovieList.SHOW_DESCRIPTION:
858                         self["DescriptionBorder"].show()
859                         self["list"].instance.resize(eSize(self.listWidth, self.listHeight-self["DescriptionBorder"].instance.size().height()))
860                 else:
861                         self["Service"].newService(None)
862                         self["DescriptionBorder"].hide()
863                         self["list"].instance.resize(eSize(self.listWidth, self.listHeight))
864
865         def pauseService(self):
866                 # Called when pressing Power button (go to standby)
867                 self.playbackStop()
868                 self.session.nav.stopService()
869
870         def unPauseService(self):
871                 # When returning from standby. It might have been a while, so
872                 # reload the list.
873                 self.reloadList()
874
875         def can_delete(self, item):
876                 if not item:
877                         return False
878                 return canDelete(item) or isTrashFolder(item[0])
879         def can_move(self, item):
880                 return canMove(item)
881         def can_default(self, item):
882                 # returns whether item is a regular file
883                 return isSimpleFile(item)
884         def can_sort(self, item):
885                 return True
886         def can_listtype(self, item):
887                 return True
888         def can_preview(self, item):
889                 return isSimpleFile(item)
890
891         def _updateButtonTexts(self):
892                 for k in ('red', 'green', 'yellow', 'blue'):
893                         btn = userDefinedButtons[k]
894                         self['key_' + k].setText(userDefinedActions[btn.value])
895
896         def updateButtons(self):
897                 item = self.getCurrentSelection()
898                 for name in ('red', 'green', 'yellow', 'blue'):
899                         action = userDefinedButtons[name].value
900                         if action.startswith('@'):
901                                 check = self.can_default
902                         elif action.startswith('/'):
903                                 check = self.can_gohome
904                         else:
905                                 try:
906                                         check = getattr(self, 'can_' + action)
907                                 except:
908                                         check = self.can_default
909                         gui = self["key_" + name]
910                         if check(item):
911                                 gui.show()
912                         else:
913                                 gui.hide()
914
915         def showEventInformation(self):
916                 from Screens.EventView import EventViewSimple
917                 from ServiceReference import ServiceReference
918                 evt = self["list"].getCurrentEvent()
919                 if evt:
920                         self.session.open(EventViewSimple, evt, ServiceReference(self.getCurrent()))
921
922         def saveListsize(self):
923                         listsize = self["list"].instance.size()
924                         self.listWidth = listsize.width()
925                         self.listHeight = listsize.height()
926                         self.updateDescription()
927
928         def onFirstTimeShown(self):
929                 self.onShown.remove(self.onFirstTimeShown) # Just once, not after returning etc.
930                 self.show()
931                 self.reloadList(self.selectedmovie, home=True)
932                 del self.selectedmovie
933
934         def getCurrent(self):
935                 # Returns selected serviceref (may be None)
936                 return self["list"].getCurrent()
937
938         def getCurrentSelection(self):
939                 # Returns None or (serviceref, info, begin, len)
940                 return self["list"].l.getCurrentSelection()
941
942         def playAsDVD(self, path):
943                 try:
944                         from Screens import DVD
945                         if path.endswith('VIDEO_TS/'):
946                                 # strip away VIDEO_TS/ part
947                                 path = os.path.split(path.rstrip('/'))[0]
948                         self.session.open(DVD.DVDPlayer, dvd_filelist=[path])
949                         return True
950                 except Exception, e:
951                         print "[ML] DVD Player not installed:", e
952
953         def __serviceStarted(self):
954                 if not self.list.playInBackground:
955                         return
956                 ref = self.session.nav.getCurrentService()
957                 cue = ref.cueSheet()
958                 if not cue:
959                         return
960                 # disable writing the stop position
961                 cue.setCutListEnable(2)
962                 # find "resume" position
963                 cuts = cue.getCutList()
964                 if not cuts:
965                         return
966                 for (pts, what) in cuts:
967                         if what == 3:
968                                 last = pts
969                                 break
970                 else:
971                         # no resume, jump to start of program (first marker)
972                         last = cuts[0][0]
973                 self.doSeekTo = last
974                 self.callLater(self.doSeek)
975
976         def doSeek(self, pts = None):
977                 if pts is None:
978                         pts = self.doSeekTo
979                 seekable = self.getSeek()
980                 if seekable is None:
981                         return
982                 seekable.seekTo(pts)
983
984         def getSeek(self):
985                 service = self.session.nav.getCurrentService()
986                 if service is None:
987                         return None
988                 seek = service.seek()
989                 if seek is None or not seek.isCurrentlySeekable():
990                         return None
991                 return seek
992
993         def callLater(self, function):
994                 self.previewTimer = eTimer()
995                 self.previewTimer.callback.append(function)
996                 self.previewTimer.start(10, True)
997
998         def __evEOF(self):
999                 playInBackground = self.list.playInBackground
1000                 if not playInBackground:
1001                         print "Not playing anything in background"
1002                         return
1003                 self.session.nav.stopService()
1004                 self.list.playInBackground = None
1005                 if config.movielist.play_audio_internal.value:
1006                         index = self.list.findService(playInBackground)
1007                         if index is None:
1008                                 return # Not found?
1009                         next = self.list.getItem(index + 1)
1010                         if not next:
1011                                 return
1012                         path = next.getPath()
1013                         ext = os.path.splitext(path)[1].lower()
1014                         print "Next up:", path
1015                         if ext in AUDIO_EXTENSIONS:
1016                                 self.nextInBackground = next
1017                                 self.callLater(self.preview)
1018                                 self["list"].moveToIndex(index+1)
1019
1020         def preview(self):
1021                 current = self.getCurrent()
1022                 if current is not None:
1023                         path = current.getPath()
1024                         if current.flags & eServiceReference.mustDescent:
1025                                 self.gotFilename(path)
1026                         else:
1027                                 Screens.InfoBar.InfoBar.instance.checkTimeshiftRunning(self.previewCheckTimeshiftCallback)
1028
1029         def startPreview(self):
1030                 if self.nextInBackground is not None:
1031                         current = self.nextInBackground
1032                         self.nextInBackground = None
1033                 else:
1034                         current = self.getCurrent()
1035                 playInBackground = self.list.playInBackground
1036                 if playInBackground:
1037                         self.list.playInBackground = None
1038                         self.session.nav.stopService()
1039                         if playInBackground != current:
1040                                 # come back to play the new one
1041                                 self.callLater(self.preview)
1042                 else:
1043                         self.list.playInBackground = current
1044                         self.session.nav.playService(current)
1045
1046         def previewCheckTimeshiftCallback(self, answer):
1047                 if answer:
1048                         self.startPreview()
1049
1050         def seekRelative(self, direction, amount):
1051                 if self.list.playInBackground:
1052                         seekable = self.getSeek()
1053                         if seekable is None:
1054                                 return
1055                         seekable.seekRelative(direction, amount)
1056
1057         def playbackStop(self):
1058                 if self.list.playInBackground:
1059                         self.list.playInBackground = None
1060                         self.session.nav.stopService()
1061
1062         def itemSelected(self, answer = True):
1063                 current = self.getCurrent()
1064                 if current is not None:
1065                         path = current.getPath()
1066                         if current.flags & eServiceReference.mustDescent:
1067                                 if path.endswith("VIDEO_TS/") or os.path.exists(os.path.join(path, 'VIDEO_TS.IFO')):
1068                                         #force a DVD extention
1069                                         Screens.InfoBar.InfoBar.instance.checkTimeshiftRunning(boundFunction(self.itemSelectedCheckTimeshiftCallback, ".iso", path))
1070                                         return
1071                                 self.gotFilename(path)
1072                         else:
1073                                 ext = os.path.splitext(path)[1].lower()
1074                                 if config.movielist.play_audio_internal.value and (ext in AUDIO_EXTENSIONS):
1075                                         self.preview()
1076                                         return
1077                                 if self.list.playInBackground:
1078                                         # Stop preview, come back later
1079                                         self.session.nav.stopService()
1080                                         self.list.playInBackground = None
1081                                         self.callLater(self.itemSelected)
1082                                         return
1083                                 if ext in IMAGE_EXTENSIONS:
1084                                         try:
1085                                                 from Plugins.Extensions.PicturePlayer import ui
1086                                                 # Build the list for the PicturePlayer UI
1087                                                 filelist = []
1088                                                 index = 0
1089                                                 for item in self.list.list:
1090                                                         p = item[0].getPath()
1091                                                         if p == path:
1092                                                                 index = len(filelist)
1093                                                         if os.path.splitext(p)[1].lower() in IMAGE_EXTENSIONS:
1094                                                                 filelist.append(((p,False), None))
1095                                                 self.session.open(ui.Pic_Full_View, filelist, index, path)
1096                                         except Exception, ex:
1097                                                 print "[ML] Cannot display", str(ex)
1098                                         return
1099                                 Screens.InfoBar.InfoBar.instance.checkTimeshiftRunning(boundFunction(self.itemSelectedCheckTimeshiftCallback, ext, path))
1100
1101         def itemSelectedCheckTimeshiftCallback(self, ext, path, answer):
1102                 if answer:
1103                         if ext in DVD_EXTENSIONS:
1104                                 if self.playAsDVD(path):
1105                                         return
1106                         self.movieSelected()
1107
1108         # Note: DVDBurn overrides this method, hence the itemSelected indirection.
1109         def movieSelected(self):
1110                 current = self.getCurrent()
1111                 if current is not None:
1112                         self.saveconfig()
1113                         self.close(current)
1114
1115         def doContext(self):
1116                 current = self.getCurrent()
1117                 if current is not None:
1118                         self.session.openWithCallback(self.doneContext, MovieContextMenu, self, current)
1119
1120         def doneContext(self, action):
1121                 if action is not None:
1122                         action()
1123
1124         def saveLocalSettings(self):
1125                 if config.movielist.settings_per_directory.value:
1126                         try:
1127                                 path = os.path.join(config.movielist.last_videodir.value, ".e2settings.pkl")
1128                                 pickle.dump(self.settings, open(path, "wb"))
1129                         except Exception, e:
1130                                 print "Failed to save settings to %s: %s" % (path, e)
1131                 # Also set config items, in case the user has a read-only disk
1132                 config.movielist.moviesort.value = self.settings["moviesort"]
1133                 config.movielist.listtype.value = self.settings["listtype"]
1134                 config.movielist.description.value = self.settings["description"]
1135                 config.usage.on_movie_eof.value = self.settings["movieoff"]
1136                 # save moviesort and movieeof values for using by hotkeys
1137                 config.movielist.moviesort.save()
1138                 config.usage.on_movie_eof.save()
1139
1140         def loadLocalSettings(self):
1141                 'Load settings, called when entering a directory'
1142                 if config.movielist.settings_per_directory.value:
1143                         try:
1144                                 path = os.path.join(config.movielist.last_videodir.value, ".e2settings.pkl")
1145                                 updates = pickle.load(open(path, "rb"))
1146                                 self.applyConfigSettings(updates)
1147                         except IOError, e:
1148                                 updates = {
1149                                         "listtype": config.movielist.listtype.default,
1150                                         "moviesort": config.movielist.moviesort.default,
1151                                         "description": config.movielist.description.default,
1152                                         "movieoff": config.usage.on_movie_eof.default
1153                                 }
1154                                 self.applyConfigSettings(updates)
1155                                 pass # ignore fail to open errors
1156                         except Exception, e:
1157                                 print "Failed to load settings from %s: %s" % (path, e)
1158                 else:
1159                         updates = {
1160                                 "listtype": config.movielist.listtype.value,
1161                                 "moviesort": config.movielist.moviesort.value,
1162                                 "description": config.movielist.description.value,
1163                                 "movieoff": config.usage.on_movie_eof.value
1164                                 }
1165                         self.applyConfigSettings(updates)
1166
1167         def applyConfigSettings(self, updates):
1168                 needUpdate = ("description" in updates) and (updates["description"] != self.settings["description"])
1169                 self.settings.update(updates)
1170                 if needUpdate:
1171                         self["list"].setDescriptionState(self.settings["description"])
1172                         self.updateDescription()
1173                 if self.settings["listtype"] != self["list"].list_type:
1174                         self["list"].setListType(self.settings["listtype"])
1175                         needUpdate = True
1176                 if self.settings["moviesort"] != self["list"].sort_type:
1177                         self["list"].setSortType(self.settings["moviesort"])
1178                         needUpdate = True
1179                 if self.settings["movieoff"] != self.movieOff:
1180                         self.movieOff = self.settings["movieoff"]
1181                         needUpdate = True
1182                 config.movielist.moviesort.value = self.settings["moviesort"]
1183                 config.movielist.listtype.value = self.settings["listtype"]
1184                 config.movielist.description.value = self.settings["description"]
1185                 config.usage.on_movie_eof.value = self.settings["movieoff"]
1186                 return needUpdate
1187
1188         def sortBy(self, newType):
1189                 self.settings["moviesort"] = newType
1190                 self.saveLocalSettings()
1191                 self.setSortType(newType)
1192                 self.reloadList()
1193
1194         def listType(self, newType):
1195                 self.settings["listtype"] = newType
1196                 self.saveLocalSettings()
1197                 self.setListType(newType)
1198                 self.reloadList()
1199
1200         def showDescription(self, newType):
1201                 self.settings["description"] = newType
1202                 self.saveLocalSettings()
1203                 self.setDescriptionState(newType)
1204                 self.updateDescription()
1205
1206         def abort(self):
1207                 global playlist
1208                 del playlist[:]
1209                 if self.list.playInBackground:
1210                         self.list.playInBackground = None
1211                         self.session.nav.stopService()
1212                         self.callLater(self.abort)
1213                         return
1214                 self.saveconfig()
1215                 self.close(None)
1216
1217         def saveconfig(self):
1218                 config.movielist.last_selected_tags.value = self.selected_tags
1219
1220         def configure(self):
1221                 self.session.openWithCallback(self.configureDone, MovieBrowserConfiguration)
1222
1223         def configureDone(self, result):
1224                 if result:
1225                         self.applyConfigSettings({\
1226                                 "listtype": config.movielist.listtype.value,
1227                                 "moviesort": config.movielist.moviesort.value,
1228                                 "description": config.movielist.description.value,
1229                                 "movieoff": config.usage.on_movie_eof.value})
1230                         self.saveLocalSettings()
1231                         self._updateButtonTexts()
1232                         self.reloadList()
1233
1234         def can_sortby(self, item):
1235                 return True
1236
1237         def do_sortby(self):
1238                 self.selectSortby()
1239
1240         def selectSortby(self):
1241                 menu = []
1242                 index = 0
1243                 used = 0
1244                 for x in l_moviesort:
1245                         if int(x[0]) == int(config.movielist.moviesort.value):
1246                                 used = index
1247                         menu.append((_(x[1]), x[0], "%d" % index))
1248                         index += 1
1249                 self.session.openWithCallback(self.sortbyMenuCallback, ChoiceBox, title=_("Sort list:"), list=menu, selection = used)
1250
1251         def sortbyMenuCallback(self, choice):
1252                 if choice is None:
1253                         return
1254                 self.sortBy(int(choice[1]))
1255                 self["movie_sort"].setPixmapNum(int(choice[1])-1)
1256
1257         def getTagDescription(self, tag):
1258                 # TODO: access the tag database
1259                 return tag
1260
1261         def updateTags(self):
1262                 # get a list of tags available in this list
1263                 self.tags = self["list"].tags
1264
1265         def setListType(self, type):
1266                 self["list"].setListType(type)
1267
1268         def setDescriptionState(self, val):
1269                 self["list"].setDescriptionState(val)
1270
1271         def setSortType(self, type):
1272                 self["list"].setSortType(type)
1273
1274         def setCurrentRef(self, path):
1275                 self.current_ref = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + path)
1276                 # Magic: this sets extra things to show
1277                 self.current_ref.setName('16384:jpg 16384:png 16384:gif 16384:bmp')
1278
1279         def reloadList(self, sel = None, home = False):
1280                 self.reload_sel = sel
1281                 self.reload_home = home
1282                 self["waitingtext"].visible = True
1283                 self.pathselectEnabled = False
1284                 self.callLater(self.reloadWithDelay)
1285
1286         def reloadWithDelay(self):
1287                 if not os.path.isdir(config.movielist.last_videodir.value):
1288                         path = defaultMoviePath()
1289                         config.movielist.last_videodir.value = path
1290                         config.movielist.last_videodir.save()
1291                         self.setCurrentRef(path)
1292                         self["freeDiskSpace"].path = path
1293                 if self.reload_sel is None:
1294                         self.reload_sel = self.getCurrent()
1295                 self["list"].reload(self.current_ref, self.selected_tags)
1296                 self.updateTags()
1297                 title = _("Recorded files...")
1298                 if config.usage.setup_level.index >= 2: # expert+
1299                         title += "  " + config.movielist.last_videodir.value
1300                 if self.selected_tags is not None:
1301                         title += " - " + ','.join(self.selected_tags)
1302                 self.setTitle(title)
1303                 self.displayMovieOffStatus()
1304                 self.displaySortStatus()
1305                 if not (self.reload_sel and self["list"].moveTo(self.reload_sel)):
1306                         if self.reload_home:
1307                                 self["list"].moveToFirstMovie()
1308                 self["freeDiskSpace"].update()
1309                 self["waitingtext"].visible = False
1310                 self.createPlaylist()
1311                 if self.playGoTo:
1312                         if self.isItemPlayable(self.list.getCurrentIndex() + 1):
1313                                 if self.playGoTo > 0:
1314                                         self.list.moveDown()
1315                                 else:
1316                                         self.list.moveUp()
1317                                 self.playGoTo = None
1318                                 self.callLater(self.preview)
1319                 self.callLater(self.enablePathSelect)
1320
1321         def enablePathSelect(self):
1322                 self.pathselectEnabled = True
1323
1324         def doPathSelect(self):
1325                 if self.pathselectEnabled:
1326                         self.session.openWithCallback(
1327                                 self.gotFilename,
1328                                 MovieLocationBox,
1329                                 _("Please select the movie path..."),
1330                                 config.movielist.last_videodir.value
1331                         )
1332
1333         def gotFilename(self, res, selItem = None):
1334                 if not res:
1335                         return
1336                 # serviceref must end with /
1337                 if not res.endswith('/'):
1338                         res += '/'
1339                 currentDir = config.movielist.last_videodir.value
1340                 if res != currentDir:
1341                         if os.path.isdir(res):
1342                                 config.movielist.last_videodir.value = res
1343                                 config.movielist.last_videodir.save()
1344                                 self.loadLocalSettings()
1345                                 self.setCurrentRef(res)
1346                                 self["freeDiskSpace"].path = res
1347                                 if selItem:
1348                                         self.reloadList(home = True, sel = selItem)
1349                                 else:
1350                                         self.reloadList(home = True, sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + currentDir))
1351                         else:
1352                                 self.session.open(
1353                                         MessageBox,
1354                                         _("Directory %s does not exist.") % (res),
1355                                         type = MessageBox.TYPE_ERROR,
1356                                         timeout = 5
1357                                         )
1358
1359         def showAll(self):
1360                 self.selected_tags_ele = None
1361                 self.selected_tags = None
1362                 self.reloadList(home = True)
1363
1364         def showTagsN(self, tagele):
1365                 if not self.tags:
1366                         self.showTagWarning()
1367                 elif not tagele or (self.selected_tags and tagele.value in self.selected_tags) or not tagele.value in self.tags:
1368                         self.showTagsMenu(tagele)
1369                 else:
1370                         self.selected_tags_ele = tagele
1371                         self.selected_tags = self.tags[tagele.value]
1372                         self.reloadList(home = True)
1373
1374         def showTagsFirst(self):
1375                 self.showTagsN(config.movielist.first_tags)
1376
1377         def showTagsSecond(self):
1378                 self.showTagsN(config.movielist.second_tags)
1379
1380         def can_tags(self, item):
1381                 return self.tags
1382         def do_tags(self):
1383                 self.showTagsN(None)
1384
1385         def tagChosen(self, tag):
1386                 if tag is not None:
1387                         if tag[1] is None: # all
1388                                 self.showAll()
1389                                 return
1390                         # TODO: Some error checking maybe, don't wanna crash on KeyError
1391                         self.selected_tags = self.tags[tag[0]]
1392                         if self.selected_tags_ele:
1393                                 self.selected_tags_ele.value = tag[0]
1394                                 self.selected_tags_ele.save()
1395                         self.reloadList(home = True)
1396
1397         def showTagsMenu(self, tagele):
1398                 self.selected_tags_ele = tagele
1399                 lst = [(_("show all tags"), None)] + [(tag, self.getTagDescription(tag)) for tag in sorted(self.tags)]
1400                 self.session.openWithCallback(self.tagChosen, ChoiceBox, title=_("Please select tag to filter..."), list = lst)
1401
1402         def showTagWarning(self):
1403                 self.session.open(MessageBox, _("No tags are set on these movies."), MessageBox.TYPE_ERROR)
1404
1405         def selectMovieLocation(self, title, callback):
1406                 bookmarks = [("("+_("Other")+"...)", None)]
1407                 buildMovieLocationList(bookmarks)
1408                 self.onMovieSelected = callback
1409                 self.movieSelectTitle = title
1410                 self.session.openWithCallback(self.gotMovieLocation, ChoiceBox, title=title, list = bookmarks)
1411
1412         def gotMovieLocation(self, choice):
1413                 if not choice:
1414                         # cancelled
1415                         self.onMovieSelected(None)
1416                         del self.onMovieSelected
1417                         return
1418                 if isinstance(choice, tuple):
1419                         if choice[1] is None:
1420                                 # Display full browser, which returns string
1421                                 self.session.openWithCallback(
1422                                         self.gotMovieLocation,
1423                                         MovieLocationBox,
1424                                         self.movieSelectTitle,
1425                                         config.movielist.last_videodir.value
1426                                 )
1427                                 return
1428                         choice = choice[1]
1429                 choice = os.path.normpath(choice)
1430                 self.rememberMovieLocation(choice)
1431                 self.onMovieSelected(choice)
1432                 del self.onMovieSelected
1433
1434         def rememberMovieLocation(self, where):
1435                 if where in last_selected_dest:
1436                         last_selected_dest.remove(where)
1437                 last_selected_dest.insert(0, where)
1438                 if len(last_selected_dest) > 5:
1439                         del last_selected_dest[-1]
1440
1441         def can_bookmarks(self, item):
1442                 return True
1443         def do_bookmarks(self):
1444                 self.selectMovieLocation(title=_("Please select the movie path..."), callback=self.gotFilename)
1445
1446         def can_addbookmark(self, item):
1447                 return True
1448         def exist_bookmark(self):
1449                 path = config.movielist.last_videodir.value
1450                 if path in config.movielist.videodirs.value:
1451                         return True
1452                 return False
1453         def do_addbookmark(self):
1454                 path = config.movielist.last_videodir.value
1455                 if path in config.movielist.videodirs.value:
1456                         if len(path) > 40:
1457                                 path = '...' + path[-40:]
1458                         self.session.openWithCallback(self.removeBookmark, MessageBox, _("Do you really want to remove your bookmark of %s?") % path)
1459                 else:
1460                         config.movielist.videodirs.value += [path]
1461                         config.movielist.videodirs.save()
1462         def removeBookmark(self, yes):
1463                 if not yes:
1464                         return
1465                 path = config.movielist.last_videodir.value
1466                 bookmarks = config.movielist.videodirs.value
1467                 bookmarks.remove(path)
1468                 config.movielist.videodirs.value = bookmarks
1469                 config.movielist.videodirs.save()
1470
1471         def can_createdir(self, item):
1472                 return True
1473         def do_createdir(self):
1474                 from Screens.VirtualKeyBoard import VirtualKeyBoard
1475                 self.session.openWithCallback(self.createDirCallback, VirtualKeyBoard,
1476                         title = _("Please enter name of the new directory"),
1477                         text = "")
1478         def createDirCallback(self, name):
1479                 if not name:
1480                         return
1481                 msg = None
1482                 try:
1483                         path = os.path.join(config.movielist.last_videodir.value, name)
1484                         os.mkdir(path)
1485                         if not path.endswith('/'):
1486                                 path += '/'
1487                         self.reloadList(sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + path))
1488                 except OSError, e:
1489                         print "Error %s:" % e.errno, e
1490                         if e.errno == 17:
1491                                 msg = _("The path %s already exists.") % name
1492                         else:
1493                                 msg = _("Error") + '\n' + str(e)
1494                 except Exception, e:
1495                         print "[ML] Unexpected error:", e
1496                         msg = _("Error") + '\n' + str(e)
1497                 if msg:
1498                         self.session.open(MessageBox, msg, type = MessageBox.TYPE_ERROR, timeout = 5)
1499
1500         def can_rename(self, item):
1501                 return canMove(item)
1502         def do_rename(self):
1503                 item = self.getCurrentSelection()
1504                 if not canMove(item):
1505                         return
1506                 if isFolder(item):
1507                         p = os.path.split(item[0].getPath())
1508                         if not p[1]:
1509                                 # if path ends in '/', p is blank.
1510                                 p = os.path.split(p[0])
1511                         name = p[1]
1512                 else:
1513                         info = item[1]
1514                         name = info.getName(item[0])
1515                 from Screens.VirtualKeyBoard import VirtualKeyBoard
1516                 self.session.openWithCallback(self.renameCallback, VirtualKeyBoard,
1517                         title = _("Rename"),
1518                         text = name)
1519
1520         def do_decode(self):
1521                 from ServiceReference import ServiceReference
1522                 item = self.getCurrentSelection()
1523                 info = item[1]
1524                 serviceref = ServiceReference(None, reftype = eServiceReference.idDVB, path = item[0].getPath())
1525                 name = info.getName(item[0]) + ' - decoded'
1526                 description = info.getInfoString(item[0], iServiceInformation.sDescription)
1527                 recording = RecordTimer.RecordTimerEntry(serviceref, int(time.time()), int(time.time()) + 3600, name, description, 0, dirname = preferredTimerPath())
1528                 recording.dontSave = True
1529                 recording.autoincrease = True
1530                 recording.setAutoincreaseEnd()
1531                 self.session.nav.RecordTimer.record(recording, ignoreTSC = True)
1532
1533         def renameCallback(self, name):
1534                 if not name:
1535                         return
1536                 name = name.strip()
1537                 item = self.getCurrentSelection()
1538                 if item and item[0]:
1539                         try:
1540                                 path = item[0].getPath().rstrip('/')
1541                                 meta = path + '.meta'
1542                                 if os.path.isfile(meta):
1543                                         metafile = open(meta, "r+")
1544                                         sid = metafile.readline()
1545                                         oldtitle = metafile.readline()
1546                                         rest = metafile.read()
1547                                         metafile.seek(0)
1548                                         metafile.write("%s%s\n%s" %(sid, name, rest))
1549                                         metafile.truncate()
1550                                         metafile.close()
1551                                         index = self.list.getCurrentIndex()
1552                                         info = self.list.list[index]
1553                                         if hasattr(info[3], 'txt'):
1554                                                 info[3].txt = name
1555                                         else:
1556                                                 self.list.invalidateCurrentItem()
1557                                         return
1558                                 pathname,filename = os.path.split(path)
1559                                 newpath = os.path.join(pathname, name)
1560                                 msg = None
1561                                 print "[ML] rename", path, "to", newpath
1562                                 os.rename(path, newpath)
1563                                 self.reloadList(sel = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + newpath))
1564                         except OSError, e:
1565                                 print "Error %s:" % e.errno, e
1566                                 if e.errno == 17:
1567                                         msg = _("The path %s already exists.") % name
1568                                 else:
1569                                         msg = _("Error") + '\n' + str(e)
1570                         except Exception, e:
1571                                 import traceback
1572                                 print "[ML] Unexpected error:", e
1573                                 traceback.print_exc()
1574                                 msg = _("Error") + '\n' + str(e)
1575                         if msg:
1576                                 self.session.open(MessageBox, msg, type = MessageBox.TYPE_ERROR, timeout = 5)
1577
1578         def do_reset(self):
1579                 current = self.getCurrent()
1580                 if current:
1581                         resetMoviePlayState(current.getPath() + ".cuts", current)
1582                         self["list"].invalidateCurrentItem() # trigger repaint
1583
1584         def do_move(self):
1585                 item = self.getCurrentSelection()
1586                 if canMove(item):
1587                         current = item[0]
1588                         info = item[1]
1589                         if info is None:
1590                                 # Special case
1591                                 return
1592                         name = info and info.getName(current) or _("this recording")
1593                         path = os.path.normpath(current.getPath())
1594                         # show a more limited list of destinations, no point
1595                         # in showing mountpoints.
1596                         title = _("Select destination for:") + " " + name
1597                         bookmarks = [("("+_("Other")+"...)", None)]
1598                         inlist = []
1599                         # Subdirs
1600                         try:
1601                                 base = os.path.split(path)[0]
1602                                 for fn in os.listdir(base):
1603                                         if not fn.startswith('.'): # Skip hidden things
1604                                                 d = os.path.join(base, fn)
1605                                                 if os.path.isdir(d) and (d not in inlist):
1606                                                         bookmarks.append((fn,d))
1607                                                         inlist.append(d)
1608                         except Exception, e :
1609                                 print "[MovieSelection]", e
1610                         # Last favourites
1611                         for d in last_selected_dest:
1612                                 if d not in inlist:
1613                                         bookmarks.append((d,d))
1614                         # Other favourites
1615                         for d in config.movielist.videodirs.value:
1616                                 d = os.path.normpath(d)
1617                                 bookmarks.append((d,d))
1618                                 inlist.append(d)
1619                         for p in Components.Harddisk.harddiskmanager.getMountedPartitions():
1620                                 d = os.path.normpath(p.mountpoint)
1621                                 if d not in inlist:
1622                                         bookmarks.append((p.description, d))
1623                                         inlist.append(d)
1624                         self.onMovieSelected = self.gotMoveMovieDest
1625                         self.movieSelectTitle = title
1626                         self.session.openWithCallback(self.gotMovieLocation, ChoiceBox, title=title, list=bookmarks)
1627
1628         def gotMoveMovieDest(self, choice):
1629                 if not choice:
1630                         return
1631                 dest = os.path.normpath(choice)
1632                 try:
1633                         item = self.getCurrentSelection()
1634                         current = item[0]
1635                         if item[1] is None:
1636                                 name = None
1637                         else:
1638                                 name = item[1].getName(current)
1639                         moveServiceFiles(current, dest, name)
1640                         self["list"].removeService(current)
1641                 except Exception, e:
1642                         self.session.open(MessageBox, str(e), MessageBox.TYPE_ERROR)
1643
1644         def can_copy(self, item):
1645                 return canCopy(item)
1646         def do_copy(self):
1647                 item = self.getCurrentSelection()
1648                 if canMove(item):
1649                         current = item[0]
1650                         info = item[1]
1651                         if info is None:
1652                                 # Special case
1653                                 return
1654                         name = info and info.getName(current) or _("this recording")
1655                         self.selectMovieLocation(title=_("Select copy destination for:") + " " + name, callback=self.gotCopyMovieDest)
1656
1657         def gotCopyMovieDest(self, choice):
1658                 if not choice:
1659                         return
1660                 dest = os.path.normpath(choice)
1661                 try:
1662                         item = self.getCurrentSelection()
1663                         current = item[0]
1664                         if item[1] is None:
1665                                 name = None
1666                         else:
1667                                 name = item[1].getName(current)
1668                         copyServiceFiles(current, dest, name)
1669                 except Exception, e:
1670                         self.session.open(MessageBox, str(e), MessageBox.TYPE_ERROR)
1671
1672         def stopTimer(self, timer):
1673                 if timer.isRunning():
1674                         if timer.repeated:
1675                                 timer.enable()
1676                                 timer.processRepeated(findRunningEvent = False)
1677                                 self.session.nav.RecordTimer.doActivate(timer)
1678                         else:
1679                                 timer.afterEvent = RecordTimer.AFTEREVENT.NONE
1680                                 NavigationInstance.instance.RecordTimer.removeEntry(timer)
1681
1682         def onTimerChoice(self, choice):
1683                 if isinstance(choice, tuple) and choice[1]:
1684                         choice, timer = choice[1]
1685                         if not choice:
1686                                 # cancel
1687                                 return
1688                         if "s" in choice:
1689                                 self.stopTimer(timer)
1690                         if "d" in choice:
1691                                 self.delete(True)
1692
1693         def do_delete(self):
1694                 self.delete()
1695
1696         def delete(self, *args):
1697                 if args and (not args[0]):
1698                         # cancelled by user (passing any arg means it's a dialog return)
1699                         return
1700                 item = self.getCurrentSelection()
1701                 if not canDelete(item):
1702                         if item and isTrashFolder(item[0]):
1703                                 # Red button to empty trashcan...
1704                                 self.purgeAll()
1705                         return
1706                 current = item[0]
1707                 info = item[1]
1708                 cur_path = os.path.realpath(current.getPath())
1709                 st = os.stat(cur_path)
1710                 name = info and info.getName(current) or _("this recording")
1711                 are_you_sure = _("Do you really want to delete %s?") % (name)
1712                 if current.flags & eServiceReference.mustDescent:
1713                         files = 0
1714                         subdirs = 0
1715                         if args:
1716                                 # already confirmed...
1717                                 # but not implemented yet...
1718                                 msg = ''
1719                                 if config.usage.movielist_trashcan.value:
1720                                         try:
1721                                                 # Move the files to the trash can in a way that their CTIME is
1722                                                 # set to "now". A simple move would not correctly update the
1723                                                 # ctime, and hence trigger a very early purge.
1724                                                 trash = Tools.Trashcan.createTrashFolder(cur_path)
1725                                                 trash = os.path.join(trash, os.path.split(cur_path)[1])
1726                                                 os.mkdir(trash)
1727                                                 for root, dirnames, filenames in os.walk(cur_path):
1728                                                         trashroot = os.path.join(trash, root[len(cur_path)+1:])
1729                                                         for fn in filenames:
1730                                                                 print "Move %s -> %s" % (os.path.join(root, fn), os.path.join(trashroot, fn))
1731                                                                 os.rename(os.path.join(root, fn), os.path.join(trashroot, fn))
1732                                                         for dn in dirnames:
1733                                                                 print "MkDir", os.path.join(trashroot, dn)
1734                                                                 os.mkdir(os.path.join(trashroot, dn))
1735                                                 # second pass to remove the empty directories
1736                                                 for root, dirnames, filenames in os.walk(cur_path, topdown=False):
1737                                                         for dn in dirnames:
1738                                                                 print "rmdir", os.path.join(trashroot, dn)
1739                                                                 os.rmdir(os.path.join(root, dn))
1740                                                 os.rmdir(cur_path)
1741                                                 self["list"].removeService(current)
1742                                                 self.showActionFeedback(_("Deleted") + " " + name)
1743                                                 # Files were moved to .Trash, ok.
1744                                                 return
1745                                         except OSError, e:
1746                                                 print "[MovieSelection] Cannot move to trash", e
1747                                                 if e.errno == 18:
1748                                                         # This occurs when moving across devices
1749                                                         msg = _("Cannot move files on a different disk or system to the trash can") + ". "
1750                                                 else:
1751                                                         msg = _("Cannot move to trash can") + ".\n" + str(e) + "\n"
1752                                         except Exception, e:
1753                                                 print "[MovieSelection] Weird error moving to trash", e
1754                                                 # Failed to create trash or move files.
1755                                                 msg = _("Cannot move to trash can") + "\n" + str(e) + "\n"
1756                                 msg += _("Sorry, deleting directories can (for now) only be done through the trash can.")
1757                                 self.session.open(MessageBox, msg, MessageBox.TYPE_ERROR)
1758                                 return
1759                         for fn in os.listdir(cur_path):
1760                                 if (fn != '.') and (fn != '..'):
1761                                         ffn = os.path.join(cur_path, fn)
1762                                         if os.path.isdir(ffn):
1763                                                 subdirs += 1
1764                                         else:
1765                                                 files += 1
1766                         if files or subdirs:
1767                                 msg = _("Directory contains %s and %s.") % (ngettext("%d file", "%d files", files) % files, ngettext("%d subdirectory", "%d subdirectories", subdirs) % subdirs) + '\n' + are_you_sure
1768                                 if isInTrashFolder(current):
1769                                         # Red button to empty trashcan item or subdir
1770                                         msg = _("Deleted items") + "\n" + msg
1771                                         callback = self.purgeConfirmed
1772                                 else:
1773                                         callback = self.delete
1774                                 self.session.openWithCallback(callback, MessageBox, msg)
1775                                 return
1776                         else:
1777                                 try:
1778                                         os.rmdir(cur_path)
1779                                 except Exception, e:
1780                                         print "[MovieSelection] Failed delete", e
1781                                         self.session.open(MessageBox, _("Delete failed!") + "\n" + str(e), MessageBox.TYPE_ERROR)
1782                                 else:
1783                                         self["list"].removeService(current)
1784                                         self.showActionFeedback(_("Deleted") + " " + name)
1785                 else:
1786                         if not args:
1787                                 rec_filename = os.path.split(current.getPath())[1]
1788                                 if rec_filename.endswith(".ts"): rec_filename = rec_filename[:-3]
1789                                 for timer in NavigationInstance.instance.RecordTimer.timer_list:
1790                                         if timer.isRunning() and not timer.justplay and rec_filename in timer.Filename:
1791                                                 choices = [
1792                                                         (_("Cancel"), None),
1793                                                         (_("Stop recording"), ("s", timer)),
1794                                                         (_("Stop recording and delete"), ("sd", timer))]
1795                                                 self.session.openWithCallback(self.onTimerChoice, ChoiceBox, title=_("Recording in progress") + ":\n%s" % name, list=choices)
1796                                                 return
1797                                 if time.time() - st.st_mtime < 5:
1798                                         if not args:
1799                                                 self.session.openWithCallback(self.delete, MessageBox, _("File appears to be busy.\n") + are_you_sure)
1800                                                 return
1801                         if config.usage.movielist_trashcan.value:
1802                                 try:
1803                                         trash = Tools.Trashcan.createTrashFolder(cur_path)
1804                                         # Also check whether we're INSIDE the trash, then it's a purge.
1805                                         if cur_path.startswith(trash):
1806                                                 msg = _("Deleted items") + "\n"
1807                                         else:
1808                                                 moveServiceFiles(current, trash, name, allowCopy=False)
1809                                                 self["list"].removeService(current)
1810                                                 # Files were moved to .Trash, ok.
1811                                                 from Screens.InfoBarGenerics import delResumePoint
1812                                                 delResumePoint(current)
1813                                                 self.showActionFeedback(_("Deleted") + " " + name)
1814                                                 return
1815                                 except OSError, e:
1816                                         print "[MovieSelection] Cannot move to trash", e
1817                                         if e.errno == 18:
1818                                                 # This occurs when moving across devices
1819                                                 msg = _("Cannot move files on a different disk or system to the trash can") + ". "
1820                                         else:
1821                                                 msg = _("Cannot move to trash can") + ".\n" + str(e) + "\n"
1822                                 except Exception, e:
1823                                         print "[MovieSelection] Weird error moving to trash", e
1824                                         # Failed to create trash or move files.
1825                                         msg = _("Cannot move to trash can") + "\n" + str(e) + "\n"
1826                         else:
1827                                 msg = ''
1828                         self.session.openWithCallback(self.deleteConfirmed, MessageBox, msg + are_you_sure)
1829
1830         def deleteConfirmed(self, confirmed):
1831                 if not confirmed:
1832                         return
1833                 item = self.getCurrentSelection()
1834                 if item is None:
1835                         return # huh?
1836                 current = item[0]
1837                 info = item[1]
1838                 name = info and info.getName(current) or _("this recording")
1839                 serviceHandler = eServiceCenter.getInstance()
1840                 offline = serviceHandler.offlineOperations(current)
1841                 try:
1842                         if offline is None:
1843                                 from enigma import eBackgroundFileEraser
1844                                 eBackgroundFileEraser.getInstance().erase(os.path.realpath(current.getPath()))
1845                         else:
1846                                 if offline.deleteFromDisk(0):
1847                                         raise Exception, "Offline delete failed"
1848                         self["list"].removeService(current)
1849                         from Screens.InfoBarGenerics import delResumePoint
1850                         delResumePoint(current)
1851                         self.showActionFeedback(_("Deleted") + " " + name)
1852                 except Exception, ex:
1853                         self.session.open(MessageBox, _("Delete failed!") + "\n" + name + "\n" + str(ex), MessageBox.TYPE_ERROR)
1854
1855
1856         def purgeAll(self):
1857                 recordings = self.session.nav.getRecordings()
1858                 next_rec_time = -1
1859                 msg = _("Permanently delete all recordings in the trash can?")
1860                 if not recordings:
1861                         next_rec_time = self.session.nav.RecordTimer.getNextRecordingTime()
1862                 if recordings or (next_rec_time > 0 and (next_rec_time - time.time()) < 120):
1863                         msg += "\n" + _("Recording(s) are in progress or coming up in few seconds!")
1864                 self.session.openWithCallback(self.purgeConfirmed, MessageBox, msg)
1865
1866         def purgeConfirmed(self, confirmed):
1867                 if not confirmed:
1868                         return
1869                 item = self.getCurrentSelection()
1870                 current = item[0]
1871                 cur_path = os.path.realpath(current.getPath())
1872                 Tools.Trashcan.cleanAll(cur_path)
1873
1874         def showNetworkSetup(self):
1875                 import NetworkSetup
1876                 self.session.open(NetworkSetup.NetworkAdapterSelection)
1877
1878         def showActionFeedback(self, text):
1879                 if self.feedbackTimer is None:
1880                         self.feedbackTimer = eTimer()
1881                         self.feedbackTimer.callback.append(self.hideActionFeedback)
1882                 else:
1883                         self.feedbackTimer.stop()
1884                 self.feedbackTimer.start(3000, 1)
1885                 self.diskinfo.setText(text)
1886
1887         def hideActionFeedback(self):
1888                 print "[ML] hide feedback"
1889                 self.diskinfo.update()
1890
1891         def can_gohome(self, item):
1892                 return True
1893
1894         def do_gohome(self):
1895                 self.gotFilename(defaultMoviePath())
1896
1897         def do_sort(self):
1898                 index = 0
1899                 for index, item in enumerate(l_moviesort):
1900                         if int(item[0]) == int(config.movielist.moviesort.value):
1901                                 break
1902                 if index >= len(l_moviesort) - 1:
1903                         index = 0
1904                 else:
1905                         index += 1
1906                 #descriptions in native languages too long...
1907                 sorttext = l_moviesort[index][2]
1908                 if config.movielist.btn_red.value == "sort": self['key_red'].setText(sorttext)
1909                 if config.movielist.btn_green.value == "sort": self['key_green'].setText(sorttext)
1910                 if config.movielist.btn_yellow.value == "sort": self['key_yellow'].setText(sorttext)
1911                 if config.movielist.btn_blue.value == "sort": self['key_blue'].setText(sorttext)
1912                 self.sorttimer = eTimer()
1913                 self.sorttimer.callback.append(self._updateButtonTexts)
1914                 self.sorttimer.start(1500, True) #time for displaying sorting type just applied
1915                 self.sortBy(int(l_moviesort[index][0]))
1916                 self["movie_sort"].setPixmapNum(int(l_moviesort[index][0])-1)
1917
1918         def do_listtype(self):
1919                 index = 0
1920                 for index, item in enumerate(l_listtype):
1921                         if int(item[0]) == int(config.movielist.listtype.value):
1922                                 break
1923                 if index >= len(l_listtype) - 1:
1924                         index = 0
1925                 else:
1926                         index += 1
1927                 self.listType(int(l_listtype[index][0]))
1928
1929         def do_preview(self):
1930                 self.preview()
1931
1932         def displaySortStatus(self):
1933                 self["movie_sort"].setPixmapNum(int(config.movielist.moviesort.value)-1)
1934                 self["movie_sort"].show()
1935
1936         def can_movieoff(self, item):
1937                 return True
1938
1939         def do_movieoff(self):
1940                 self.setNextMovieOffStatus()
1941                 self.displayMovieOffStatus()
1942
1943         def displayMovieOffStatus(self):
1944                 self["movie_off"].setPixmapNum(config.usage.on_movie_eof.getIndex())
1945                 self["movie_off"].show()
1946
1947         def setNextMovieOffStatus(self):
1948                 config.usage.on_movie_eof.selectNext()
1949                 self.settings["movieoff"] = config.usage.on_movie_eof.value
1950                 self.saveLocalSettings()
1951
1952         def can_movieoff_menu(self, item):
1953                 return True
1954
1955         def do_movieoff_menu(self):
1956                 current_movie_eof = config.usage.on_movie_eof.value
1957                 menu = []
1958                 for x in config.usage.on_movie_eof.choices:
1959                         config.usage.on_movie_eof.value = x
1960                         menu.append((config.usage.on_movie_eof.getText(), x))
1961                 config.usage.on_movie_eof.value = current_movie_eof
1962                 used = config.usage.on_movie_eof.getIndex()
1963                 self.session.openWithCallback(self.movieoffMenuCallback, ChoiceBox, title = _("On end of movie"), list = menu, selection = used)
1964
1965         def movieoffMenuCallback(self, choice):
1966                 if choice is None:
1967                         return
1968                 self.settings["movieoff"] = choice[1]
1969                 self.saveLocalSettings()
1970                 self.displayMovieOffStatus()
1971
1972         def createPlaylist(self):
1973                 global playlist
1974                 items = playlist
1975                 del items[:]
1976                 for index, item in enumerate(self["list"]):
1977                         if item:
1978                                 item = item[0]
1979                                 path = item.getPath()
1980                                 if not item.flags & eServiceReference.mustDescent:
1981                                         ext = os.path.splitext(path)[1].lower()
1982                                         if ext in IMAGE_EXTENSIONS:
1983                                                 continue
1984                                         else:
1985                                                 items.append(item)
1986
1987 playlist = []