d4a098c35e5b13e2f37d679ea12255f01a269554
[enigma2.git] / lib / python / Screens / ChannelSelection.py
1 from Tools.Profile import profile
2
3 from Screen import Screen
4 from Components.Button import Button
5 from Components.ServiceList import ServiceList
6 from Components.ActionMap import NumberActionMap, ActionMap, HelpableActionMap
7 from Components.MenuList import MenuList
8 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
9 profile("ChannelSelection.py 1")
10 from EpgSelection import EPGSelection
11 from enigma import eServiceReference, eEPGCache, eServiceCenter, eRCInput, eTimer, eDVBDB, iPlayableService, iServiceInformation, getPrevAsciiCode
12 from Components.config import config, ConfigSubsection, ConfigText
13 from Tools.NumericalTextInput import NumericalTextInput
14 profile("ChannelSelection.py 2")
15 from Components.NimManager import nimmanager
16 profile("ChannelSelection.py 2.1")
17 from Components.Sources.RdsDecoder import RdsDecoder
18 profile("ChannelSelection.py 2.2")
19 from Components.Sources.ServiceEvent import ServiceEvent
20 profile("ChannelSelection.py 2.3")
21 from Components.Input import Input
22 profile("ChannelSelection.py 3")
23 from Components.ParentalControl import parentalControl
24 from Screens.InputBox import InputBox, PinInput
25 from Screens.MessageBox import MessageBox
26 from Screens.ServiceInfo import ServiceInfo
27 profile("ChannelSelection.py 4")
28 from Screens.PictureInPicture import PictureInPicture
29 from Screens.RdsDisplay import RassInteractive
30 from ServiceReference import ServiceReference
31 from Tools.BoundFunction import boundFunction
32 from re import compile
33 from os import remove
34 profile("ChannelSelection.py after imports")
35
36 FLAG_SERVICE_NEW_FOUND = 64 #define in lib/dvb/idvb.h as dxNewFound = 64
37
38 class BouquetSelector(Screen):
39         def __init__(self, session, bouquets, selectedFunc, enableWrapAround=False):
40                 Screen.__init__(self, session)
41
42                 self.selectedFunc=selectedFunc
43
44                 self["actions"] = ActionMap(["OkCancelActions"],
45                         {
46                                 "ok": self.okbuttonClick,
47                                 "cancel": self.cancelClick
48                         })
49                 entrys = [ (x[0], x[1]) for x in bouquets ]
50                 self["menu"] = MenuList(entrys, enableWrapAround)
51
52         def getCurrent(self):
53                 cur = self["menu"].getCurrent()
54                 return cur and cur[1]
55
56         def okbuttonClick(self):
57                 self.selectedFunc(self.getCurrent())
58
59         def up(self):
60                 self["menu"].up()
61
62         def down(self):
63                 self["menu"].down()
64
65         def cancelClick(self):
66                 self.close(False)
67
68 # csel.bouquet_mark_edit values
69 OFF = 0
70 EDIT_BOUQUET = 1
71 EDIT_ALTERNATIVES = 2
72
73 def append_when_current_valid(current, menu, args, level = 0):
74         if current and current.valid() and level <= config.usage.setup_level.index:
75                 menu.append(args)
76
77 class ChannelContextMenu(Screen):
78         def __init__(self, session, csel):
79                 Screen.__init__(self, session)
80                 #raise Exception("we need a better summary screen here")
81                 self.csel = csel
82                 self.bsel = None
83
84                 self["actions"] = ActionMap(["OkCancelActions"],
85                         {
86                                 "ok": self.okbuttonClick,
87                                 "cancel": self.cancelClick
88                         })
89                 menu = [ ]
90
91                 current = csel.getCurrentSelection()
92                 current_root = csel.getRoot()
93                 current_sel_path = current.getPath()
94                 current_sel_flags = current.flags
95                 inBouquetRootList = current_root and current_root.getPath().find('FROM BOUQUET "bouquets.') != -1 #FIXME HACK
96                 inBouquet = csel.getMutableList() is not None
97                 haveBouquets = config.usage.multibouquet.value
98
99                 if not (current_sel_path or current_sel_flags & (eServiceReference.isDirectory|eServiceReference.isMarker)):
100                         append_when_current_valid(current, menu, (_("show transponder info"), self.showServiceInformations), level = 2)
101                 if csel.bouquet_mark_edit == OFF and not csel.movemode:
102                         if not inBouquetRootList:
103                                 isPlayable = not (current_sel_flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
104                                 if isPlayable:
105                                         append_when_current_valid(current, menu, (_("Activate Picture in Picture"), self.showServiceInPiP), level = 0)
106                                         if config.ParentalControl.configured.value:
107                                                 if parentalControl.getProtectionLevel(csel.getCurrentSelection().toCompareString()) == -1:
108                                                         append_when_current_valid(current, menu, (_("add to parental protection"), boundFunction(self.addParentalProtection, csel.getCurrentSelection())), level = 0)
109                                                 else:
110                                                         append_when_current_valid(current, menu, (_("remove from parental protection"), boundFunction(self.removeParentalProtection, csel.getCurrentSelection())), level = 0)
111                                         if haveBouquets:
112                                                 append_when_current_valid(current, menu, (_("add service to bouquet"), self.addServiceToBouquetSelected), level = 0)
113                                         else:
114                                                 append_when_current_valid(current, menu, (_("add service to favourites"), self.addServiceToBouquetSelected), level = 0)
115                                 else:
116                                         if current_root.getPath().find('FROM SATELLITES') != -1:
117                                                 append_when_current_valid(current, menu, (_("remove selected satellite"), self.removeSatelliteServices), level = 0)
118                                         if haveBouquets:
119                                                 if not inBouquet and current_sel_path.find("PROVIDERS") == -1:
120                                                         append_when_current_valid(current, menu, (_("copy to bouquets"), self.copyCurrentToBouquetList), level = 0)
121                                         if current_sel_path.find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
122                                                 append_when_current_valid(current, menu, (_("remove all new found flags"), self.removeAllNewFoundFlags), level = 0)
123                                 if inBouquet:
124                                         append_when_current_valid(current, menu, (_("remove entry"), self.removeCurrentService), level = 0)
125                                 if current_root and current_root.getPath().find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
126                                         append_when_current_valid(current, menu, (_("remove new found flag"), self.removeNewFoundFlag), level = 0)
127                         else:
128                                         menu.append((_("add bouquet"), self.showBouquetInputBox))
129                                         append_when_current_valid(current, menu, (_("remove entry"), self.removeBouquet), level = 0)
130
131                 if inBouquet: # current list is editable?
132                         if csel.bouquet_mark_edit == OFF:
133                                 if not csel.movemode:
134                                         append_when_current_valid(current, menu, (_("enable move mode"), self.toggleMoveMode), level = 1)
135                                         if not inBouquetRootList and current_root and not (current_root.flags & eServiceReference.isGroup):
136                                                 menu.append((_("add marker"), self.showMarkerInputBox))
137                                                 if haveBouquets:
138                                                         append_when_current_valid(current, menu, (_("enable bouquet edit"), self.bouquetMarkStart), level = 0)
139                                                 else:
140                                                         append_when_current_valid(current, menu, (_("enable favourite edit"), self.bouquetMarkStart), level = 0)
141                                                 if current_sel_flags & eServiceReference.isGroup:
142                                                         append_when_current_valid(current, menu, (_("edit alternatives"), self.editAlternativeServices), level = 2)
143                                                         append_when_current_valid(current, menu, (_("show alternatives"), self.showAlternativeServices), level = 2)
144                                                         append_when_current_valid(current, menu, (_("remove all alternatives"), self.removeAlternativeServices), level = 2)
145                                                 elif not current_sel_flags & eServiceReference.isMarker:
146                                                         append_when_current_valid(current, menu, (_("add alternatives"), self.addAlternativeServices), level = 2)
147                                 else:
148                                         append_when_current_valid(current, menu, (_("disable move mode"), self.toggleMoveMode), level = 0)
149                         else:
150                                 if csel.bouquet_mark_edit == EDIT_BOUQUET:
151                                         if haveBouquets:
152                                                 append_when_current_valid(current, menu, (_("end bouquet edit"), self.bouquetMarkEnd), level = 0)
153                                                 append_when_current_valid(current, menu, (_("abort bouquet edit"), self.bouquetMarkAbort), level = 0)
154                                         else:
155                                                 append_when_current_valid(current, menu, (_("end favourites edit"), self.bouquetMarkEnd), level = 0)
156                                                 append_when_current_valid(current, menu, (_("abort favourites edit"), self.bouquetMarkAbort), level = 0)
157                                 else:
158                                                 append_when_current_valid(current, menu, (_("end alternatives edit"), self.bouquetMarkEnd), level = 0)
159                                                 append_when_current_valid(current, menu, (_("abort alternatives edit"), self.bouquetMarkAbort), level = 0)
160
161                 menu.append((_("back"), self.cancelClick))
162                 self["menu"] = MenuList(menu)
163
164         def okbuttonClick(self):
165                 self["menu"].getCurrent()[1]()
166
167         def cancelClick(self):
168                 self.close(False)
169
170         def showServiceInformations(self):
171                 self.session.open( ServiceInfo, self.csel.getCurrentSelection() )
172
173         def showBouquetInputBox(self):
174                 self.session.openWithCallback(self.bouquetInputCallback, InputBox, title=_("Please enter a name for the new bouquet"), text="bouquetname", maxSize=False, visible_width = 56, type=Input.TEXT)
175
176         def bouquetInputCallback(self, bouquet):
177                 if bouquet is not None:
178                         self.csel.addBouquet(bouquet, None)
179                 self.close()
180
181         def addParentalProtection(self, service):
182                 parentalControl.protectService(service.toCompareString())
183                 self.close()
184
185         def removeParentalProtection(self, service):
186                 self.session.openWithCallback(boundFunction(self.pinEntered, service.toCompareString()), PinInput, pinList = [config.ParentalControl.servicepin[0].value], triesEntry = config.ParentalControl.retries.servicepin, title = _("Enter the service pin"), windowTitle = _("Change pin code"))
187
188         def pinEntered(self, service, result):
189                 if result:
190                         parentalControl.unProtectService(service)
191                         self.close()
192                 else:
193                         self.session.openWithCallback(self.close, MessageBox, _("The pin code you entered is wrong."), MessageBox.TYPE_ERROR)
194                         
195         def showServiceInPiP(self):
196                 if self.session.pipshown:
197                         del self.session.pip
198                 self.session.pip = self.session.instantiateDialog(PictureInPicture)
199                 self.session.pip.show()
200                 newservice = self.csel.servicelist.getCurrent()
201                 if self.session.pip.playService(newservice):
202                         self.session.pipshown = True
203                         self.session.pip.servicePath = self.csel.getCurrentServicePath()
204                         self.close()
205                 else:
206                         self.session.pipshown = False
207                         del self.session.pip
208                         self.session.openWithCallback(self.close, MessageBox, _("Could not open Picture in Picture"), MessageBox.TYPE_ERROR)
209
210         def addServiceToBouquetSelected(self):
211                 bouquets = self.csel.getBouquetList()
212                 if bouquets is None:
213                         cnt = 0
214                 else:
215                         cnt = len(bouquets)
216                 if cnt > 1: # show bouquet list
217                         self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, bouquets, self.addCurrentServiceToBouquet)
218                 elif cnt == 1: # add to only one existing bouquet
219                         self.addCurrentServiceToBouquet(bouquets[0][1])
220
221         def bouquetSelClosed(self, recursive):
222                 self.bsel = None
223                 if recursive:
224                         self.close(False)
225
226         def removeSatelliteServices(self):
227                 curpath = self.csel.getCurrentSelection().getPath()
228                 idx = curpath.find("satellitePosition == ")
229                 if idx != -1:
230                         tmp = curpath[idx+21:]
231                         idx = tmp.find(')')
232                         if idx != -1:
233                                 satpos = int(tmp[:idx])
234                                 eDVBDB.getInstance().removeServices(-1, -1, -1, satpos)
235                 self.close()
236
237         def copyCurrentToBouquetList(self):
238                 self.csel.copyCurrentToBouquetList()
239                 self.close()
240
241         def removeBouquet(self):
242                 self.csel.removeBouquet()
243                 self.close()
244
245         def showMarkerInputBox(self):
246                 self.session.openWithCallback(self.markerInputCallback, InputBox, title=_("Please enter a name for the new marker"), text="markername", maxSize=False, visible_width = 56, type=Input.TEXT)
247
248         def markerInputCallback(self, marker):
249                 if marker is not None:
250                         self.csel.addMarker(marker)
251                 self.close()
252
253         def addCurrentServiceToBouquet(self, dest):
254                 self.csel.addServiceToBouquet(dest)
255                 if self.bsel is not None:
256                         self.bsel.close(True)
257                 else:
258                         self.close(True) # close bouquet selection
259
260         def removeCurrentService(self):
261                 self.csel.removeCurrentService()
262                 self.close()
263
264         def toggleMoveMode(self):
265                 self.csel.toggleMoveMode()
266                 self.close()
267
268         def bouquetMarkStart(self):
269                 self.csel.startMarkedEdit(EDIT_BOUQUET)
270                 self.close()
271
272         def bouquetMarkEnd(self):
273                 self.csel.endMarkedEdit(abort=False)
274                 self.close()
275
276         def bouquetMarkAbort(self):
277                 self.csel.endMarkedEdit(abort=True)
278                 self.close()
279
280         def removeNewFoundFlag(self):
281                 eDVBDB.getInstance().removeFlag(self.csel.getCurrentSelection(), FLAG_SERVICE_NEW_FOUND)
282                 self.close()
283
284         def removeAllNewFoundFlags(self):
285                 curpath = self.csel.getCurrentSelection().getPath()
286                 idx = curpath.find("satellitePosition == ")
287                 if idx != -1:
288                         tmp = curpath[idx+21:]
289                         idx = tmp.find(')')
290                         if idx != -1:
291                                 satpos = int(tmp[:idx])
292                                 eDVBDB.getInstance().removeFlags(FLAG_SERVICE_NEW_FOUND, -1, -1, -1, satpos)
293                 self.close()
294
295         def editAlternativeServices(self):
296                 self.csel.startMarkedEdit(EDIT_ALTERNATIVES)
297                 self.close()
298
299         def showAlternativeServices(self):
300                 self.csel.enterPath(self.csel.getCurrentSelection())
301                 self.close()
302
303         def removeAlternativeServices(self):
304                 self.csel.removeAlternativeServices()
305                 self.close()
306
307         def addAlternativeServices(self):
308                 self.csel.addAlternativeServices()
309                 self.csel.startMarkedEdit(EDIT_ALTERNATIVES)
310                 self.close()
311
312 class SelectionEventInfo:
313         def __init__(self):
314                 self["ServiceEvent"] = ServiceEvent()
315                 self.servicelist.connectSelChanged(self.__selectionChanged)
316                 self.timer = eTimer()
317                 self.timer.callback.append(self.updateEventInfo)
318                 self.onShown.append(self.__selectionChanged)
319
320         def __selectionChanged(self):
321                 if self.execing:
322                         self.timer.start(100, True)
323
324         def updateEventInfo(self):
325                 cur = self.getCurrentSelection()
326                 self["ServiceEvent"].newService(cur)
327
328 class ChannelSelectionEPG:
329         def __init__(self):
330                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
331                         {
332                                 "showEPGList": self.showEPGList,
333                         })
334
335         def showEPGList(self):
336                 ref=self.getCurrentSelection()
337                 if ref:
338                         self.savedService = ref
339                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB=self.changeServiceCB)
340
341         def SingleServiceEPGClosed(self, ret=False):
342                 self.setCurrentSelection(self.savedService)
343
344         def changeServiceCB(self, direction, epg):
345                 beg = self.getCurrentSelection()
346                 while True:
347                         if direction > 0:
348                                 self.moveDown()
349                         else:
350                                 self.moveUp()
351                         cur = self.getCurrentSelection()
352                         if cur == beg or not (cur.flags & eServiceReference.isMarker):
353                                 break
354                 epg.setService(ServiceReference(self.getCurrentSelection()))
355
356 class ChannelSelectionEdit:
357         def __init__(self):
358                 self.entry_marked = False
359                 self.movemode = False
360                 self.bouquet_mark_edit = OFF
361                 self.mutableList = None
362                 self.__marked = [ ]
363                 self.saved_title = None
364                 self.saved_root = None
365
366                 class ChannelSelectionEditActionMap(ActionMap):
367                         def __init__(self, csel, contexts = [ ], actions = { }, prio=0):
368                                 ActionMap.__init__(self, contexts, actions, prio)
369                                 self.csel = csel
370
371                         def action(self, contexts, action):
372                                 if action == "cancel":
373                                         self.csel.handleEditCancel()
374                                         return 0 # fall-trough
375                                 elif action == "ok":
376                                         return 0 # fall-trough
377                                 else:
378                                         return ActionMap.action(self, contexts, action)
379
380                 self["ChannelSelectEditActions"] = ChannelSelectionEditActionMap(self, ["ChannelSelectEditActions", "OkCancelActions"],
381                         {
382                                 "contextMenu": self.doContext,
383                         })
384
385         def getMutableList(self, root=eServiceReference()):
386                 if not self.mutableList is None:
387                         return self.mutableList
388                 serviceHandler = eServiceCenter.getInstance()
389                 if not root.valid():
390                         root=self.getRoot()
391                 list = root and serviceHandler.list(root)
392                 if list is not None:
393                         return list.startEdit()
394                 return None
395
396         def buildBouquetID(self, str):
397                 tmp = str.lower()
398                 name = ''
399                 for c in tmp:
400                         if (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9'):
401                                 name += c
402                         else:
403                                 name += '_'
404                 return name
405
406         def addMarker(self, name):
407                 current = self.servicelist.getCurrent()
408                 mutableList = self.getMutableList()
409                 cnt = 0
410                 while mutableList:
411                         str = '1:64:%d:0:0:0:0:0:0:0::%s'%(cnt, name)
412                         ref = eServiceReference(str)
413                         if current and current.valid():
414                                 if not mutableList.addService(ref, current):
415                                         self.servicelist.addService(ref, True)
416                                         mutableList.flushChanges()
417                                         break
418                         elif not mutableList.addService(ref):
419                                 self.servicelist.addService(ref, True)
420                                 mutableList.flushChanges()
421                                 break
422                         cnt+=1
423
424         def addAlternativeServices(self):
425                 cur_service = ServiceReference(self.getCurrentSelection())
426                 root = self.getRoot()
427                 cur_root = root and ServiceReference(root)
428                 mutableBouquet = cur_root.list().startEdit()
429                 if mutableBouquet:
430                         name = cur_service.getServiceName()
431                         print "NAME", name
432                         if self.mode == MODE_TV:
433                                 str = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"alternatives.%s.tv\" ORDER BY bouquet'%(self.buildBouquetID(name))
434                         else:
435                                 str = '1:134:2:0:0:0:0:0:0:0:FROM BOUQUET \"alternatives.%s.radio\" ORDER BY bouquet'%(self.buildBouquetID(name))
436                         new_ref = ServiceReference(str)
437                         if not mutableBouquet.addService(new_ref.ref, cur_service.ref):
438                                 mutableBouquet.removeService(cur_service.ref)
439                                 mutableBouquet.flushChanges()
440                                 eDVBDB.getInstance().reloadBouquets()
441                                 mutableAlternatives = new_ref.list().startEdit()
442                                 if mutableAlternatives:
443                                         mutableAlternatives.setListName(name)
444                                         if mutableAlternatives.addService(cur_service.ref):
445                                                 print "add", cur_service.toString(), "to new alternatives failed"
446                                         mutableAlternatives.flushChanges()
447                                         self.servicelist.addService(new_ref.ref, True)
448                                         self.servicelist.removeCurrent()
449                                         self.servicelist.moveUp()
450                                 else:
451                                         print "get mutable list for new created alternatives failed"
452                         else:
453                                 print "add", str, "to", cur_root.getServiceName(), "failed"
454                 else:
455                         print "bouquetlist is not editable"
456
457         def addBouquet(self, bName, services):
458                 serviceHandler = eServiceCenter.getInstance()
459                 mutableBouquetList = serviceHandler.list(self.bouquet_root).startEdit()
460                 if mutableBouquetList:
461                         if self.mode == MODE_TV:
462                                 bName += " (TV)"
463                                 str = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET \"userbouquet.%s.tv\" ORDER BY bouquet'%(self.buildBouquetID(bName))
464                         else:
465                                 bName += " (Radio)"
466                                 str = '1:7:2:0:0:0:0:0:0:0:FROM BOUQUET \"userbouquet.%s.radio\" ORDER BY bouquet'%(self.buildBouquetID(bName))
467                         new_bouquet_ref = eServiceReference(str)
468                         if not mutableBouquetList.addService(new_bouquet_ref):
469                                 mutableBouquetList.flushChanges()
470                                 eDVBDB.getInstance().reloadBouquets()
471                                 mutableBouquet = serviceHandler.list(new_bouquet_ref).startEdit()
472                                 if mutableBouquet:
473                                         mutableBouquet.setListName(bName)
474                                         if services is not None:
475                                                 for service in services:
476                                                         if mutableBouquet.addService(service):
477                                                                 print "add", service.toString(), "to new bouquet failed"
478                                         mutableBouquet.flushChanges()
479                                 else:
480                                         print "get mutable list for new created bouquet failed"
481                                 # do some voodoo to check if current_root is equal to bouquet_root
482                                 cur_root = self.getRoot();
483                                 str1 = cur_root and cur_root.toString()
484                                 pos1 = str1 and str1.find("FROM BOUQUET") or -1
485                                 pos2 = self.bouquet_rootstr.find("FROM BOUQUET")
486                                 if pos1 != -1 and pos2 != -1 and str1[pos1:] == self.bouquet_rootstr[pos2:]:
487                                         self.servicelist.addService(new_bouquet_ref)
488                         else:
489                                 print "add", str, "to bouquets failed"
490                 else:
491                         print "bouquetlist is not editable"
492
493         def copyCurrentToBouquetList(self):
494                 provider = ServiceReference(self.getCurrentSelection())
495                 providerName = provider.getServiceName()
496                 serviceHandler = eServiceCenter.getInstance()
497                 services = serviceHandler.list(provider.ref)
498                 self.addBouquet(providerName, services and services.getContent('R', True))
499
500         def removeAlternativeServices(self):
501                 cur_service = ServiceReference(self.getCurrentSelection())
502                 root = self.getRoot()
503                 cur_root = root and ServiceReference(root)
504                 list = cur_service.list()
505                 first_in_alternative = list and list.getNext()
506                 if first_in_alternative:
507                         edit_root = cur_root and cur_root.list().startEdit()
508                         if edit_root:
509                                 if not edit_root.addService(first_in_alternative, cur_service.ref):
510                                         self.servicelist.addService(first_in_alternative, True)
511                                 else:
512                                         print "couldn't add first alternative service to current root"
513                         else:
514                                 print "couldn't edit current root!!"
515                 else:
516                         print "remove empty alternative list !!"
517                 self.removeBouquet()
518                 self.servicelist.moveUp()
519
520         def removeBouquet(self):
521                 refstr = self.getCurrentSelection().toString()
522                 print "removeBouquet", refstr
523                 self.bouquetNumOffsetCache = { }
524                 pos = refstr.find('FROM BOUQUET "')
525                 filename = None
526                 if pos != -1:
527                         refstr = refstr[pos+14:]
528                         pos = refstr.find('"')
529                         if pos != -1:
530                                 filename = '/etc/enigma2/' + refstr[:pos] # FIXMEEE !!! HARDCODED /etc/enigma2
531                 self.removeCurrentService()
532                 try:
533                         if filename is not None:
534                                 remove(filename)
535                 except OSError:
536                         print "error during remove of", filename
537
538 #  multiple marked entry stuff ( edit mode, later multiepg selection )
539         def startMarkedEdit(self, type):
540                 self.savedPath = self.servicePath[:]
541                 if type == EDIT_ALTERNATIVES:
542                         self.enterPath(self.getCurrentSelection())
543                 self.mutableList = self.getMutableList()
544                 # add all services from the current list to internal marked set in listboxservicecontent
545                 self.clearMarks() # this clears the internal marked set in the listboxservicecontent
546                 self.saved_title = self.getTitle()
547                 pos = self.saved_title.find(')')
548                 new_title = self.saved_title[:pos+1]
549                 if type == EDIT_ALTERNATIVES:
550                         self.bouquet_mark_edit = EDIT_ALTERNATIVES
551                         new_title += ' ' + _("[alternative edit]")
552                 else:
553                         self.bouquet_mark_edit = EDIT_BOUQUET
554                         if config.usage.multibouquet.value:
555                                 new_title += ' ' + _("[bouquet edit]")
556                         else:
557                                 new_title += ' ' + _("[favourite edit]")
558                 self.setTitle(new_title)
559                 self.__marked = self.servicelist.getRootServices()
560                 for x in self.__marked:
561                         self.servicelist.addMarked(eServiceReference(x))
562                 self.showAllServices()
563
564         def endMarkedEdit(self, abort):
565                 if not abort and self.mutableList is not None:
566                         self.bouquetNumOffsetCache = { }
567                         new_marked = set(self.servicelist.getMarked())
568                         old_marked = set(self.__marked)
569                         removed = old_marked - new_marked
570                         added = new_marked - old_marked
571                         changed = False
572                         for x in removed:
573                                 changed = True
574                                 self.mutableList.removeService(eServiceReference(x))
575                         for x in added:
576                                 changed = True
577                                 self.mutableList.addService(eServiceReference(x))
578                         if changed:
579                                 self.mutableList.flushChanges()
580                 self.__marked = []
581                 self.clearMarks()
582                 self.bouquet_mark_edit = OFF
583                 self.mutableList = None
584                 self.setTitle(self.saved_title)
585                 self.saved_title = None
586                 # self.servicePath is just a reference to servicePathTv or Radio...
587                 # so we never ever do use the asignment operator in self.servicePath
588                 del self.servicePath[:] # remove all elements
589                 self.servicePath += self.savedPath # add saved elements
590                 del self.savedPath
591                 self.setRoot(self.servicePath[-1])
592
593         def clearMarks(self):
594                 self.servicelist.clearMarks()
595
596         def doMark(self):
597                 ref = self.servicelist.getCurrent()
598                 if self.servicelist.isMarked(ref):
599                         self.servicelist.removeMarked(ref)
600                 else:
601                         self.servicelist.addMarked(ref)
602
603         def removeCurrentService(self):
604                 ref = self.servicelist.getCurrent()
605                 mutableList = self.getMutableList()
606                 if ref.valid() and mutableList is not None:
607                         if not mutableList.removeService(ref):
608                                 self.bouquetNumOffsetCache = { }
609                                 mutableList.flushChanges() #FIXME dont flush on each single removed service
610                                 self.servicelist.removeCurrent()
611
612         def addServiceToBouquet(self, dest, service=None):
613                 mutableList = self.getMutableList(dest)
614                 if not mutableList is None:
615                         if service is None: #use current selected service
616                                 service = self.servicelist.getCurrent()
617                         if not mutableList.addService(service):
618                                 self.bouquetNumOffsetCache = { }
619                                 mutableList.flushChanges()
620                                 # do some voodoo to check if current_root is equal to dest
621                                 cur_root = self.getRoot();
622                                 str1 = cur_root and cur_root.toString() or -1
623                                 str2 = dest.toString()
624                                 pos1 = str1.find("FROM BOUQUET")
625                                 pos2 = str2.find("FROM BOUQUET")
626                                 if pos1 != -1 and pos2 != -1 and str1[pos1:] == str2[pos2:]:
627                                         self.servicelist.addService(service)
628
629         def toggleMoveMode(self):
630                 if self.movemode:
631                         if self.entry_marked:
632                                 self.toggleMoveMarked() # unmark current entry
633                         self.movemode = False
634                         self.pathChangeDisabled = False # re-enable path change
635                         self.mutableList.flushChanges() # FIXME add check if changes was made
636                         self.mutableList = None
637                         self.setTitle(self.saved_title)
638                         self.saved_title = None
639                         cur_root = self.getRoot()
640                         if cur_root and cur_root == self.bouquet_root:
641                                 self.bouquetNumOffsetCache = { }
642                 else:
643                         self.mutableList = self.getMutableList()
644                         self.movemode = True
645                         self.pathChangeDisabled = True # no path change allowed in movemode
646                         self.saved_title = self.getTitle()
647                         new_title = self.saved_title
648                         pos = self.saved_title.find(')')
649                         new_title = self.saved_title[:pos+1] + ' ' + _("[move mode]") + self.saved_title[pos+1:]
650                         self.setTitle(new_title);
651
652         def handleEditCancel(self):
653                 if self.movemode: #movemode active?
654                         self.channelSelected() # unmark
655                         self.toggleMoveMode() # disable move mode
656                 elif self.bouquet_mark_edit != OFF:
657                         self.endMarkedEdit(True) # abort edit mode
658
659         def toggleMoveMarked(self):
660                 if self.entry_marked:
661                         self.servicelist.setCurrentMarked(False)
662                         self.entry_marked = False
663                 else:
664                         self.servicelist.setCurrentMarked(True)
665                         self.entry_marked = True
666
667         def doContext(self):
668                 self.session.open(ChannelContextMenu, self)
669
670 MODE_TV = 0
671 MODE_RADIO = 1
672
673 # type 1 = digital television service
674 # type 4 = nvod reference service (NYI)
675 # type 17 = MPEG-2 HD digital television service
676 # type 22 = advanced codec SD digital television
677 # type 24 = advanced codec SD NVOD reference service (NYI)
678 # type 25 = advanced codec HD digital television
679 # type 27 = advanced codec HD NVOD reference service (NYI)
680 # type 2 = digital radio sound service
681 # type 10 = advanced codec digital radio sound service
682
683 service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 22) || (type == 25) || (type == 134) || (type == 195)'
684 service_types_radio = '1:7:2:0:0:0:0:0:0:0:(type == 2) || (type == 10)'
685
686 class ChannelSelectionBase(Screen):
687         def __init__(self, session):
688                 Screen.__init__(self, session)
689
690                 self["key_red"] = Button(_("All"))
691                 self["key_green"] = Button(_("Satellites"))
692                 self["key_yellow"] = Button(_("Provider"))
693                 self["key_blue"] = Button(_("Favourites"))
694
695                 self["list"] = ServiceList()
696                 self.servicelist = self["list"]
697
698                 self.numericalTextInput = NumericalTextInput()
699                 self.numericalTextInput.setUseableChars(u'1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ')
700
701                 self.servicePathTV = [ ]
702                 self.servicePathRadio = [ ]
703                 self.servicePath = [ ]
704
705                 self.mode = MODE_TV
706
707                 self.pathChangeDisabled = False
708
709                 self.bouquetNumOffsetCache = { }
710
711                 self["ChannelSelectBaseActions"] = NumberActionMap(["ChannelSelectBaseActions", "NumberActions", "InputAsciiActions"],
712                         {
713                                 "showFavourites": self.showFavourites,
714                                 "showAllServices": self.showAllServices,
715                                 "showProviders": self.showProviders,
716                                 "showSatellites": self.showSatellites,
717                                 "nextBouquet": self.nextBouquet,
718                                 "prevBouquet": self.prevBouquet,
719                                 "nextMarker": self.nextMarker,
720                                 "prevMarker": self.prevMarker,
721                                 "gotAsciiCode": self.keyAsciiCode,
722                                 "1": self.keyNumberGlobal,
723                                 "2": self.keyNumberGlobal,
724                                 "3": self.keyNumberGlobal,
725                                 "4": self.keyNumberGlobal,
726                                 "5": self.keyNumberGlobal,
727                                 "6": self.keyNumberGlobal,
728                                 "7": self.keyNumberGlobal,
729                                 "8": self.keyNumberGlobal,
730                                 "9": self.keyNumberGlobal,
731                                 "0": self.keyNumber0
732                         })
733                 self.recallBouquetMode()
734
735         def getBouquetNumOffset(self, bouquet):
736                 if not config.usage.multibouquet.value:
737                         return 0
738                 str = bouquet.toString()
739                 offsetCount = 0
740                 if not self.bouquetNumOffsetCache.has_key(str):
741                         serviceHandler = eServiceCenter.getInstance()
742                         bouquetlist = serviceHandler.list(self.bouquet_root)
743                         if not bouquetlist is None:
744                                 while True:
745                                         bouquetIterator = bouquetlist.getNext()
746                                         if not bouquetIterator.valid(): #end of list
747                                                 break
748                                         self.bouquetNumOffsetCache[bouquetIterator.toString()]=offsetCount
749                                         if not (bouquetIterator.flags & eServiceReference.isDirectory):
750                                                 continue
751                                         servicelist = serviceHandler.list(bouquetIterator)
752                                         if not servicelist is None:
753                                                 while True:
754                                                         serviceIterator = servicelist.getNext()
755                                                         if not serviceIterator.valid(): #check if end of list
756                                                                 break
757                                                         playable = not (serviceIterator.flags & (eServiceReference.isDirectory|eServiceReference.isMarker))
758                                                         if playable:
759                                                                 offsetCount += 1
760                 return self.bouquetNumOffsetCache.get(str, offsetCount)
761
762         def recallBouquetMode(self):
763                 if self.mode == MODE_TV:
764                         self.service_types = service_types_tv
765                         if config.usage.multibouquet.value:
766                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
767                         else:
768                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'%(self.service_types)
769                 else:
770                         self.service_types = service_types_radio
771                         if config.usage.multibouquet.value:
772                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.radio" ORDER BY bouquet'
773                         else:
774                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.radio" ORDER BY bouquet'%(self.service_types)
775                 self.bouquet_root = eServiceReference(self.bouquet_rootstr)
776
777         def setTvMode(self):
778                 self.mode = MODE_TV
779                 self.servicePath = self.servicePathTV
780                 self.recallBouquetMode()
781                 title = self.getTitle()
782                 pos = title.find(" (")
783                 if pos != -1:
784                         title = title[:pos]
785                 title += " (TV)"
786                 self.setTitle(title)
787
788         def setRadioMode(self):
789                 self.mode = MODE_RADIO
790                 self.servicePath = self.servicePathRadio
791                 self.recallBouquetMode()
792                 title = self.getTitle()
793                 pos = title.find(" (")
794                 if pos != -1:
795                         title = title[:pos]
796                 title += " (Radio)"
797                 self.setTitle(title)
798
799         def setRoot(self, root, justSet=False):
800                 path = root.getPath()
801                 inBouquetRootList = path.find('FROM BOUQUET "bouquets.') != -1 #FIXME HACK
802                 pos = path.find('FROM BOUQUET')
803                 isBouquet = (pos != -1) and (root.flags & eServiceReference.isDirectory)
804                 if not inBouquetRootList and isBouquet:
805                         self.servicelist.setMode(ServiceList.MODE_FAVOURITES)
806                         self.servicelist.setNumberOffset(self.getBouquetNumOffset(root))
807                 else:
808                         self.servicelist.setMode(ServiceList.MODE_NORMAL)
809                 self.servicelist.setRoot(root, justSet)
810                 self.buildTitleString()
811
812         def removeModeStr(self, str):
813                 if self.mode == MODE_TV:
814                         pos = str.find(' (TV)')
815                 else:
816                         pos = str.find(' (Radio)')
817                 if pos != -1:
818                         return str[:pos]
819                 return str
820
821         def getServiceName(self, ref):
822                 str = self.removeModeStr(ServiceReference(ref).getServiceName())
823                 if not str:
824                         pathstr = ref.getPath()
825                         if 'FROM PROVIDERS' in pathstr:
826                                 return _("Provider")
827                         if 'FROM SATELLITES' in pathstr:
828                                 return _("Satellites")
829                         if ') ORDER BY name' in pathstr:
830                                 return _("All")
831                 return str
832
833         def buildTitleString(self):
834                 titleStr = self.getTitle()
835                 pos = titleStr.find(']')
836                 if pos == -1:
837                         pos = titleStr.find(')')
838                 if pos != -1:
839                         titleStr = titleStr[:pos+1]
840                         Len = len(self.servicePath)
841                         if Len > 0:
842                                 base_ref = self.servicePath[0]
843                                 if Len > 1:
844                                         end_ref = self.servicePath[Len-1]
845                                 else:
846                                         end_ref = None
847                                 nameStr = self.getServiceName(base_ref)
848                                 titleStr += ' ' + nameStr
849                                 if end_ref is not None:
850                                         if Len > 2:
851                                                 titleStr += '/../'
852                                         else:
853                                                 titleStr += '/'
854                                         nameStr = self.getServiceName(end_ref)
855                                         titleStr += nameStr
856                                 self.setTitle(titleStr)
857
858         def moveUp(self):
859                 self.servicelist.moveUp()
860
861         def moveDown(self):
862                 self.servicelist.moveDown()
863
864         def clearPath(self):
865                 del self.servicePath[:]
866
867         def enterPath(self, ref, justSet=False):
868                 self.servicePath.append(ref)
869                 self.setRoot(ref, justSet)
870
871         def pathUp(self, justSet=False):
872                 prev = self.servicePath.pop()
873                 if self.servicePath:
874                         current = self.servicePath[-1]
875                         self.setRoot(current, justSet)
876                         if not justSet:
877                                 self.setCurrentSelection(prev)
878                 return prev
879
880         def isBasePathEqual(self, ref):
881                 if len(self.servicePath) > 1 and self.servicePath[0] == ref:
882                         return True
883                 return False
884
885         def isPrevPathEqual(self, ref):
886                 length = len(self.servicePath)
887                 if length > 1 and self.servicePath[length-2] == ref:
888                         return True
889                 return False
890
891         def preEnterPath(self, refstr):
892                 return False
893
894         def showAllServices(self):
895                 if not self.pathChangeDisabled:
896                         refstr = '%s ORDER BY name'%(self.service_types)
897                         if not self.preEnterPath(refstr):
898                                 ref = eServiceReference(refstr)
899                                 currentRoot = self.getRoot()
900                                 if currentRoot is None or currentRoot != ref:
901                                         self.clearPath()
902                                         self.enterPath(ref)
903
904         def showSatellites(self):
905                 if not self.pathChangeDisabled:
906                         refstr = '%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types)
907                         if not self.preEnterPath(refstr):
908                                 ref = eServiceReference(refstr)
909                                 justSet=False
910                                 prev = None
911
912                                 if self.isBasePathEqual(ref):
913                                         if self.isPrevPathEqual(ref):
914                                                 justSet=True
915                                         prev = self.pathUp(justSet)
916                                 else:
917                                         currentRoot = self.getRoot()
918                                         if currentRoot is None or currentRoot != ref:
919                                                 justSet=True
920                                                 self.clearPath()
921                                                 self.enterPath(ref, True)
922                                 if justSet:
923                                         serviceHandler = eServiceCenter.getInstance()
924                                         servicelist = serviceHandler.list(ref)
925                                         if not servicelist is None:
926                                                 while True:
927                                                         service = servicelist.getNext()
928                                                         if not service.valid(): #check if end of list
929                                                                 break
930                                                         unsigned_orbpos = service.getUnsignedData(4) >> 16
931                                                         orbpos = service.getData(4) >> 16
932                                                         if orbpos < 0:
933                                                                 orbpos += 3600
934                                                         if service.getPath().find("FROM PROVIDER") != -1:
935                                                                 service_type = _("Providers")
936                                                         elif service.getPath().find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
937                                                                 service_type = _("New")
938                                                         else:
939                                                                 service_type = _("Services")
940                                                         try:
941                                                                 # why we need this cast?
942                                                                 service_name = str(nimmanager.getSatDescription(orbpos))
943                                                         except:
944                                                                 if unsigned_orbpos == 0xFFFF: #Cable
945                                                                         service_name = _("Cable")
946                                                                 elif unsigned_orbpos == 0xEEEE: #Terrestrial
947                                                                         service_name = _("Terrestrial")
948                                                                 else:
949                                                                         if orbpos > 1800: # west
950                                                                                 orbpos = 3600 - orbpos
951                                                                                 h = _("W")
952                                                                         else:
953                                                                                 h = _("E")
954                                                                         service_name = ("%d.%d" + h) % (orbpos / 10, orbpos % 10)
955                                                         service.setName("%s - %s" % (service_name, service_type))
956                                                         self.servicelist.addService(service)
957                                                 cur_ref = self.session.nav.getCurrentlyPlayingServiceReference()
958                                                 if cur_ref:
959                                                         pos = self.service_types.rfind(':')
960                                                         refstr = '%s (channelID == %08x%04x%04x) && %s ORDER BY name' %(self.service_types[:pos+1],
961                                                                 cur_ref.getUnsignedData(4), # NAMESPACE
962                                                                 cur_ref.getUnsignedData(2), # TSID
963                                                                 cur_ref.getUnsignedData(3), # ONID
964                                                                 self.service_types[pos+1:])
965                                                         ref = eServiceReference(refstr)
966                                                         ref.setName(_("Current Transponder"))
967                                                         self.servicelist.addService(ref)
968                                                 self.servicelist.finishFill()
969                                                 if prev is not None:
970                                                         self.setCurrentSelection(prev)
971
972         def showProviders(self):
973                 if not self.pathChangeDisabled:
974                         refstr = '%s FROM PROVIDERS ORDER BY name'%(self.service_types)
975                         if not self.preEnterPath(refstr):
976                                 ref = eServiceReference(refstr)
977                                 if self.isBasePathEqual(ref):
978                                         self.pathUp()
979                                 else:
980                                         currentRoot = self.getRoot()
981                                         if currentRoot is None or currentRoot != ref:
982                                                 self.clearPath()
983                                                 self.enterPath(ref)
984
985         def changeBouquet(self, direction):
986                 if not self.pathChangeDisabled:
987                         if len(self.servicePath) > 1:
988                                 #when enter satellite root list we must do some magic stuff..
989                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
990                                 if self.isBasePathEqual(ref):
991                                         self.showSatellites()
992                                 else:
993                                         self.pathUp()
994                                 if direction < 0:
995                                         self.moveUp()
996                                 else:
997                                         self.moveDown()
998                                 ref = self.getCurrentSelection()
999                                 self.enterPath(ref)
1000
1001         def inBouquet(self):
1002                 if self.servicePath and self.servicePath[0] == self.bouquet_root:
1003                         return True
1004                 return False
1005
1006         def atBegin(self):
1007                 return self.servicelist.atBegin()
1008
1009         def atEnd(self):
1010                 return self.servicelist.atEnd()
1011
1012         def nextBouquet(self):
1013                 self.changeBouquet(+1)
1014
1015         def prevBouquet(self):
1016                 self.changeBouquet(-1)
1017
1018         def showFavourites(self):
1019                 if not self.pathChangeDisabled:
1020                         if not self.preEnterPath(self.bouquet_rootstr):
1021                                 if self.isBasePathEqual(self.bouquet_root):
1022                                         self.pathUp()
1023                                 else:
1024                                         currentRoot = self.getRoot()
1025                                         if currentRoot is None or currentRoot != self.bouquet_root:
1026                                                 self.clearPath()
1027                                                 self.enterPath(self.bouquet_root)
1028
1029         def keyNumberGlobal(self, number):
1030                 unichar = self.numericalTextInput.getKey(number)
1031                 charstr = unichar.encode("utf-8")
1032                 if len(charstr) == 1:
1033                         self.servicelist.moveToChar(charstr[0])
1034
1035         def keyAsciiCode(self):
1036                 unichar = unichr(getPrevAsciiCode())
1037                 charstr = unichar.encode("utf-8")
1038                 if len(charstr) == 1:
1039                         self.servicelist.moveToChar(charstr[0])
1040
1041         def getRoot(self):
1042                 return self.servicelist.getRoot()
1043
1044         def getCurrentSelection(self):
1045                 return self.servicelist.getCurrent()
1046
1047         def setCurrentSelection(self, service):
1048                 self.servicelist.setCurrent(service)
1049
1050         def getBouquetList(self):
1051                 bouquets = [ ]
1052                 serviceHandler = eServiceCenter.getInstance()
1053                 if config.usage.multibouquet.value:
1054                         list = serviceHandler.list(self.bouquet_root)
1055                         if list:
1056                                 while True:
1057                                         s = list.getNext()
1058                                         if not s.valid():
1059                                                 break
1060                                         if s.flags & eServiceReference.isDirectory:
1061                                                 info = serviceHandler.info(s)
1062                                                 if info:
1063                                                         bouquets.append((info.getName(s), s))
1064                                 return bouquets
1065                 else:
1066                         info = serviceHandler.info(self.bouquet_root)
1067                         if info:
1068                                 bouquets.append((info.getName(self.bouquet_root), self.bouquet_root))
1069                         return bouquets
1070                 return None
1071
1072         def keyNumber0(self, num):
1073                 if len(self.servicePath) > 1:
1074                         self.keyGoUp()
1075                 else:
1076                         self.keyNumberGlobal(num)
1077
1078         def keyGoUp(self):
1079                 if len(self.servicePath) > 1:
1080                         if self.isBasePathEqual(self.bouquet_root):
1081                                 self.showFavourites()
1082                         else:
1083                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
1084                                 if self.isBasePathEqual(ref):
1085                                         self.showSatellites()
1086                                 else:
1087                                         ref = eServiceReference('%s FROM PROVIDERS ORDER BY name'%(self.service_types))
1088                                         if self.isBasePathEqual(ref):
1089                                                 self.showProviders()
1090                                         else:
1091                                                 self.showAllServices()
1092
1093         def nextMarker(self):
1094                 self.servicelist.moveToNextMarker()
1095
1096         def prevMarker(self):
1097                 self.servicelist.moveToPrevMarker()
1098
1099 HISTORYSIZE = 20
1100
1101 #config for lastservice
1102 config.tv = ConfigSubsection()
1103 config.tv.lastservice = ConfigText()
1104 config.tv.lastroot = ConfigText()
1105 config.radio = ConfigSubsection()
1106 config.radio.lastservice = ConfigText()
1107 config.radio.lastroot = ConfigText()
1108 config.servicelist = ConfigSubsection()
1109 config.servicelist.lastmode = ConfigText(default = "tv")
1110
1111 class ChannelSelection(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, SelectionEventInfo):
1112         def __init__(self, session):
1113                 ChannelSelectionBase.__init__(self,session)
1114                 ChannelSelectionEdit.__init__(self)
1115                 ChannelSelectionEPG.__init__(self)
1116                 SelectionEventInfo.__init__(self)
1117
1118                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1119                         {
1120                                 "cancel": self.cancel,
1121                                 "ok": self.channelSelected,
1122                                 "keyRadio": self.setModeRadio,
1123                                 "keyTV": self.setModeTv,
1124                         })
1125
1126                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1127                         {
1128                                 iPlayableService.evStart: self.__evServiceStart,
1129                                 iPlayableService.evEnd: self.__evServiceEnd
1130                         })
1131
1132                 self.lastChannelRootTimer = eTimer()
1133                 self.lastChannelRootTimer.callback.append(self.__onCreate)
1134                 self.lastChannelRootTimer.start(100,True)
1135
1136                 self.history_tv = [ ]
1137                 self.history_radio = [ ]
1138                 self.history = self.history_tv
1139                 self.history_pos = 0
1140
1141                 self.lastservice = config.tv.lastservice
1142                 self.lastroot = config.tv.lastroot
1143                 self.revertMode = None
1144                 config.usage.multibouquet.addNotifier(self.multibouquet_config_changed)
1145                 self.new_service_played = False
1146                 self.onExecBegin.append(self.asciiOn)
1147
1148         def asciiOn(self):
1149                 rcinput = eRCInput.getInstance()
1150                 rcinput.setKeyboardMode(rcinput.kmAscii)
1151
1152         def asciiOff(self):
1153                 rcinput = eRCInput.getInstance()
1154                 rcinput.setKeyboardMode(rcinput.kmNone)
1155
1156         def multibouquet_config_changed(self, val):
1157                 self.recallBouquetMode()
1158
1159         def __evServiceStart(self):
1160                 service = self.session.nav.getCurrentService()
1161                 if service:
1162                         info = service.info()
1163                         if info:
1164                                 refstr = info.getInfoString(iServiceInformation.sServiceref)
1165                                 self.servicelist.setPlayableIgnoreService(eServiceReference(refstr))
1166
1167         def __evServiceEnd(self):
1168                 self.servicelist.setPlayableIgnoreService(eServiceReference())
1169
1170         def setMode(self):
1171                 self.restoreRoot()
1172                 lastservice=eServiceReference(self.lastservice.value)
1173                 if lastservice.valid():
1174                         self.setCurrentSelection(lastservice)
1175
1176         def setModeTv(self):
1177                 if self.revertMode is None and config.servicelist.lastmode.value == "radio":
1178                         self.revertMode = MODE_RADIO
1179                 self.history = self.history_tv
1180                 self.lastservice = config.tv.lastservice
1181                 self.lastroot = config.tv.lastroot
1182                 config.servicelist.lastmode.value = "tv"
1183                 self.setTvMode()
1184                 self.setMode()
1185
1186         def setModeRadio(self):
1187                 if self.revertMode is None and config.servicelist.lastmode.value == "tv":
1188                         self.revertMode = MODE_TV
1189                 if config.usage.e1like_radio_mode.value:
1190                         self.history = self.history_radio
1191                         self.lastservice = config.radio.lastservice
1192                         self.lastroot = config.radio.lastroot
1193                         config.servicelist.lastmode.value = "radio"
1194                         self.setRadioMode()
1195                         self.setMode()
1196
1197         def __onCreate(self):
1198                 if config.usage.e1like_radio_mode.value:
1199                         if config.servicelist.lastmode.value == "tv":
1200                                 self.setModeTv()
1201                         else:
1202                                 self.setModeRadio()
1203                 else:
1204                         self.setModeTv()
1205                 lastservice=eServiceReference(self.lastservice.value)
1206                 if lastservice.valid():
1207                         self.zap()
1208
1209         def channelSelected(self):
1210                 ref = self.getCurrentSelection()
1211                 if self.movemode:
1212                         self.toggleMoveMarked()
1213                 elif (ref.flags & 7) == 7:
1214                         self.enterPath(ref)
1215                 elif self.bouquet_mark_edit != OFF:
1216                         if not (self.bouquet_mark_edit == EDIT_ALTERNATIVES and ref.flags & eServiceReference.isGroup):
1217                                 self.doMark()
1218                 elif not (ref.flags & eServiceReference.isMarker): # no marker
1219                         root = self.getRoot()
1220                         if not root or not (root.flags & eServiceReference.isGroup):
1221                                 self.zap()
1222                                 self.asciiOff()
1223                                 self.close(ref)
1224
1225         #called from infoBar and channelSelected
1226         def zap(self):
1227                 self.revertMode=None
1228                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1229                 nref = self.getCurrentSelection()
1230                 if ref is None or ref != nref:
1231                         self.new_service_played = True
1232                         self.session.nav.playService(nref)
1233                         self.saveRoot()
1234                         self.saveChannel(nref)
1235                         config.servicelist.lastmode.save()
1236                         self.addToHistory(nref)
1237
1238         def newServicePlayed(self):
1239                 ret = self.new_service_played
1240                 self.new_service_played = False
1241                 return ret
1242
1243         def addToHistory(self, ref):
1244                 if self.servicePath is not None:
1245                         tmp=self.servicePath[:]
1246                         tmp.append(ref)
1247                         try:
1248                                 del self.history[self.history_pos+1:]
1249                         except:
1250                                 pass
1251                         self.history.append(tmp)
1252                         hlen = len(self.history)
1253                         if hlen > HISTORYSIZE:
1254                                 del self.history[0]
1255                                 hlen -= 1
1256                         self.history_pos = hlen-1
1257
1258         def historyBack(self):
1259                 hlen = len(self.history)
1260                 if hlen > 1 and self.history_pos > 0:
1261                         self.history_pos -= 1
1262                         self.setHistoryPath()
1263
1264         def historyNext(self):
1265                 hlen = len(self.history)
1266                 if hlen > 1 and self.history_pos < (hlen-1):
1267                         self.history_pos += 1
1268                         self.setHistoryPath()
1269
1270         def setHistoryPath(self):
1271                 path = self.history[self.history_pos][:]
1272                 ref = path.pop()
1273                 del self.servicePath[:]
1274                 self.servicePath += path
1275                 self.saveRoot()
1276                 root = path[-1]
1277                 cur_root = self.getRoot()
1278                 if cur_root and cur_root != root:
1279                         self.setRoot(root)
1280                 self.session.nav.playService(ref)
1281                 self.setCurrentSelection(ref)
1282                 self.saveChannel(ref)
1283
1284         def saveRoot(self):
1285                 path = ''
1286                 for i in self.servicePath:
1287                         path += i.toString()
1288                         path += ';'
1289                 if path and path != self.lastroot.value:
1290                         self.lastroot.value = path
1291                         self.lastroot.save()
1292
1293         def restoreRoot(self):
1294                 self.clearPath()
1295                 re = compile('.+?;')
1296                 tmp = re.findall(self.lastroot.value)
1297                 cnt = 0
1298                 for i in tmp:
1299                         self.servicePath.append(eServiceReference(i[:-1]))
1300                         cnt += 1
1301                 if cnt:
1302                         path = self.servicePath.pop()
1303                         self.enterPath(path)
1304                 else:
1305                         self.showFavourites()
1306                         self.saveRoot()
1307
1308         def preEnterPath(self, refstr):
1309                 if self.servicePath and self.servicePath[0] != eServiceReference(refstr):
1310                         pathstr = self.lastroot.value
1311                         if pathstr is not None and pathstr.find(refstr) == 0:
1312                                 self.restoreRoot()
1313                                 lastservice=eServiceReference(self.lastservice.value)
1314                                 if lastservice.valid():
1315                                         self.setCurrentSelection(lastservice)
1316                                 return True
1317                 return False
1318
1319         def saveChannel(self, ref):
1320                 if ref is not None:
1321                         refstr = ref.toString()
1322                 else:
1323                         refstr = ""
1324                 if refstr != self.lastservice.value:
1325                         self.lastservice.value = refstr
1326                         self.lastservice.save()
1327
1328         def setCurrentServicePath(self, path):
1329                 if self.history:
1330                         self.history[self.history_pos] = path
1331                 else:
1332                         self.history.append(path)
1333                 self.setHistoryPath()
1334
1335         def getCurrentServicePath(self):
1336                 if self.history:
1337                         return self.history[self.history_pos]
1338                 return None
1339
1340         def recallPrevService(self):
1341                 hlen = len(self.history)
1342                 if hlen > 1:
1343                         if self.history_pos == hlen-1:
1344                                 tmp = self.history[self.history_pos]
1345                                 self.history[self.history_pos] = self.history[self.history_pos-1]
1346                                 self.history[self.history_pos-1] = tmp
1347                         else:
1348                                 tmp = self.history[self.history_pos+1]
1349                                 self.history[self.history_pos+1] = self.history[self.history_pos]
1350                                 self.history[self.history_pos] = tmp
1351                         self.setHistoryPath()
1352
1353         def cancel(self):
1354                 if self.revertMode is None:
1355                         self.restoreRoot()
1356                         lastservice=eServiceReference(self.lastservice.value)
1357                         if lastservice.valid() and self.getCurrentSelection() != lastservice:
1358                                 self.setCurrentSelection(lastservice)
1359                 elif self.revertMode == MODE_TV:
1360                         self.setModeTv()
1361                 elif self.revertMode == MODE_RADIO:
1362                         self.setModeRadio()
1363                 self.revertMode = None
1364                 self.asciiOff()
1365                 self.close(None)
1366
1367 class RadioInfoBar(Screen):
1368         def __init__(self, session):
1369                 Screen.__init__(self, session)
1370                 self["RdsDecoder"] = RdsDecoder(self.session.nav)
1371
1372 class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, InfoBarBase):
1373         ALLOW_SUSPEND = True
1374
1375         def __init__(self, session, infobar):
1376                 ChannelSelectionBase.__init__(self, session)
1377                 ChannelSelectionEdit.__init__(self)
1378                 ChannelSelectionEPG.__init__(self)
1379                 InfoBarBase.__init__(self)
1380                 self.infobar = infobar
1381                 self.onLayoutFinish.append(self.onCreate)
1382
1383                 self.info = session.instantiateDialog(RadioInfoBar) # our simple infobar
1384
1385                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1386                         {
1387                                 "keyTV": self.closeRadio,
1388                                 "keyRadio": self.closeRadio,
1389                                 "cancel": self.closeRadio,
1390                                 "ok": self.channelSelected,
1391                         })
1392
1393                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1394                         {
1395                                 iPlayableService.evStart: self.__evServiceStart,
1396                                 iPlayableService.evEnd: self.__evServiceEnd
1397                         })
1398
1399 ########## RDS Radiotext / Rass Support BEGIN
1400                 self.infobar = infobar # reference to real infobar (the one and only)
1401                 self["RdsDecoder"] = self.info["RdsDecoder"]
1402                 self["RdsActions"] = HelpableActionMap(self, "InfobarRdsActions",
1403                 {
1404                         "startRassInteractive": (self.startRassInteractive, _("View Rass interactive..."))
1405                 },-1)
1406                 self["RdsActions"].setEnabled(False)
1407                 infobar.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
1408                 self.onClose.append(self.__onClose)
1409
1410         def __onClose(self):
1411                 lastservice=eServiceReference(config.tv.lastservice.value)
1412                 self.session.nav.playService(lastservice)
1413
1414         def startRassInteractive(self):
1415                 self.info.hide();
1416                 self.infobar.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
1417
1418         def RassInteractiveClosed(self):
1419                 self.info.show()
1420                 self.infobar.rass_interactive = None
1421                 self.infobar.RassSlidePicChanged()
1422
1423         def RassInteractivePossibilityChanged(self, state):
1424                 self["RdsActions"].setEnabled(state)
1425 ########## RDS Radiotext / Rass Support END
1426
1427         def closeRadio(self):
1428                 self.infobar.rds_display.onRassInteractivePossibilityChanged.remove(self.RassInteractivePossibilityChanged)
1429                 self.info.hide()
1430                 #set previous tv service
1431                 self.close(None)
1432
1433         def __evServiceStart(self):
1434                 service = self.session.nav.getCurrentService()
1435                 if service:
1436                         info = service.info()
1437                         if info:
1438                                 refstr = info.getInfoString(iServiceInformation.sServiceref)
1439                                 self.servicelist.setPlayableIgnoreService(eServiceReference(refstr))
1440
1441         def __evServiceEnd(self):
1442                 self.servicelist.setPlayableIgnoreService(eServiceReference())
1443
1444         def saveRoot(self):
1445                 path = ''
1446                 for i in self.servicePathRadio:
1447                         path += i.toString()
1448                         path += ';'
1449                 if path and path != config.radio.lastroot.value:
1450                         config.radio.lastroot.value = path
1451                         config.radio.lastroot.save()
1452
1453         def restoreRoot(self):
1454                 self.clearPath()
1455                 re = compile('.+?;')
1456                 tmp = re.findall(config.radio.lastroot.value)
1457                 cnt = 0
1458                 for i in tmp:
1459                         self.servicePathRadio.append(eServiceReference(i[:-1]))
1460                         cnt += 1
1461                 if cnt:
1462                         path = self.servicePathRadio.pop()
1463                         self.enterPath(path)
1464                 else:
1465                         self.showFavourites()
1466                         self.saveRoot()
1467
1468         def preEnterPath(self, refstr):
1469                 if self.servicePathRadio and self.servicePathRadio[0] != eServiceReference(refstr):
1470                         pathstr = config.radio.lastroot.value
1471                         if pathstr is not None and pathstr.find(refstr) == 0:
1472                                 self.restoreRoot()
1473                                 lastservice=eServiceReference(config.radio.lastservice.value)
1474                                 if lastservice.valid():
1475                                         self.setCurrentSelection(lastservice)
1476                                 return True
1477                 return False
1478
1479         def onCreate(self):
1480                 self.setRadioMode()
1481                 self.restoreRoot()
1482                 lastservice=eServiceReference(config.radio.lastservice.value)
1483                 if lastservice.valid():
1484                         self.servicelist.setCurrent(lastservice)
1485                         self.session.nav.playService(lastservice)
1486                 else:
1487                         self.session.nav.stopService()
1488                 self.info.show()
1489
1490         def channelSelected(self): # just return selected service
1491                 ref = self.getCurrentSelection()
1492                 if self.movemode:
1493                         self.toggleMoveMarked()
1494                 elif (ref.flags & 7) == 7:
1495                         self.enterPath(ref)
1496                 elif self.bouquet_mark_edit != OFF:
1497                         if not (self.bouquet_mark_edit == EDIT_ALTERNATIVES and ref.flags & eServiceReference.isGroup):
1498                                 self.doMark()
1499                 elif not (ref.flags & eServiceReference.isMarker): # no marker
1500                         cur_root = self.getRoot()
1501                         if not cur_root or not (cur_root.flags & eServiceReference.isGroup):
1502                                 playingref = self.session.nav.getCurrentlyPlayingServiceReference()
1503                                 if playingref is None or playingref != ref:
1504                                         self.session.nav.playService(ref)
1505                                         config.radio.lastservice.value = ref.toString()
1506                                         config.radio.lastservice.save()
1507                                 self.saveRoot()
1508
1509 class SimpleChannelSelection(ChannelSelectionBase):
1510         def __init__(self, session, title):
1511                 ChannelSelectionBase.__init__(self, session)
1512                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1513                         {
1514                                 "cancel": self.close,
1515                                 "ok": self.channelSelected,
1516                                 "keyRadio": self.setModeRadio,
1517                                 "keyTV": self.setModeTv,
1518                         })
1519                 self.title = title
1520                 self.onLayoutFinish.append(self.layoutFinished)
1521
1522         def layoutFinished(self):
1523                 self.setModeTv()
1524
1525         def channelSelected(self): # just return selected service
1526                 ref = self.getCurrentSelection()
1527                 if (ref.flags & 7) == 7:
1528                         self.enterPath(ref)
1529                 elif not (ref.flags & eServiceReference.isMarker):
1530                         ref = self.getCurrentSelection()
1531                         self.close(ref)
1532
1533         def setModeTv(self):
1534                 self.setTvMode()
1535                 self.showFavourites()
1536
1537         def setModeRadio(self):
1538                 self.setRadioMode()
1539                 self.showFavourites()