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