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