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