enigma2 (20101216 rel31 -> 20110928 rel32)
[enigma2.git] / usr / lib / enigma2 / 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                                                         elif service.getPath().find("numCAIDs") != -1:
980                                                                 service_type = _("Services") + " FTA"
981                                                         else:
982                                                                 service_type = _("Services")
983                                                         try:
984                                                                 # why we need this cast?
985                                                                 service_name = str(nimmanager.getSatDescription(orbpos))
986                                                         except:
987                                                                 if unsigned_orbpos == 0xFFFF: #Cable
988                                                                         service_name = _("Cable")
989                                                                 elif unsigned_orbpos == 0xEEEE: #Terrestrial
990                                                                         service_name = _("Terrestrial")
991                                                                 else:
992                                                                         if orbpos > 1800: # west
993                                                                                 orbpos = 3600 - orbpos
994                                                                                 h = _("W")
995                                                                         else:
996                                                                                 h = _("E")
997                                                                         service_name = ("%d.%d" + h) % (orbpos / 10, orbpos % 10)
998                                                         service.setName("%s - %s" % (service_name, service_type))
999                                                         self.servicelist.addService(service)
1000                                                 cur_ref = self.session.nav.getCurrentlyPlayingServiceReference()
1001                                                 if cur_ref:
1002                                                         pos = self.service_types.rfind(':')
1003                                                         refstr = '%s (channelID == %08x%04x%04x) && %s ORDER BY name' %(self.service_types[:pos+1],
1004                                                                 cur_ref.getUnsignedData(4), # NAMESPACE
1005                                                                 cur_ref.getUnsignedData(2), # TSID
1006                                                                 cur_ref.getUnsignedData(3), # ONID
1007                                                                 self.service_types[pos+1:])
1008                                                         ref = eServiceReference(refstr)
1009                                                         ref.setName(_("Current Transponder"))
1010                                                         self.servicelist.addService(ref)
1011                                                 self.servicelist.finishFill()
1012                                                 if prev is not None:
1013                                                         self.setCurrentSelection(prev)
1014
1015         def showProviders(self):
1016                 if not self.pathChangeDisabled:
1017                         refstr = '%s FROM PROVIDERS ORDER BY name'%(self.service_types)
1018                         if not self.preEnterPath(refstr):
1019                                 ref = eServiceReference(refstr)
1020                                 if self.isBasePathEqual(ref):
1021                                         self.pathUp()
1022                                 else:
1023                                         currentRoot = self.getRoot()
1024                                         if currentRoot is None or currentRoot != ref:
1025                                                 self.clearPath()
1026                                                 self.enterPath(ref)
1027
1028         def changeBouquet(self, direction):
1029                 if not self.pathChangeDisabled:
1030                         if len(self.servicePath) > 1:
1031                                 #when enter satellite root list we must do some magic stuff..
1032                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
1033                                 if self.isBasePathEqual(ref):
1034                                         self.showSatellites()
1035                                 else:
1036                                         self.pathUp()
1037                                 if direction < 0:
1038                                         self.moveUp()
1039                                 else:
1040                                         self.moveDown()
1041                                 ref = self.getCurrentSelection()
1042                                 self.enterPath(ref)
1043
1044         def inBouquet(self):
1045                 if self.servicePath and self.servicePath[0] == self.bouquet_root:
1046                         return True
1047                 return False
1048
1049         def atBegin(self):
1050                 return self.servicelist.atBegin()
1051
1052         def atEnd(self):
1053                 return self.servicelist.atEnd()
1054
1055         def nextBouquet(self):
1056                 self.changeBouquet(+1)
1057
1058         def prevBouquet(self):
1059                 self.changeBouquet(-1)
1060
1061         def showFavourites(self):
1062                 if not self.pathChangeDisabled:
1063                         if not self.preEnterPath(self.bouquet_rootstr):
1064                                 if self.isBasePathEqual(self.bouquet_root):
1065                                         self.pathUp()
1066                                 else:
1067                                         currentRoot = self.getRoot()
1068                                         if currentRoot is None or currentRoot != self.bouquet_root:
1069                                                 self.clearPath()
1070                                                 self.enterPath(self.bouquet_root)
1071
1072         def keyNumberGlobal(self, number):
1073                 unichar = self.numericalTextInput.getKey(number)
1074                 charstr = unichar.encode("utf-8")
1075                 if len(charstr) == 1:
1076                         self.servicelist.moveToChar(charstr[0])
1077
1078         def keyAsciiCode(self):
1079                 unichar = unichr(getPrevAsciiCode())
1080                 charstr = unichar.encode("utf-8")
1081                 if len(charstr) == 1:
1082                         self.servicelist.moveToChar(charstr[0])
1083
1084         def getRoot(self):
1085                 return self.servicelist.getRoot()
1086
1087         def getCurrentSelection(self):
1088                 return self.servicelist.getCurrent()
1089
1090         def setCurrentSelection(self, service):
1091                 self.servicelist.setCurrent(service)
1092
1093         def getBouquetList(self):
1094                 bouquets = [ ]
1095                 serviceHandler = eServiceCenter.getInstance()
1096                 if config.usage.multibouquet.value:
1097                         list = serviceHandler.list(self.bouquet_root)
1098                         if list:
1099                                 while True:
1100                                         s = list.getNext()
1101                                         if not s.valid():
1102                                                 break
1103                                         if s.flags & eServiceReference.isDirectory:
1104                                                 info = serviceHandler.info(s)
1105                                                 if info:
1106                                                         bouquets.append((info.getName(s), s))
1107                                 return bouquets
1108                 else:
1109                         info = serviceHandler.info(self.bouquet_root)
1110                         if info:
1111                                 bouquets.append((info.getName(self.bouquet_root), self.bouquet_root))
1112                         return bouquets
1113                 return None
1114
1115         def keyNumber0(self, num):
1116                 if len(self.servicePath) > 1:
1117                         self.keyGoUp()
1118                 else:
1119                         self.keyNumberGlobal(num)
1120
1121         def keyGoUp(self):
1122                 if len(self.servicePath) > 1:
1123                         if self.isBasePathEqual(self.bouquet_root):
1124                                 self.showFavourites()
1125                         else:
1126                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
1127                                 if self.isBasePathEqual(ref):
1128                                         self.showSatellites()
1129                                 else:
1130                                         ref = eServiceReference('%s FROM PROVIDERS ORDER BY name'%(self.service_types))
1131                                         if self.isBasePathEqual(ref):
1132                                                 self.showProviders()
1133                                         else:
1134                                                 self.showAllServices()
1135
1136         def nextMarker(self):
1137                 self.servicelist.moveToNextMarker()
1138
1139         def prevMarker(self):
1140                 self.servicelist.moveToPrevMarker()
1141
1142 HISTORYSIZE = 20
1143
1144 #config for lastservice
1145 config.tv = ConfigSubsection()
1146 config.tv.lastservice = ConfigText()
1147 config.tv.lastroot = ConfigText()
1148 config.radio = ConfigSubsection()
1149 config.radio.lastservice = ConfigText()
1150 config.radio.lastroot = ConfigText()
1151 config.servicelist = ConfigSubsection()
1152 config.servicelist.lastmode = ConfigText(default = "tv")
1153
1154 class ChannelSelection(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, SelectionEventInfo):
1155         def __init__(self, session):
1156                 ChannelSelectionBase.__init__(self,session)
1157                 ChannelSelectionEdit.__init__(self)
1158                 ChannelSelectionEPG.__init__(self)
1159                 SelectionEventInfo.__init__(self)
1160
1161                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1162                         {
1163                                 "cancel": self.cancel,
1164                                 "ok": self.channelSelected,
1165                                 "keyRadio": self.setModeRadio,
1166                                 "keyTV": self.setModeTv,
1167                         })
1168
1169                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1170                         {
1171                                 iPlayableService.evStart: self.__evServiceStart,
1172                                 iPlayableService.evEnd: self.__evServiceEnd
1173                         })
1174
1175                 self.lastChannelRootTimer = eTimer()
1176                 self.lastChannelRootTimer.callback.append(self.__onCreate)
1177                 self.lastChannelRootTimer.start(100,True)
1178
1179                 self.history_tv = [ ]
1180                 self.history_radio = [ ]
1181                 self.history = self.history_tv
1182                 self.history_pos = 0
1183
1184                 self.lastservice = config.tv.lastservice
1185                 self.lastroot = config.tv.lastroot
1186                 self.revertMode = None
1187                 config.usage.multibouquet.addNotifier(self.multibouquet_config_changed)
1188                 self.new_service_played = False
1189                 self.onExecBegin.append(self.asciiOn)
1190
1191         def asciiOn(self):
1192                 rcinput = eRCInput.getInstance()
1193                 rcinput.setKeyboardMode(rcinput.kmAscii)
1194
1195         def asciiOff(self):
1196                 rcinput = eRCInput.getInstance()
1197                 rcinput.setKeyboardMode(rcinput.kmNone)
1198
1199         def multibouquet_config_changed(self, val):
1200                 self.recallBouquetMode()
1201
1202         def __evServiceStart(self):
1203                 service = self.session.nav.getCurrentService()
1204                 if service:
1205                         info = service.info()
1206                         if info:
1207                                 refstr = info.getInfoString(iServiceInformation.sServiceref)
1208                                 self.servicelist.setPlayableIgnoreService(eServiceReference(refstr))
1209
1210         def __evServiceEnd(self):
1211                 self.servicelist.setPlayableIgnoreService(eServiceReference())
1212
1213         def setMode(self):
1214                 self.rootChanged = True
1215                 self.restoreRoot()
1216                 lastservice=eServiceReference(self.lastservice.value)
1217                 if lastservice.valid():
1218                         self.setCurrentSelection(lastservice)
1219
1220         def setModeTv(self):
1221                 if self.revertMode is None and config.servicelist.lastmode.value == "radio":
1222                         self.revertMode = MODE_RADIO
1223                 self.history = self.history_tv
1224                 self.lastservice = config.tv.lastservice
1225                 self.lastroot = config.tv.lastroot
1226                 config.servicelist.lastmode.value = "tv"
1227                 self.setTvMode()
1228                 self.setMode()
1229
1230         def setModeRadio(self):
1231                 if self.revertMode is None and config.servicelist.lastmode.value == "tv":
1232                         self.revertMode = MODE_TV
1233                 if config.usage.e1like_radio_mode.value:
1234                         self.history = self.history_radio
1235                         self.lastservice = config.radio.lastservice
1236                         self.lastroot = config.radio.lastroot
1237                         config.servicelist.lastmode.value = "radio"
1238                         self.setRadioMode()
1239                         self.setMode()
1240
1241         def __onCreate(self):
1242                 if config.usage.e1like_radio_mode.value:
1243                         if config.servicelist.lastmode.value == "tv":
1244                                 self.setModeTv()
1245                         else:
1246                                 self.setModeRadio()
1247                 else:
1248                         self.setModeTv()
1249                 lastservice=eServiceReference(self.lastservice.value)
1250                 if lastservice.valid():
1251                         self.zap()
1252
1253         def channelSelected(self):
1254                 ref = self.getCurrentSelection()
1255                 if self.movemode:
1256                         self.toggleMoveMarked()
1257                 elif (ref.flags & 7) == 7:
1258                         self.enterPath(ref)
1259                 elif self.bouquet_mark_edit != OFF:
1260                         if not (self.bouquet_mark_edit == EDIT_ALTERNATIVES and ref.flags & eServiceReference.isGroup):
1261                                 self.doMark()
1262                 elif not (ref.flags & eServiceReference.isMarker): # no marker
1263                         root = self.getRoot()
1264                         if not root or not (root.flags & eServiceReference.isGroup):
1265                                 self.zap()
1266                                 self.asciiOff()
1267                                 self.close(ref)
1268
1269         #called from infoBar and channelSelected
1270         def zap(self):
1271                 self.revertMode=None
1272                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1273                 nref = self.getCurrentSelection()
1274                 if ref is None or ref != nref:
1275                         self.new_service_played = True
1276                         self.session.nav.playService(nref)
1277                         self.saveRoot()
1278                         self.saveChannel(nref)
1279                         config.servicelist.lastmode.save()
1280                         self.addToHistory(nref)
1281
1282         def newServicePlayed(self):
1283                 ret = self.new_service_played
1284                 self.new_service_played = False
1285                 return ret
1286
1287         def addToHistory(self, ref):
1288                 if self.servicePath is not None:
1289                         tmp=self.servicePath[:]
1290                         tmp.append(ref)
1291                         try:
1292                                 del self.history[self.history_pos+1:]
1293                         except:
1294                                 pass
1295                         self.history.append(tmp)
1296                         hlen = len(self.history)
1297                         if hlen > HISTORYSIZE:
1298                                 del self.history[0]
1299                                 hlen -= 1
1300                         self.history_pos = hlen-1
1301
1302         def historyBack(self):
1303                 hlen = len(self.history)
1304                 if hlen > 1 and self.history_pos > 0:
1305                         self.history_pos -= 1
1306                         self.setHistoryPath()
1307
1308         def historyNext(self):
1309                 hlen = len(self.history)
1310                 if hlen > 1 and self.history_pos < (hlen-1):
1311                         self.history_pos += 1
1312                         self.setHistoryPath()
1313
1314         def setHistoryPath(self):
1315                 path = self.history[self.history_pos][:]
1316                 ref = path.pop()
1317                 del self.servicePath[:]
1318                 self.servicePath += path
1319                 self.saveRoot()
1320                 root = path[-1]
1321                 cur_root = self.getRoot()
1322                 if cur_root and cur_root != root:
1323                         self.setRoot(root)
1324                 self.session.nav.playService(ref)
1325                 self.setCurrentSelection(ref)
1326                 self.saveChannel(ref)
1327
1328         def saveRoot(self):
1329                 path = ''
1330                 for i in self.servicePath:
1331                         path += i.toString()
1332                         path += ';'
1333                 if path and path != self.lastroot.value:
1334                         self.lastroot.value = path
1335                         self.lastroot.save()
1336
1337         def restoreRoot(self):
1338                 tmp = [x for x in self.lastroot.value.split(';') if x != '']
1339                 current = [x.toString() for x in self.servicePath]
1340                 if tmp != current or self.rootChanged:
1341                         self.clearPath()
1342                         cnt = 0
1343                         for i in tmp:
1344                                 self.servicePath.append(eServiceReference(i))
1345                                 cnt += 1
1346                         if cnt:
1347                                 path = self.servicePath.pop()
1348                                 self.enterPath(path)
1349                         else:
1350                                 self.showFavourites()
1351                                 self.saveRoot()
1352                         self.rootChanged = False
1353
1354         def preEnterPath(self, refstr):
1355                 if self.servicePath and self.servicePath[0] != eServiceReference(refstr):
1356                         pathstr = self.lastroot.value
1357                         if pathstr is not None and pathstr.find(refstr) == 0:
1358                                 self.restoreRoot()
1359                                 lastservice=eServiceReference(self.lastservice.value)
1360                                 if lastservice.valid():
1361                                         self.setCurrentSelection(lastservice)
1362                                 return True
1363                 return False
1364
1365         def saveChannel(self, ref):
1366                 if ref is not None:
1367                         refstr = ref.toString()
1368                 else:
1369                         refstr = ""
1370                 if refstr != self.lastservice.value:
1371                         self.lastservice.value = refstr
1372                         self.lastservice.save()
1373
1374         def setCurrentServicePath(self, path):
1375                 if self.history:
1376                         self.history[self.history_pos] = path
1377                 else:
1378                         self.history.append(path)
1379                 self.setHistoryPath()
1380
1381         def getCurrentServicePath(self):
1382                 if self.history:
1383                         return self.history[self.history_pos]
1384                 return None
1385
1386         def recallPrevService(self):
1387                 hlen = len(self.history)
1388                 if hlen > 1:
1389                         if self.history_pos == hlen-1:
1390                                 tmp = self.history[self.history_pos]
1391                                 self.history[self.history_pos] = self.history[self.history_pos-1]
1392                                 self.history[self.history_pos-1] = tmp
1393                         else:
1394                                 tmp = self.history[self.history_pos+1]
1395                                 self.history[self.history_pos+1] = self.history[self.history_pos]
1396                                 self.history[self.history_pos] = tmp
1397                         self.setHistoryPath()
1398
1399         def cancel(self):
1400                 if self.revertMode is None:
1401                         self.restoreRoot()
1402                         lastservice=eServiceReference(self.lastservice.value)
1403                         if lastservice.valid() and self.getCurrentSelection() != lastservice:
1404                                 self.setCurrentSelection(lastservice)
1405                 elif self.revertMode == MODE_TV:
1406                         self.setModeTv()
1407                 elif self.revertMode == MODE_RADIO:
1408                         self.setModeRadio()
1409                 self.revertMode = None
1410                 self.asciiOff()
1411                 self.close(None)
1412
1413 class RadioInfoBar(Screen):
1414         def __init__(self, session):
1415                 Screen.__init__(self, session)
1416                 self["RdsDecoder"] = RdsDecoder(self.session.nav)
1417
1418 class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, InfoBarBase):
1419         ALLOW_SUSPEND = True
1420
1421         def __init__(self, session, infobar):
1422                 ChannelSelectionBase.__init__(self, session)
1423                 ChannelSelectionEdit.__init__(self)
1424                 ChannelSelectionEPG.__init__(self)
1425                 InfoBarBase.__init__(self)
1426                 self.infobar = infobar
1427                 self.onLayoutFinish.append(self.onCreate)
1428
1429                 self.info = session.instantiateDialog(RadioInfoBar) # our simple infobar
1430
1431                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1432                         {
1433                                 "keyTV": self.cancel,
1434                                 "keyRadio": self.cancel,
1435                                 "cancel": self.cancel,
1436                                 "ok": self.channelSelected,
1437                         })
1438
1439                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1440                         {
1441                                 iPlayableService.evStart: self.__evServiceStart,
1442                                 iPlayableService.evEnd: self.__evServiceEnd
1443                         })
1444
1445 ########## RDS Radiotext / Rass Support BEGIN
1446                 self.infobar = infobar # reference to real infobar (the one and only)
1447                 self["RdsDecoder"] = self.info["RdsDecoder"]
1448                 self["RdsActions"] = HelpableActionMap(self, "InfobarRdsActions",
1449                 {
1450                         "startRassInteractive": (self.startRassInteractive, _("View Rass interactive..."))
1451                 },-1)
1452                 self["RdsActions"].setEnabled(False)
1453                 infobar.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
1454                 self.onClose.append(self.__onClose)
1455
1456         def __onClose(self):
1457                 lastservice=eServiceReference(config.tv.lastservice.value)
1458                 self.session.nav.playService(lastservice)
1459
1460         def startRassInteractive(self):
1461                 self.info.hide();
1462                 self.infobar.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
1463
1464         def RassInteractiveClosed(self):
1465                 self.info.show()
1466                 self.infobar.rass_interactive = None
1467                 self.infobar.RassSlidePicChanged()
1468
1469         def RassInteractivePossibilityChanged(self, state):
1470                 self["RdsActions"].setEnabled(state)
1471 ########## RDS Radiotext / Rass Support END
1472
1473         def cancel(self):
1474                 self.infobar.rds_display.onRassInteractivePossibilityChanged.remove(self.RassInteractivePossibilityChanged)
1475                 self.info.hide()
1476                 #set previous tv service
1477                 self.close(None)
1478
1479         def __evServiceStart(self):
1480                 service = self.session.nav.getCurrentService()
1481                 if service:
1482                         info = service.info()
1483                         if info:
1484                                 refstr = info.getInfoString(iServiceInformation.sServiceref)
1485                                 self.servicelist.setPlayableIgnoreService(eServiceReference(refstr))
1486
1487         def __evServiceEnd(self):
1488                 self.servicelist.setPlayableIgnoreService(eServiceReference())
1489
1490         def saveRoot(self):
1491                 path = ''
1492                 for i in self.servicePathRadio:
1493                         path += i.toString()
1494                         path += ';'
1495                 if path and path != config.radio.lastroot.value:
1496                         config.radio.lastroot.value = path
1497                         config.radio.lastroot.save()
1498
1499         def restoreRoot(self):
1500                 tmp = [x for x in config.radio.lastroot.value.split(';') if x != '']
1501                 current = [x.toString() for x in self.servicePathRadio]
1502                 if tmp != current or self.rootChanged:
1503                         self.clearPath()
1504                         cnt = 0
1505                         for i in tmp:
1506                                 self.servicePathRadio.append(eServiceReference(i))
1507                                 cnt += 1
1508                         if cnt:
1509                                 path = self.servicePathRadio.pop()
1510                                 self.enterPath(path)
1511                         else:
1512                                 self.showFavourites()
1513                                 self.saveRoot()
1514                         self.rootChanged = False
1515
1516         def preEnterPath(self, refstr):
1517                 if self.servicePathRadio and self.servicePathRadio[0] != eServiceReference(refstr):
1518                         pathstr = config.radio.lastroot.value
1519                         if pathstr is not None and pathstr.find(refstr) == 0:
1520                                 self.restoreRoot()
1521                                 lastservice=eServiceReference(config.radio.lastservice.value)
1522                                 if lastservice.valid():
1523                                         self.setCurrentSelection(lastservice)
1524                                 return True
1525                 return False
1526
1527         def onCreate(self):
1528                 self.setRadioMode()
1529                 self.restoreRoot()
1530                 lastservice=eServiceReference(config.radio.lastservice.value)
1531                 if lastservice.valid():
1532                         self.servicelist.setCurrent(lastservice)
1533                         self.session.nav.playService(lastservice)
1534                 else:
1535                         self.session.nav.stopService()
1536                 self.info.show()
1537
1538         def channelSelected(self): # just return selected service
1539                 ref = self.getCurrentSelection()
1540                 if self.movemode:
1541                         self.toggleMoveMarked()
1542                 elif (ref.flags & 7) == 7:
1543                         self.enterPath(ref)
1544                 elif self.bouquet_mark_edit != OFF:
1545                         if not (self.bouquet_mark_edit == EDIT_ALTERNATIVES and ref.flags & eServiceReference.isGroup):
1546                                 self.doMark()
1547                 elif not (ref.flags & eServiceReference.isMarker): # no marker
1548                         cur_root = self.getRoot()
1549                         if not cur_root or not (cur_root.flags & eServiceReference.isGroup):
1550                                 playingref = self.session.nav.getCurrentlyPlayingServiceReference()
1551                                 if playingref is None or playingref != ref:
1552                                         self.session.nav.playService(ref)
1553                                         config.radio.lastservice.value = ref.toString()
1554                                         config.radio.lastservice.save()
1555                                 self.saveRoot()
1556
1557 class SimpleChannelSelection(ChannelSelectionBase):
1558         def __init__(self, session, title):
1559                 ChannelSelectionBase.__init__(self, session)
1560                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1561                         {
1562                                 "cancel": self.close,
1563                                 "ok": self.channelSelected,
1564                                 "keyRadio": self.setModeRadio,
1565                                 "keyTV": self.setModeTv,
1566                         })
1567                 self.title = title
1568                 self.onLayoutFinish.append(self.layoutFinished)
1569
1570         def layoutFinished(self):
1571                 self.setModeTv()
1572
1573         def channelSelected(self): # just return selected service
1574                 ref = self.getCurrentSelection()
1575                 if (ref.flags & 7) == 7:
1576                         self.enterPath(ref)
1577                 elif not (ref.flags & eServiceReference.isMarker):
1578                         ref = self.getCurrentSelection()
1579                         self.close(ref)
1580
1581         def setModeTv(self):
1582                 self.setTvMode()
1583                 self.showFavourites()
1584
1585         def setModeRadio(self):
1586                 self.setRadioMode()
1587                 self.showFavourites()