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