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