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