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