add support for markers in bouquets
[enigma2.git] / lib / python / Screens / ChannelSelection.py
1 from Screen import Screen
2 from Components.Button import Button
3 from Components.ServiceList import ServiceList
4 from Components.ActionMap import NumberActionMap, ActionMap
5 from Components.MenuList import MenuList
6 from EpgSelection import EPGSelection
7 from enigma import eServiceReference, eEPGCache, eEPGCachePtr, eServiceCenter, eServiceCenterPtr, iMutableServiceListPtr, iStaticServiceInformationPtr, eTimer, eDVBDB
8 from Components.config import config, configElement, ConfigSubsection, configText, currentConfigSelectionElement
9 from Screens.FixedMenu import FixedMenu
10 from Tools.NumericalTextInput import NumericalTextInput
11 from Components.NimManager import nimmanager
12 from Components.Sources.Clock import Clock
13 from Components.Input import Input
14 from Screens.InputBox import InputBox
15 from ServiceReference import ServiceReference
16 from re import *
17 from os import remove
18
19 FLAG_SERVICE_NEW_FOUND = 64 #define in lib/dvb/idvb.h as dxNewFound = 64
20
21 import xml.dom.minidom
22
23 class BouquetSelector(Screen):
24         def __init__(self, session, bouquets, selectedFunc, enableWrapAround=False):
25                 Screen.__init__(self, session)
26
27                 self.selectedFunc=selectedFunc
28
29                 self["actions"] = ActionMap(["OkCancelActions"],
30                         {
31                                 "ok": self.okbuttonClick,
32                                 "cancel": self.cancelClick
33                         })
34                 entrys = [ ]
35                 for x in bouquets:
36                         entrys.append((x[0], x[1]))
37                 self["menu"] = MenuList(entrys, enableWrapAround)
38
39         def getCurrent(self):
40                 cur = self["menu"].getCurrent()
41                 return cur and cur[1]
42
43         def okbuttonClick(self):
44                 self.selectedFunc(self.getCurrent())
45
46         def up(self):
47                 self["menu"].up()
48
49         def down(self):
50                 self["menu"].down()
51
52         def cancelClick(self):
53                 self.close(False)
54
55 class ChannelContextMenu(Screen):
56         def __init__(self, session, csel):
57                 Screen.__init__(self, session)
58                 self.csel = csel
59                 self.bsel = None
60
61                 self["actions"] = ActionMap(["OkCancelActions"],
62                         {
63                                 "ok": self.okbuttonClick,
64                                 "cancel": self.cancelClick
65                         })
66                 menu = [ ]
67
68                 current_root = csel.getRoot()
69                 current_sel_path = csel.getCurrentSelection().getPath()
70                 current_sel_flags = csel.getCurrentSelection().flags
71                 inBouquetRootList = current_root and current_root.getPath().find('FROM BOUQUET "bouquets.') != -1 #FIXME HACK
72                 inBouquet = csel.getMutableList() is not None
73                 haveBouquets = csel.bouquet_root.getPath().find('FROM BOUQUET "bouquets.') != -1
74
75                 if not csel.bouquet_mark_edit and not csel.movemode:
76                         if not inBouquetRootList:
77                                 if (csel.getCurrentSelection().flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
78                                         if haveBouquets:
79                                                 menu.append((_("add service to bouquet"), self.addServiceToBouquetSelected))
80                                         else:
81                                                 menu.append((_("add service to favourites"), self.addServiceToBouquetSelected))
82                                 else:
83                                         if haveBouquets:
84                                                 if not inBouquet and current_sel_path.find("PROVIDERS") == -1:
85                                                         menu.append((_("copy to favourites"), self.copyCurrentToBouquetList))
86                                         if current_sel_path.find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
87                                                 menu.append((_("remove all new found flags"), self.removeAllNewFoundFlags))
88                                 if inBouquet:
89                                         menu.append((_("remove entry"), self.removeCurrentService))
90                                 if current_root.getPath().find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
91                                         menu.append((_("remove new found flag"), self.removeNewFoundFlag))
92                         else:
93                                         menu.append((_("add bouquet"), self.showBouquetInputBox))
94                                         menu.append((_("remove entry"), self.removeBouquet))
95
96                 if inBouquet: # current list is editable?
97                         if not csel.bouquet_mark_edit:
98                                 if not csel.movemode:
99                                         menu.append((_("add marker"), self.showMarkerInputBox))
100                                         menu.append((_("enable move mode"), self.toggleMoveMode))
101                                         if not inBouquetRootList:
102                                                 if haveBouquets:
103                                                         menu.append((_("enable bouquet edit"), self.bouquetMarkStart))
104                                                 else:
105                                                         menu.append((_("enable favourite edit"), self.bouquetMarkStart))
106                                 else:
107                                         menu.append((_("disable move mode"), self.toggleMoveMode))
108                         elif not inBouquetRootList:
109                                 if haveBouquets:
110                                         menu.append((_("end bouquet edit"), self.bouquetMarkEnd))
111                                         menu.append((_("abort bouquet edit"), self.bouquetMarkAbort))
112                                 else:
113                                         menu.append((_("end favourites edit"), self.bouquetMarkEnd))
114                                         menu.append((_("abort favourites edit"), self.bouquetMarkAbort))
115
116                 menu.append((_("back"), self.cancelClick))
117                 self["menu"] = MenuList(menu)
118
119         def okbuttonClick(self):
120                 self["menu"].getCurrent()[1]()
121
122         def cancelClick(self):
123                 self.close(False)
124                 
125         def showBouquetInputBox(self):
126                 self.session.openWithCallback(self.bouquetInputCallback, InputBox, title=_("Please enter a name for the new bouquet"), text="bouquetname", maxSize=False, type=Input.TEXT)
127
128         def bouquetInputCallback(self, bouquet):
129                 if bouquet is not None:
130                         self.csel.addBouquet(bouquet, None)
131                 self.close()
132
133         def addServiceToBouquetSelected(self):
134                 bouquets = self.csel.getBouquetList()
135                 if bouquets is None:
136                         cnt = 0
137                 else:
138                         cnt = len(bouquets)
139                 if cnt > 1: # show bouquet list
140                         self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, bouquets, self.addCurrentServiceToBouquet)
141                 elif cnt == 1: # add to only one existing bouquet
142                         self.addCurrentServiceToBouquet(bouquets[0][1])
143                 else: #no bouquets in root.. so assume only one favourite list is used
144                         self.addCurrentServiceToBouquet(self.csel.bouquet_root)
145
146         def bouquetSelClosed(self, recursive):
147                 self.bsel = None
148                 if recursive:
149                         self.close(False)
150
151         def copyCurrentToBouquetList(self):
152                 self.csel.copyCurrentToBouquetList()
153                 self.close()
154
155         def removeBouquet(self):
156                 self.csel.removeBouquet()
157                 self.close()
158
159         def showMarkerInputBox(self):
160                 self.session.openWithCallback(self.markerInputCallback, InputBox, title=_("Please enter a name for the new marker"), text="markername", maxSize=False, type=Input.TEXT)
161
162         def markerInputCallback(self, marker):
163                 if marker is not None:
164                         self.csel.addMarker(marker)
165                 self.close()
166
167         def addCurrentServiceToBouquet(self, dest):
168                 self.csel.addCurrentServiceToBouquet(dest)
169                 if self.bsel is not None:
170                         self.bsel.close(True)
171                 else:
172                         self.close(True) # close bouquet selection
173
174         def removeCurrentService(self):
175                 self.csel.removeCurrentService()
176                 self.close()
177
178         def toggleMoveMode(self):
179                 self.csel.toggleMoveMode()
180                 self.close()
181
182         def bouquetMarkStart(self):
183                 self.csel.startMarkedEdit()
184                 self.close()
185
186         def bouquetMarkEnd(self):
187                 self.csel.endMarkedEdit(abort=False)
188                 self.close()
189
190         def bouquetMarkAbort(self):
191                 self.csel.endMarkedEdit(abort=True)
192                 self.close()
193
194         def removeNewFoundFlag(self):
195                 eDVBDB.getInstance().removeFlag(self.csel.getCurrentSelection(), FLAG_SERVICE_NEW_FOUND)
196                 self.close()
197
198         def removeAllNewFoundFlags(self):
199                 curpath = self.csel.getCurrentSelection().getPath()
200                 idx = curpath.find("satellitePosition == ")
201                 if idx != -1:
202                         tmp = curpath[idx+21:]
203                         idx = tmp.find(')')
204                         if idx != -1:
205                                 satpos = int(tmp[:idx])
206                                 eDVBDB.getInstance().removeFlags(FLAG_SERVICE_NEW_FOUND, -1, -1, -1, satpos)
207                 self.close()
208
209 class ChannelSelectionEPG:
210         def __init__(self):
211                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
212                         {
213                                 "showEPGList": self.showEPGList,
214                         })
215
216         def showEPGList(self):
217                 ref=self.getCurrentSelection()
218                 ptr=eEPGCache.getInstance()
219                 if ptr.startTimeQuery(ref) != -1:
220                         self.session.open(EPGSelection, ref)
221                 else:
222                         print 'no epg for service', ref.toString()
223
224 class ChannelSelectionEdit:
225         def __init__(self):
226                 self.entry_marked = False
227                 self.movemode = False
228                 self.bouquet_mark_edit = False
229                 self.mutableList = None
230                 self.__marked = [ ]
231                 self.saved_title = None
232                 self.saved_root = None
233
234                 class ChannelSelectionEditActionMap(ActionMap):
235                         def __init__(self, csel, contexts = [ ], actions = { }, prio=0):
236                                 ActionMap.__init__(self, contexts, actions, prio)
237                                 self.csel = csel
238
239                         def action(self, contexts, action):
240                                 if action == "cancel":
241                                         self.csel.handleEditCancel()
242                                         return 0 # fall-trough
243                                 elif action == "ok":
244                                         return 0 # fall-trough
245                                 else:
246                                         return ActionMap.action(self, contexts, action)
247
248                 self["ChannelSelectEditActions"] = ChannelSelectionEditActionMap(self, ["ChannelSelectEditActions", "OkCancelActions"],
249                         {
250                                 "contextMenu": self.doContext,
251                         })
252
253         def getMutableList(self, root=eServiceReference()):
254                 if not self.mutableList is None:
255                         return self.mutableList
256                 serviceHandler = eServiceCenter.getInstance()
257                 if not root.valid():
258                         root=self.getRoot()
259                 list = root and serviceHandler.list(root)
260                 if list is not None:
261                         return list.startEdit()
262                 return None
263
264         def buildBouquetID(self, str):
265                 tmp = str.lower()
266                 name = ''
267                 for c in tmp:
268                         if (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9'):
269                                 name += c
270                         else:
271                                 name += '_'
272                 return name
273
274         def addMarker(self, name):
275                 current = self.servicelist.getCurrent()
276                 mutableList = self.getMutableList()
277                 cnt = 0
278                 while mutableList:
279                         str = '1:64:%d:0:0:0:0:0:0:0::%s'%(cnt, name)
280                         ref = eServiceReference(str)
281                         if current and current.valid():
282                                 if not mutableList.addService(ref, current):
283                                         self.servicelist.addService(ref, True)
284                                         mutableList.flushChanges()
285                                         break
286                         elif not mutableList.addService(ref):
287                                 self.servicelist.addService(ref, True)
288                                 mutableList.flushChanges()
289                                 break
290                         cnt+=1
291
292         def addBouquet(self, bName, services):
293                 serviceHandler = eServiceCenter.getInstance()
294                 mutableBouquetList = serviceHandler.list(self.bouquet_root).startEdit()
295                 if mutableBouquetList:
296                         if self.mode == MODE_TV:
297                                 bName += " (TV)"
298                                 str = '1:7:1:0:0:0:0:0:0:0:(type == 1) FROM BOUQUET \"userbouquet.%s.tv\" ORDER BY bouquet'%(self.buildBouquetID(bName))
299                         else:
300                                 bName += " (Radio)"
301                                 str = '1:7:2:0:0:0:0:0:0:0:(type == 2) FROM BOUQUET \"userbouquet.%s.radio\" ORDER BY bouquet'%(self.buildBouquetID(bName))
302                         new_bouquet_ref = eServiceReference(str)
303                         if not mutableBouquetList.addService(new_bouquet_ref):
304                                 self.bouquetNumOffsetCache = { }
305                                 mutableBouquetList.flushChanges()
306                                 eDVBDB.getInstance().reloadBouquets()
307                                 mutableBouquet = serviceHandler.list(new_bouquet_ref).startEdit()
308                                 if mutableBouquet:
309                                         mutableBouquet.setListName(bName)
310                                         if services is not None:
311                                                 for service in services:
312                                                         if mutableBouquet.addService(service):
313                                                                 print "add", service.toString(), "to new bouquet failed"
314                                                         else:
315                                                                 current = self.servicelist.getCurrent()
316                                                                 if current and current.toString() == self.bouquet_rootstr:
317                                                                         self.servicelist.addService(service, True)
318                                         mutableBouquet.flushChanges()
319                                 else:
320                                         print "get mutable list for new created bouquet failed"
321                         else:
322                                 print "add", str, "to bouquets failed"
323                 else:
324                         print "bouquetlist is not editable"
325
326         def copyCurrentToBouquetList(self):
327                 provider = ServiceReference(self.getCurrentSelection())
328                 providerName = provider.getServiceName()
329                 serviceHandler = eServiceCenter.getInstance()
330                 services = serviceHandler.list(provider.ref)
331                 self.addBouquet(providerName, services and services.getContent('R', True))
332
333         def removeBouquet(self):
334                 refstr = self.getCurrentSelection().toString()
335                 self.bouquetNumOffsetCache = { }
336                 pos = refstr.find('FROM BOUQUET "')
337                 if pos != -1:
338                         refstr = refstr[pos+14:]
339                         pos = refstr.find('"')
340                         if pos != -1:
341                                 filename = '/etc/enigma2/' + refstr[:pos] # FIXMEEE !!! HARDCODED /etc/enigma2
342                 self.removeCurrentService()
343                 try:
344                         remove(filename)
345                 except OSError:
346                         print "error during remove of", filename
347
348 #  multiple marked entry stuff ( edit mode, later multiepg selection )
349         def startMarkedEdit(self):
350                 self.mutableList = self.getMutableList()
351                 # add all services from the current list to internal marked set in listboxservicecontent
352                 self.clearMarks() # this clears the internal marked set in the listboxservicecontent
353                 self.saved_title = self.instance.getTitle()
354                 pos = self.saved_title.find(')')
355                 new_title = self.saved_title[:pos+1]
356                 if self.bouquet_root.getPath().find('FROM BOUQUET "bouquets.') != -1:
357                         new_title += ' ' + _("[bouquet edit]")
358                 else:
359                         new_title += ' ' + _("[favourite edit]")
360                 self.setTitle(new_title)
361                 self.bouquet_mark_edit = True
362                 self.__marked = self.servicelist.getRootServices()
363                 for x in self.__marked:
364                         self.servicelist.addMarked(eServiceReference(x))
365                 self.savedPath = self.servicePath[:]
366                 self.showAllServices()
367
368         def endMarkedEdit(self, abort):
369                 if not abort and self.mutableList is not None:
370                         self.bouquetNumOffsetCache = { }
371                         new_marked = set(self.servicelist.getMarked())
372                         old_marked = set(self.__marked)
373                         removed = old_marked - new_marked
374                         added = new_marked - old_marked
375                         changed = False
376                         for x in removed:
377                                 changed = True
378                                 self.mutableList.removeService(eServiceReference(x))
379                         for x in added:
380                                 changed = True
381                                 self.mutableList.addService(eServiceReference(x))
382                         if changed:
383                                 self.mutableList.flushChanges()
384                 self.__marked = []
385                 self.clearMarks()
386                 self.bouquet_mark_edit = False
387                 self.mutableList = None
388                 self.setTitle(self.saved_title)
389                 self.saved_title = None
390                 # self.servicePath is just a reference to servicePathTv or Radio...
391                 # so we never ever do use the asignment operator in self.servicePath
392                 del self.servicePath[:] # remove all elements
393                 self.servicePath += self.savedPath # add saved elements
394                 del self.savedPath
395                 self.setRoot(self.servicePath[len(self.servicePath)-1])
396
397         def clearMarks(self):
398                 self.servicelist.clearMarks()
399
400         def doMark(self):
401                 ref = self.servicelist.getCurrent()
402                 if self.servicelist.isMarked(ref):
403                         self.servicelist.removeMarked(ref)
404                 else:
405                         self.servicelist.addMarked(ref)
406
407         def removeCurrentService(self):
408                 ref = self.servicelist.getCurrent()
409                 mutableList = self.getMutableList()
410                 if ref.valid() and mutableList is not None:
411                         if not mutableList.removeService(ref):
412                                 self.bouquetNumOffsetCache = { }
413                                 mutableList.flushChanges() #FIXME dont flush on each single removed service
414                                 self.servicelist.removeCurrent()
415
416         def addCurrentServiceToBouquet(self, dest):
417                 mutableList = self.getMutableList(dest)
418                 if not mutableList is None:
419                         if not mutableList.addService(self.servicelist.getCurrent()):
420                                 self.bouquetNumOffsetCache = { }
421                                 mutableList.flushChanges()
422
423         def toggleMoveMode(self):
424                 if self.movemode:
425                         if self.entry_marked:
426                                 self.toggleMoveMarked() # unmark current entry
427                         self.movemode = False
428                         self.pathChangedDisabled = False # re-enable path change
429                         self.mutableList.flushChanges() # FIXME add check if changes was made
430                         self.mutableList = None
431                         self.setTitle(self.saved_title)
432                         self.saved_title = None
433                         if self.getRoot() == self.bouquet_root:
434                                 self.bouquetNumOffsetCache = { }
435                 else:
436                         self.mutableList = self.getMutableList()
437                         self.movemode = True
438                         self.pathChangedDisabled = True # no path change allowed in movemode
439                         self.saved_title = self.instance.getTitle()
440                         new_title = self.saved_title
441                         pos = self.saved_title.find(')')
442                         new_title = self.saved_title[:pos+1] + ' ' + _("[move mode]") + self.saved_title[pos+1:]
443                         self.setTitle(new_title);
444
445         def handleEditCancel(self):
446                 if self.movemode: #movemode active?
447                         self.channelSelected() # unmark
448                         self.toggleMoveMode() # disable move mode
449                 elif self.bouquet_mark_edit:
450                         self.endMarkedEdit(True) # abort edit mode
451
452         def toggleMoveMarked(self):
453                 if self.entry_marked:
454                         self.servicelist.setCurrentMarked(False)
455                         self.entry_marked = False
456                 else:
457                         self.servicelist.setCurrentMarked(True)
458                         self.entry_marked = True
459
460         def doContext(self):
461                 self.session.open(ChannelContextMenu, self)
462
463 MODE_TV = 0
464 MODE_RADIO = 1
465
466 class ChannelSelectionBase(Screen):
467         def __init__(self, session):
468                 Screen.__init__(self, session)
469
470                 # this makes it much simple to implement a selectable radio or tv mode :)
471                 self.service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 195) || (type == 25)'
472                 self.service_types_radio = '1:7:2:0:0:0:0:0:0:0:(type == 2)'
473
474                 self["key_red"] = Button(_("All"))
475                 self["key_green"] = Button(_("Satellites"))
476                 self["key_yellow"] = Button(_("Provider"))
477                 self["key_blue"] = Button(_("Favourites"))
478
479                 self["list"] = ServiceList()
480                 self.servicelist = self["list"]
481
482                 self.numericalTextInput = NumericalTextInput()
483
484                 self.servicePathTV = [ ]
485                 self.servicePathRadio = [ ]
486                 self.servicePath = [ ]
487
488                 self.mode = MODE_TV
489
490                 self.pathChangedDisabled = False
491
492                 self.bouquetNumOffsetCache = { }
493
494                 self["ChannelSelectBaseActions"] = NumberActionMap(["ChannelSelectBaseActions", "NumberActions"],
495                         {
496                                 "showFavourites": self.showFavourites,
497                                 "showAllServices": self.showAllServices,
498                                 "showProviders": self.showProviders,
499                                 "showSatellites": self.showSatellites,
500                                 "nextBouquet": self.nextBouquet,
501                                 "prevBouquet": self.prevBouquet,
502                                 "nextMarker": self.nextMarker,
503                                 "prevMarker": self.prevMarker,
504                                 "1": self.keyNumberGlobal,
505                                 "2": self.keyNumberGlobal,
506                                 "3": self.keyNumberGlobal,
507                                 "4": self.keyNumberGlobal,
508                                 "5": self.keyNumberGlobal,
509                                 "6": self.keyNumberGlobal,
510                                 "7": self.keyNumberGlobal,
511                                 "8": self.keyNumberGlobal,
512                                 "9": self.keyNumberGlobal,
513                                 "0": self.keyNumber0
514                         })
515
516         def appendDVBTypes(self, ref):
517                 path = ref.getPath()
518                 pos = path.find(' FROM BOUQUET')
519                 if pos != -1:
520                         return eServiceReference(self.service_types + path[pos:])
521                 return ref
522
523         def getBouquetNumOffset(self, bouquet):
524                 if self.bouquet_root.getPath().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
525                         return 0
526                 bouquet = self.appendDVBTypes(bouquet)
527                 try:
528                         return self.bouquetNumOffsetCache[bouquet.toString()]
529                 except:
530                         offsetCount = 0
531                         serviceHandler = eServiceCenter.getInstance()
532                         bouquetlist = serviceHandler.list(self.bouquet_root)
533                         if not bouquetlist is None:
534                                 while True:
535                                         bouquetIterator = self.appendDVBTypes(bouquetlist.getNext())
536                                         if not bouquetIterator.valid(): #end of list
537                                                 break
538                                         self.bouquetNumOffsetCache[bouquetIterator.toString()]=offsetCount
539                                         if ((bouquetIterator.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory):
540                                                 continue
541                                         servicelist = serviceHandler.list(bouquetIterator)
542                                         if not servicelist is None:
543                                                 while True:
544                                                         serviceIterator = servicelist.getNext()
545                                                         if not serviceIterator.valid(): #check if end of list
546                                                                 break
547                                                         if serviceIterator.flags: #playable services have no flags
548                                                                 continue
549                                                         offsetCount += 1
550                 return self.bouquetNumOffsetCache.get(bouquet.toString(), offsetCount)
551
552         def recallBouquetMode(self):
553                 if self.mode == MODE_TV:
554                         self.service_types = self.service_types_tv
555                         if currentConfigSelectionElement(config.usage.multibouquet) == "yes":
556                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:(type == 1) FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
557                         else:
558                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'%(self.service_types)
559                 else:
560                         self.service_types = self.service_types_radio
561                         if currentConfigSelectionElement(config.usage.multibouquet) == "yes":
562                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:(type == 1) FROM BOUQUET "bouquets.radio" ORDER BY bouquet'
563                         else:
564                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.radio" ORDER BY bouquet'%(self.service_types)
565                 self.bouquet_root = eServiceReference(self.bouquet_rootstr)
566
567         def setTvMode(self):
568                 self.mode = MODE_TV
569                 self.servicePath = self.servicePathTV
570                 self.recallBouquetMode()
571                 title = self.instance.getTitle()
572                 pos = title.find(" (")
573                 if pos != -1:
574                         title = title[:pos]
575                 title += " (TV)"
576                 self.setTitle(title)
577
578         def setRadioMode(self):
579                 self.mode = MODE_RADIO
580                 self.servicePath = self.servicePathRadio
581                 self.recallBouquetMode()
582                 title = self.instance.getTitle()
583                 pos = title.find(" (")
584                 if pos != -1:
585                         title = title[:pos]
586                 title += " (Radio)"
587                 self.setTitle(title)
588
589         def setRoot(self, root, justSet=False):
590                 path = root.getPath()
591                 inBouquetRootList = path.find('FROM BOUQUET "bouquets.') != -1 #FIXME HACK
592                 pos = path.find(' FROM BOUQUET')
593                 isBouquet = pos != -1
594                 if not inBouquetRootList and isBouquet:
595                         self.servicelist.setMode(ServiceList.MODE_FAVOURITES)
596                         self.servicelist.setNumberOffset(self.getBouquetNumOffset(root))
597                         refstr = self.service_types + path[pos:]
598                         root = eServiceReference(refstr)
599                 else:
600                         self.servicelist.setMode(ServiceList.MODE_NORMAL)
601                 self.servicelist.setRoot(root, justSet)
602                 self.buildTitleString()
603
604         def removeModeStr(self, str):
605                 if self.mode == MODE_TV:
606                         pos = str.find(' (TV)')
607                 else:
608                         pos = str.find(' (Radio)')
609                 if pos != -1:
610                         return str[:pos]
611                 return str
612
613         def getServiceName(self, ref):
614                 str = self.removeModeStr(ServiceReference(ref).getServiceName())
615                 if not len(str):
616                         pathstr = ref.getPath()
617                         if pathstr.find('FROM PROVIDERS') != -1:
618                                 return _("Provider")
619                         if pathstr.find('FROM SATELLITES') != -1:
620                                 return _("Satellites")
621                         if pathstr.find(') ORDER BY name') != -1:
622                                 return _("All")
623                 return str
624
625         def buildTitleString(self):
626                 titleStr = self.instance.getTitle()
627                 pos = titleStr.find(']')
628                 if pos == -1:
629                         pos = titleStr.find(')')
630                 if pos != -1:
631                         titleStr = titleStr[:pos+1]
632                         Len = len(self.servicePath)
633                         if Len > 0:
634                                 base_ref = self.servicePath[0]
635                                 if Len > 1:
636                                         end_ref = self.servicePath[Len-1]
637                                 else:
638                                         end_ref = None
639                                 nameStr = self.getServiceName(base_ref)
640                                 titleStr += ' ' + nameStr
641                                 if end_ref is not None:
642                                         if Len > 2:
643                                                 titleStr += '/../'
644                                         else:
645                                                 titleStr += '/'
646                                         nameStr = self.getServiceName(end_ref)
647                                         titleStr += nameStr
648                                 self.setTitle(titleStr)
649
650         def moveUp(self):
651                 self.servicelist.moveUp()
652
653         def moveDown(self):
654                 self.servicelist.moveDown()
655
656         def clearPath(self):
657                 del self.servicePath[:]
658
659         def enterPath(self, ref, justSet=False):
660                 self.servicePath.append(ref)
661                 self.setRoot(ref, justSet)
662
663         def pathUp(self, justSet=False):
664                 prev = self.servicePath.pop()
665                 length = len(self.servicePath)
666                 if length:
667                         current = self.servicePath[length-1]
668                         self.setRoot(current, justSet)
669                         if not justSet:
670                                 self.setCurrentSelection(prev)
671                 return prev
672
673         def isBasePathEqual(self, ref):
674                 if len(self.servicePath) > 1 and self.servicePath[0] == ref:
675                         return True
676                 return False
677
678         def isPrevPathEqual(self, ref):
679                 length = len(self.servicePath)
680                 if length > 1 and self.servicePath[length-2] == ref:
681                         return True
682                 return False
683
684         def preEnterPath(self, refstr):
685                 return False
686
687         def showAllServices(self):
688                 if not self.pathChangedDisabled:
689                         refstr = '%s ORDER BY name'%(self.service_types)
690                         if not self.preEnterPath(refstr):
691                                 ref = eServiceReference(refstr)
692                                 currentRoot = self.getRoot()
693                                 if currentRoot is None or currentRoot != ref:
694                                         self.clearPath()
695                                         self.enterPath(ref)
696
697         def showSatellites(self):
698                 if not self.pathChangedDisabled:
699                         refstr = '%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types)
700                         if not self.preEnterPath(refstr):
701                                 ref = eServiceReference(refstr)
702                                 justSet=False
703                                 prev = None
704
705                                 if self.isBasePathEqual(ref):
706                                         if self.isPrevPathEqual(ref):
707                                                 justSet=True
708                                         prev = self.pathUp(justSet)
709                                 else:
710                                         currentRoot = self.getRoot()
711                                         if currentRoot is None or currentRoot != ref:
712                                                 justSet=True
713                                                 self.clearPath()
714                                                 self.enterPath(ref, True)
715                                 if justSet:
716                                         serviceHandler = eServiceCenter.getInstance()
717                                         servicelist = serviceHandler.list(ref)
718                                         if not servicelist is None:
719                                                 while True:
720                                                         service = servicelist.getNext()
721                                                         if not service.valid(): #check if end of list
722                                                                 break
723                                                         orbpos = service.getUnsignedData(4) >> 16
724                                                         if service.getPath().find("FROM PROVIDER") != -1:
725                                                                 service_name = _("Providers")
726                                                         elif service.getPath().find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
727                                                                 service_name = _("New")
728                                                         else:
729                                                                 service_name = _("Services")
730                                                         try:
731                                                                 service_name += str(' - %s'%(nimmanager.getSatDescription(orbpos)))
732                                                                 service.setName(service_name) # why we need this cast?
733                                                         except:
734                                                                 if orbpos == 0xFFFF: #Cable
735                                                                         n = ("%s (%s)") % (service_name, _("Cable"))
736                                                                 elif orbpos == 0xEEEE: #Terrestrial
737                                                                         n = ("%s (%s)") % (service_name, _("Terrestrial"))
738                                                                 else:
739                                                                         if orbpos > 1800: # west
740                                                                                 orbpos = 3600 - orbpos
741                                                                                 h = _("W")
742                                                                         else:
743                                                                                 h = _("E")
744                                                                         n = ("%s (%d.%d" + h + ")") % (service_name, orbpos / 10, orbpos % 10)
745                                                                 service.setName(n)
746                                                         self.servicelist.addService(service)
747                                                         self.servicelist.finishFill()
748                                                         if prev is not None:
749                                                                 self.setCurrentSelection(prev)
750
751         def showProviders(self):
752                 if not self.pathChangedDisabled:
753                         refstr = '%s FROM PROVIDERS ORDER BY name'%(self.service_types)
754                         if not self.preEnterPath(refstr):
755                                 ref = eServiceReference(refstr)
756                                 if self.isBasePathEqual(ref):
757                                         self.pathUp()
758                                 else:
759                                         currentRoot = self.getRoot()
760                                         if currentRoot is None or currentRoot != ref:
761                                                 self.clearPath()
762                                                 self.enterPath(ref)
763
764         def changeBouquet(self, direction):
765                 if not self.pathChangedDisabled:
766                         if self.isBasePathEqual(self.bouquet_root):
767                                 self.pathUp()
768                                 if direction < 0:
769                                         self.moveUp()
770                                 else:
771                                         self.moveDown()
772                                 ref = self.getCurrentSelection()
773                                 self.enterPath(ref)
774
775         def inBouquet(self):
776                 return self.isBasePathEqual(self.bouquet_root)
777
778         def atBegin(self):
779                 return self.servicelist.atBegin()
780
781         def atEnd(self):
782                 return self.servicelist.atEnd()
783
784         def nextBouquet(self):
785                 self.changeBouquet(+1)
786
787         def prevBouquet(self):
788                 self.changeBouquet(-1)
789
790         def showFavourites(self):
791                 if not self.pathChangedDisabled:
792                         if not self.preEnterPath(self.bouquet_rootstr):
793                                 if self.isBasePathEqual(self.bouquet_root):
794                                         self.pathUp()
795                                 else:
796                                         currentRoot = self.getRoot()
797                                         if currentRoot is None or currentRoot != self.bouquet_root:
798                                                 self.clearPath()
799                                                 self.enterPath(self.bouquet_root)
800
801         def keyNumberGlobal(self, number):
802                 char = self.numericalTextInput.getKey(number)
803                 self.servicelist.moveToChar(char)
804
805         def getRoot(self):
806                 return self.servicelist.getRoot()
807
808         def getCurrentSelection(self):
809                 return self.servicelist.getCurrent()
810
811         def setCurrentSelection(self, service):
812                 servicepath = service.getPath()
813                 pos = servicepath.find(" FROM BOUQUET")
814                 if pos != -1:
815                         if self.mode == MODE_TV:
816                                 servicepath = '(type == 1)' + servicepath[pos:]
817                         else:
818                                 servicepath = '(type == 2)' + servicepath[pos:]
819                         service.setPath(servicepath)
820                 self.servicelist.setCurrent(service)
821
822         def getBouquetList(self):
823                 serviceCount=0
824                 bouquets = [ ]
825                 serviceHandler = eServiceCenter.getInstance()
826                 list = serviceHandler.list(self.bouquet_root)
827                 if not list is None:
828                         while True:
829                                 s = list.getNext()
830                                 if not s.valid():
831                                         break
832                                 if ((s.flags & eServiceReference.flagDirectory) == eServiceReference.flagDirectory):
833                                         info = serviceHandler.info(s)
834                                         if not info is None:
835                                                 bouquets.append((info.getName(s), s))
836                                 else:
837                                         serviceCount += 1
838                         if len(bouquets) == 0 and serviceCount > 0:
839                                 info = serviceHandler.info(self.bouquet_root)
840                                 if not info is None:
841                                         bouquets.append((info.getName(self.bouquet_root), self.bouquet_root))
842                         return bouquets
843                 return None
844
845         def keyNumber0(self, num):
846                 if len(self.servicePath) > 1:
847                         self.keyGoUp()
848                 else:
849                         self.keyNumberGlobal(num)
850
851         def keyGoUp(self):
852                 if len(self.servicePath) > 1:
853                         if self.isBasePathEqual(self.bouquet_root):
854                                 self.showFavourites()
855                         else:
856                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
857                                 if self.isBasePathEqual(ref):
858                                         self.showSatellites()
859                                 else:
860                                         ref = eServiceReference('%s FROM PROVIDERS ORDER BY name'%(self.service_types))
861                                         if self.isBasePathEqual(ref):
862                                                 self.showProviders()
863                                         else:
864                                                 self.showAllServices()
865
866         def nextMarker(self):
867                 self.servicelist.moveToNextMarker()
868
869         def prevMarker(self):
870                 self.servicelist.moveToPrevMarker()
871
872 HISTORYSIZE = 20
873
874 #config for lastservice
875 config.tv = ConfigSubsection()
876 config.tv.lastservice = configElement("config.tv.lastservice", configText, "", 0)
877 config.tv.lastroot = configElement("config.tv.lastroot", configText, "", 0)
878 config.radio = ConfigSubsection()
879 config.radio.lastservice = configElement("config.radio.lastservice", configText, "", 0)
880 config.radio.lastroot = configElement("config.radio.lastroot", configText, "", 0)
881 config.servicelist = ConfigSubsection()
882 config.servicelist.lastmode = configElement("config.servicelist.lastmode", configText, "tv", 0)
883
884 class ChannelSelection(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG):
885         def __init__(self, session):
886                 ChannelSelectionBase.__init__(self,session)
887                 ChannelSelectionEdit.__init__(self)
888                 ChannelSelectionEPG.__init__(self)
889
890                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
891                         {
892                                 "cancel": self.cancel,
893                                 "ok": self.channelSelected,
894                                 "keyRadio": self.setModeRadio,
895                                 "keyTV": self.setModeTv,
896                         })
897
898                 self.onShown.append(self.__onShown)
899
900                 self.lastChannelRootTimer = eTimer()
901                 self.lastChannelRootTimer.timeout.get().append(self.__onCreate)
902                 self.lastChannelRootTimer.start(100,True)
903
904                 self.history_tv = [ ]
905                 self.history_radio = [ ]
906                 self.history = self.history_tv
907                 self.history_pos = 0
908
909                 self.lastservice = config.tv.lastservice
910                 self.lastroot = config.tv.lastroot
911                 self.revertMode = None
912
913         def setMode(self):
914                 self.restoreRoot()
915                 lastservice=eServiceReference(self.lastservice.value)
916                 if lastservice.valid():
917                         self.setCurrentSelection(lastservice)
918
919         def setModeTv(self):
920                 if self.revertMode is None and config.servicelist.lastmode.value == "radio":
921                         self.revertMode = MODE_RADIO
922                 self.history = self.history_tv
923                 self.lastservice = config.tv.lastservice
924                 self.lastroot = config.tv.lastroot
925                 config.servicelist.lastmode.value = "tv"
926                 self.setTvMode()
927                 self.setMode()
928
929         def setModeRadio(self):
930                 if self.revertMode is None and config.servicelist.lastmode.value == "tv":
931                         self.revertMode = MODE_TV
932                 if currentConfigSelectionElement(config.usage.e1like_radio_mode) == "yes":
933                         self.history = self.history_radio
934                         self.lastservice = config.radio.lastservice
935                         self.lastroot = config.radio.lastroot
936                         config.servicelist.lastmode.value = "radio"
937                         self.setRadioMode()
938                         self.setMode()
939
940         def __onCreate(self):
941                 if currentConfigSelectionElement(config.usage.e1like_radio_mode) == "yes":
942                         if config.servicelist.lastmode.value == "tv":
943                                 self.setModeTv()
944                         else:
945                                 self.setModeRadio()
946                 else:
947                         self.setModeTv()
948                 lastservice=eServiceReference(self.lastservice.value)
949                 if lastservice.valid():
950                         self.zap()
951
952         def __onShown(self):
953                 self.recallBouquetMode()
954                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
955                 if ref is not None and ref.valid() and ref.getPath() == "":
956                         self.servicelist.setPlayableIgnoreService(ref)
957                 else:
958                         self.servicelist.setPlayableIgnoreService(eServiceReference())
959
960         def channelSelected(self):
961                 ref = self.getCurrentSelection()
962                 if self.movemode:
963                         self.toggleMoveMarked()
964                 elif (ref.flags & 7) == 7:
965                         self.enterPath(ref)
966                 elif self.bouquet_mark_edit:
967                         self.doMark()
968                 elif not (ref.flags & 64): # no marker
969                         self.zap()
970                         self.close(ref)
971
972         #called from infoBar and channelSelected
973         def zap(self):
974                 self.revertMode=None
975                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
976                 nref = self.getCurrentSelection()
977                 if ref is None or ref != nref:
978                         self.session.nav.playService(nref)
979                         self.saveRoot()
980                         self.saveChannel()
981                         config.servicelist.lastmode.save()
982                         self.addToHistory(nref)
983
984         def addToHistory(self, ref):
985                 if self.servicePath is not None:
986                         tmp=self.servicePath[:]
987                         tmp.append(ref)
988                         try:
989                                 del self.history[self.history_pos+1:]
990                         except:
991                                 pass
992                         self.history.append(tmp)
993                         hlen = len(self.history)
994                         if hlen > HISTORYSIZE:
995                                 del self.history[0]
996                                 hlen -= 1
997                         self.history_pos = hlen-1
998
999         def historyBack(self):
1000                 hlen = len(self.history)
1001                 if hlen > 1 and self.history_pos > 0:
1002                         self.history_pos -= 1
1003                         self.setHistoryPath()
1004
1005         def historyNext(self):
1006                 hlen = len(self.history)
1007                 if hlen > 1 and self.history_pos < (hlen-1):
1008                         self.history_pos += 1
1009                         self.setHistoryPath()
1010
1011         def setHistoryPath(self):
1012                 path = self.history[self.history_pos][:]
1013                 ref = path.pop()
1014                 del self.servicePath[:]
1015                 self.servicePath += path
1016                 self.saveRoot()
1017                 plen = len(path)
1018                 root = path[plen-1]
1019                 if self.getRoot() != root:
1020                         self.setRoot(root)
1021                 self.session.nav.playService(ref)
1022                 self.setCurrentSelection(ref)
1023                 self.saveChannel()
1024
1025         def saveRoot(self):
1026                 path = ''
1027                 for i in self.servicePath:
1028                         path += i.toString()
1029                         path += ';'
1030                 if len(path) and path != self.lastroot.value:
1031                         self.lastroot.value = path
1032                         self.lastroot.save()
1033
1034         def restoreRoot(self):
1035                 self.clearPath()
1036                 re = compile('.+?;')
1037                 tmp = re.findall(self.lastroot.value)
1038                 cnt = 0
1039                 for i in tmp:
1040                         self.servicePath.append(eServiceReference(i[:len(i)-1]))
1041                         cnt += 1
1042                 if cnt:
1043                         path = self.servicePath.pop()
1044                         self.enterPath(path)
1045                 else:
1046                         self.showFavourites()
1047                         self.saveRoot()
1048
1049         def preEnterPath(self, refstr):
1050                 if len(self.servicePath) and self.servicePath[0] != eServiceReference(refstr):
1051                         pathstr = self.lastroot.value
1052                         if pathstr is not None and pathstr.find(refstr) == 0:
1053                                 self.restoreRoot()
1054                                 lastservice=eServiceReference(self.lastservice.value)
1055                                 if lastservice.valid():
1056                                         self.setCurrentSelection(lastservice)
1057                                 return True
1058                 return False
1059
1060         def saveChannel(self):
1061                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1062                 if ref is not None:
1063                         refstr = ref.toString()
1064                 else:
1065                         refstr = ""
1066                 if refstr != self.lastservice.value:
1067                         self.lastservice.value = refstr
1068                         self.lastservice.save()
1069
1070         def setCurrentServicePath(self, path):
1071                 hlen = len(self.history)
1072                 if hlen > 0:
1073                         self.history[self.history_pos] = path
1074                 else:
1075                         self.history.append(path)
1076                 self.setHistoryPath()
1077
1078         def getCurrentServicePath(self):
1079                 hlen = len(self.history)
1080                 if hlen > 0:
1081                         return self.history[self.history_pos]
1082                 return None
1083
1084         def recallPrevService(self):
1085                 hlen = len(self.history)
1086                 if hlen > 1:
1087                         if self.history_pos == hlen-1:
1088                                 tmp = self.history[self.history_pos]
1089                                 self.history[self.history_pos] = self.history[self.history_pos-1]
1090                                 self.history[self.history_pos-1] = tmp
1091                         else:
1092                                 tmp = self.history[self.history_pos+1]
1093                                 self.history[self.history_pos+1] = self.history[self.history_pos]
1094                                 self.history[self.history_pos] = tmp
1095                         self.setHistoryPath()
1096
1097         def cancel(self):
1098                 if self.revertMode is None:
1099                         self.restoreRoot()
1100                         lastservice=eServiceReference(self.lastservice.value)
1101                         if lastservice.valid() and self.getCurrentSelection() != lastservice:
1102                                 self.setCurrentSelection(lastservice)
1103                 elif self.revertMode == MODE_TV:
1104                         self.setModeTv()
1105                 elif self.revertMode == MODE_RADIO:
1106                         self.setModeRadio()
1107                 self.revertMode = None
1108                 self.close(None)
1109
1110 from Screens.InfoBarGenerics import InfoBarEvent, InfoBarServiceName, InfoBarInstantRecord
1111
1112 class RadioInfoBar(Screen, InfoBarEvent, InfoBarServiceName, InfoBarInstantRecord):
1113         def __init__(self, session):
1114                 Screen.__init__(self, session)
1115                 InfoBarEvent.__init__(self)
1116                 InfoBarServiceName.__init__(self)
1117                 InfoBarInstantRecord.__init__(self)
1118                 self["CurrentTime"] = Clock()
1119
1120 class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG):
1121         def __init__(self, session):
1122                 ChannelSelectionBase.__init__(self, session)
1123                 ChannelSelectionEdit.__init__(self)
1124                 ChannelSelectionEPG.__init__(self)
1125
1126                 config.radio = ConfigSubsection();
1127                 config.radio.lastservice = configElement("config.radio.lastservice", configText, "", 0);
1128                 config.radio.lastroot = configElement("config.radio.lastroot", configText, "", 0);
1129                 self.onLayoutFinish.append(self.onCreate)
1130
1131                 self.info = session.instantiateDialog(RadioInfoBar)
1132
1133                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1134                         {
1135                                 "keyTV": self.closeRadio,
1136                                 "keyRadio": self.closeRadio,
1137                                 "cancel": self.closeRadio,
1138                                 "ok": self.channelSelected,
1139                         })
1140
1141         def saveRoot(self):
1142                 path = ''
1143                 for i in self.servicePathRadio:
1144                         path += i.toString()
1145                         path += ';'
1146                 if len(path) and path != config.radio.lastroot.value:
1147                         config.radio.lastroot.value = path
1148                         config.radio.lastroot.save()
1149
1150         def restoreRoot(self):
1151                 self.clearPath()
1152                 re = compile('.+?;')
1153                 tmp = re.findall(config.radio.lastroot.value)
1154                 cnt = 0
1155                 for i in tmp:
1156                         self.servicePathRadio.append(eServiceReference(i[:len(i)-1]))
1157                         cnt += 1
1158                 if cnt:
1159                         path = self.servicePathRadio.pop()
1160                         self.enterPath(path)
1161                 else:
1162                         self.showFavourites()
1163                         self.saveRoot()
1164
1165         def preEnterPath(self, refstr):
1166                 if len(self.servicePathRadio) and self.servicePathRadio[0] != eServiceReference(refstr):
1167                         pathstr = config.radio.lastroot.value
1168                         if pathstr is not None and pathstr.find(refstr) == 0:
1169                                 self.restoreRoot()
1170                                 lastservice=eServiceReference(config.radio.lastservice.value)
1171                                 if lastservice.valid():
1172                                         self.setCurrentSelection(lastservice)
1173                                 return True
1174                 return False
1175
1176         def onCreate(self):
1177                 self.setRadioMode()
1178                 self.restoreRoot()
1179                 lastservice=eServiceReference(config.radio.lastservice.value)
1180                 if lastservice.valid():
1181                         self.servicelist.setCurrent(lastservice)
1182                         self.session.nav.playService(lastservice)
1183                         self.servicelist.setPlayableIgnoreService(lastservice)
1184                 self.info.show()
1185
1186         def channelSelected(self): # just return selected service
1187                 ref = self.getCurrentSelection()
1188                 if self.movemode:
1189                         self.toggleMoveMarked()
1190                 elif (ref.flags & 7) == 7:
1191                         self.enterPath(ref)
1192                 elif self.bouquet_mark_edit:
1193                         self.doMark()
1194                 elif not (ref.flags & 64): # no marker
1195                         playingref = self.session.nav.getCurrentlyPlayingServiceReference()
1196                         if playingref is None or playingref != ref:
1197                                 self.session.nav.playService(ref)
1198                                 self.servicelist.setPlayableIgnoreService(ref)
1199                                 config.radio.lastservice.value = ref.toString()
1200                                 config.radio.lastservice.save()
1201                         self.saveRoot()
1202
1203         def closeRadio(self):
1204                 self.info.hide()
1205                 #set previous tv service
1206                 lastservice=eServiceReference(config.tv.lastservice.value)
1207                 self.session.nav.playService(lastservice)
1208                 self.close(None)
1209
1210 class SimpleChannelSelection(ChannelSelectionBase):
1211         def __init__(self, session, title):
1212                 ChannelSelectionBase.__init__(self, session)
1213                 self.title = title
1214                 self.onShown.append(self.__onExecCallback)
1215
1216                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1217                         {
1218                                 "cancel": self.close,
1219                                 "ok": self.channelSelected,
1220                                 "keyRadio": self.setModeRadio,
1221                                 "keyTV": self.setModeTv,
1222                         })
1223
1224         def __onExecCallback(self):
1225                 self.setTitle(self.title)
1226                 self.setModeTv()
1227
1228         def channelSelected(self): # just return selected service
1229                 ref = self.getCurrentSelection()
1230                 if (ref.flags & 7) == 7:
1231                         self.enterPath(ref)
1232                 elif not (ref.flags & 64):
1233                         ref = self.getCurrentSelection()
1234                         self.close(ref)
1235
1236         def setModeTv(self):
1237                 self.setTvMode()
1238                 self.showFavourites()
1239
1240         def setModeRadio(self):
1241                 self.setRadioMode()
1242                 self.showFavourites()