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