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