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