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