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