Merge commit 'origin/master' into tmbinc/FixTimingBugs
[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 # this makes it much simple to implement a selectable radio or tv mode :)
657 service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 195) || (type == 25) || (type == 134)'
658 service_types_radio = '1:7:2:0:0:0:0:0:0:0:(type == 2)'
659
660 class ChannelSelectionBase(Screen):
661         def __init__(self, session):
662                 Screen.__init__(self, session)
663
664                 self["key_red"] = Button(_("All"))
665                 self["key_green"] = Button(_("Satellites"))
666                 self["key_yellow"] = Button(_("Provider"))
667                 self["key_blue"] = Button(_("Favourites"))
668
669                 self["list"] = ServiceList()
670                 self.servicelist = self["list"]
671
672                 self.numericalTextInput = NumericalTextInput()
673                 self.numericalTextInput.setUseableChars(u'1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ')
674
675                 self.servicePathTV = [ ]
676                 self.servicePathRadio = [ ]
677                 self.servicePath = [ ]
678
679                 self.mode = MODE_TV
680
681                 self.pathChangeDisabled = False
682
683                 self.bouquetNumOffsetCache = { }
684
685                 self["ChannelSelectBaseActions"] = NumberActionMap(["ChannelSelectBaseActions", "NumberActions", "InputAsciiActions"],
686                         {
687                                 "showFavourites": self.showFavourites,
688                                 "showAllServices": self.showAllServices,
689                                 "showProviders": self.showProviders,
690                                 "showSatellites": self.showSatellites,
691                                 "nextBouquet": self.nextBouquet,
692                                 "prevBouquet": self.prevBouquet,
693                                 "nextMarker": self.nextMarker,
694                                 "prevMarker": self.prevMarker,
695                                 "gotAsciiCode": self.keyAsciiCode,
696                                 "1": self.keyNumberGlobal,
697                                 "2": self.keyNumberGlobal,
698                                 "3": self.keyNumberGlobal,
699                                 "4": self.keyNumberGlobal,
700                                 "5": self.keyNumberGlobal,
701                                 "6": self.keyNumberGlobal,
702                                 "7": self.keyNumberGlobal,
703                                 "8": self.keyNumberGlobal,
704                                 "9": self.keyNumberGlobal,
705                                 "0": self.keyNumber0
706                         })
707                 self.recallBouquetMode()
708
709         def getBouquetNumOffset(self, bouquet):
710                 if not config.usage.multibouquet.value:
711                         return 0
712                 str = bouquet.toString()
713                 offsetCount = 0
714                 if not self.bouquetNumOffsetCache.has_key(str):
715                         serviceHandler = eServiceCenter.getInstance()
716                         bouquetlist = serviceHandler.list(self.bouquet_root)
717                         if not bouquetlist is None:
718                                 while True:
719                                         bouquetIterator = bouquetlist.getNext()
720                                         if not bouquetIterator.valid(): #end of list
721                                                 break
722                                         self.bouquetNumOffsetCache[bouquetIterator.toString()]=offsetCount
723                                         if not (bouquetIterator.flags & eServiceReference.isDirectory):
724                                                 continue
725                                         servicelist = serviceHandler.list(bouquetIterator)
726                                         if not servicelist is None:
727                                                 while True:
728                                                         serviceIterator = servicelist.getNext()
729                                                         if not serviceIterator.valid(): #check if end of list
730                                                                 break
731                                                         playable = not (serviceIterator.flags & (eServiceReference.isDirectory|eServiceReference.isMarker))
732                                                         if playable:
733                                                                 offsetCount += 1
734                 return self.bouquetNumOffsetCache.get(str, offsetCount)
735
736         def recallBouquetMode(self):
737                 if self.mode == MODE_TV:
738                         self.service_types = service_types_tv
739                         if config.usage.multibouquet.value:
740                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
741                         else:
742                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'%(self.service_types)
743                 else:
744                         self.service_types = service_types_radio
745                         if config.usage.multibouquet.value:
746                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.radio" ORDER BY bouquet'
747                         else:
748                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.radio" ORDER BY bouquet'%(self.service_types)
749                 self.bouquet_root = eServiceReference(self.bouquet_rootstr)
750
751         def setTvMode(self):
752                 self.mode = MODE_TV
753                 self.servicePath = self.servicePathTV
754                 self.recallBouquetMode()
755                 title = self.instance.getTitle()
756                 pos = title.find(" (")
757                 if pos != -1:
758                         title = title[:pos]
759                 title += " (TV)"
760                 self.setTitle(title)
761
762         def setRadioMode(self):
763                 self.mode = MODE_RADIO
764                 self.servicePath = self.servicePathRadio
765                 self.recallBouquetMode()
766                 title = self.instance.getTitle()
767                 pos = title.find(" (")
768                 if pos != -1:
769                         title = title[:pos]
770                 title += " (Radio)"
771                 self.setTitle(title)
772
773         def setRoot(self, root, justSet=False):
774                 path = root.getPath()
775                 inBouquetRootList = path.find('FROM BOUQUET "bouquets.') != -1 #FIXME HACK
776                 pos = path.find('FROM BOUQUET')
777                 isBouquet = (pos != -1) and (root.flags & eServiceReference.isDirectory)
778                 if not inBouquetRootList and isBouquet:
779                         self.servicelist.setMode(ServiceList.MODE_FAVOURITES)
780                         self.servicelist.setNumberOffset(self.getBouquetNumOffset(root))
781                 else:
782                         self.servicelist.setMode(ServiceList.MODE_NORMAL)
783                 self.servicelist.setRoot(root, justSet)
784                 self.buildTitleString()
785
786         def removeModeStr(self, str):
787                 if self.mode == MODE_TV:
788                         pos = str.find(' (TV)')
789                 else:
790                         pos = str.find(' (Radio)')
791                 if pos != -1:
792                         return str[:pos]
793                 return str
794
795         def getServiceName(self, ref):
796                 str = self.removeModeStr(ServiceReference(ref).getServiceName())
797                 if not str:
798                         pathstr = ref.getPath()
799                         if 'FROM PROVIDERS' in pathstr:
800                                 return _("Provider")
801                         if 'FROM SATELLITES' in pathstr:
802                                 return _("Satellites")
803                         if ') ORDER BY name' in pathstr:
804                                 return _("All")
805                 return str
806
807         def buildTitleString(self):
808                 titleStr = self.instance.getTitle()
809                 pos = titleStr.find(']')
810                 if pos == -1:
811                         pos = titleStr.find(')')
812                 if pos != -1:
813                         titleStr = titleStr[:pos+1]
814                         Len = len(self.servicePath)
815                         if Len > 0:
816                                 base_ref = self.servicePath[0]
817                                 if Len > 1:
818                                         end_ref = self.servicePath[Len-1]
819                                 else:
820                                         end_ref = None
821                                 nameStr = self.getServiceName(base_ref)
822                                 titleStr += ' ' + nameStr
823                                 if end_ref is not None:
824                                         if Len > 2:
825                                                 titleStr += '/../'
826                                         else:
827                                                 titleStr += '/'
828                                         nameStr = self.getServiceName(end_ref)
829                                         titleStr += nameStr
830                                 self.setTitle(titleStr)
831
832         def moveUp(self):
833                 self.servicelist.moveUp()
834
835         def moveDown(self):
836                 self.servicelist.moveDown()
837
838         def clearPath(self):
839                 del self.servicePath[:]
840
841         def enterPath(self, ref, justSet=False):
842                 self.servicePath.append(ref)
843                 self.setRoot(ref, justSet)
844
845         def pathUp(self, justSet=False):
846                 prev = self.servicePath.pop()
847                 if self.servicePath:
848                         current = self.servicePath[-1]
849                         self.setRoot(current, justSet)
850                         if not justSet:
851                                 self.setCurrentSelection(prev)
852                 return prev
853
854         def isBasePathEqual(self, ref):
855                 if len(self.servicePath) > 1 and self.servicePath[0] == ref:
856                         return True
857                 return False
858
859         def isPrevPathEqual(self, ref):
860                 length = len(self.servicePath)
861                 if length > 1 and self.servicePath[length-2] == ref:
862                         return True
863                 return False
864
865         def preEnterPath(self, refstr):
866                 return False
867
868         def showAllServices(self):
869                 if not self.pathChangeDisabled:
870                         refstr = '%s ORDER BY name'%(self.service_types)
871                         if not self.preEnterPath(refstr):
872                                 ref = eServiceReference(refstr)
873                                 currentRoot = self.getRoot()
874                                 if currentRoot is None or currentRoot != ref:
875                                         self.clearPath()
876                                         self.enterPath(ref)
877
878         def showSatellites(self):
879                 if not self.pathChangeDisabled:
880                         refstr = '%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types)
881                         if not self.preEnterPath(refstr):
882                                 ref = eServiceReference(refstr)
883                                 justSet=False
884                                 prev = None
885
886                                 if self.isBasePathEqual(ref):
887                                         if self.isPrevPathEqual(ref):
888                                                 justSet=True
889                                         prev = self.pathUp(justSet)
890                                 else:
891                                         currentRoot = self.getRoot()
892                                         if currentRoot is None or currentRoot != ref:
893                                                 justSet=True
894                                                 self.clearPath()
895                                                 self.enterPath(ref, True)
896                                 if justSet:
897                                         serviceHandler = eServiceCenter.getInstance()
898                                         servicelist = serviceHandler.list(ref)
899                                         if not servicelist is None:
900                                                 while True:
901                                                         service = servicelist.getNext()
902                                                         if not service.valid(): #check if end of list
903                                                                 break
904                                                         unsigned_orbpos = service.getUnsignedData(4) >> 16
905                                                         orbpos = service.getData(4) >> 16
906                                                         if orbpos < 0:
907                                                                 orbpos += 3600
908                                                         if service.getPath().find("FROM PROVIDER") != -1:
909                                                                 service_type = _("Providers")
910                                                         elif service.getPath().find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
911                                                                 service_type = _("New")
912                                                         else:
913                                                                 service_type = _("Services")
914                                                         try:
915                                                                 # why we need this cast?
916                                                                 service_name = str(nimmanager.getSatDescription(orbpos))
917                                                         except:
918                                                                 if unsigned_orbpos == 0xFFFF: #Cable
919                                                                         service_name = _("Cable")
920                                                                 elif unsigned_orbpos == 0xEEEE: #Terrestrial
921                                                                         service_name = _("Terrestrial")
922                                                                 else:
923                                                                         if orbpos > 1800: # west
924                                                                                 orbpos = 3600 - orbpos
925                                                                                 h = _("W")
926                                                                         else:
927                                                                                 h = _("E")
928                                                                         service_name = ("%d.%d" + h) % (orbpos / 10, orbpos % 10)
929                                                         service.setName("%s - %s" % (service_name, service_type))
930                                                         self.servicelist.addService(service)
931                                                 cur_ref = self.session.nav.getCurrentlyPlayingServiceReference()
932                                                 if cur_ref:
933                                                         pos = self.service_types.rfind(':')
934                                                         refstr = '%s (channelID == %08x%04x%04x) && %s ORDER BY name' %(self.service_types[:pos+1],
935                                                                 cur_ref.getUnsignedData(4), # NAMESPACE
936                                                                 cur_ref.getUnsignedData(2), # TSID
937                                                                 cur_ref.getUnsignedData(3), # ONID
938                                                                 self.service_types[pos+1:])
939                                                         ref = eServiceReference(refstr)
940                                                         ref.setName(_("Current Transponder"))
941                                                         self.servicelist.addService(ref)
942                                                 self.servicelist.finishFill()
943                                                 if prev is not None:
944                                                         self.setCurrentSelection(prev)
945
946         def showProviders(self):
947                 if not self.pathChangeDisabled:
948                         refstr = '%s FROM PROVIDERS ORDER BY name'%(self.service_types)
949                         if not self.preEnterPath(refstr):
950                                 ref = eServiceReference(refstr)
951                                 if self.isBasePathEqual(ref):
952                                         self.pathUp()
953                                 else:
954                                         currentRoot = self.getRoot()
955                                         if currentRoot is None or currentRoot != ref:
956                                                 self.clearPath()
957                                                 self.enterPath(ref)
958
959         def changeBouquet(self, direction):
960                 if not self.pathChangeDisabled:
961                         if len(self.servicePath) > 1:
962                                 #when enter satellite root list we must do some magic stuff..
963                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
964                                 if self.isBasePathEqual(ref):
965                                         self.showSatellites()
966                                 else:
967                                         self.pathUp()
968                                 if direction < 0:
969                                         self.moveUp()
970                                 else:
971                                         self.moveDown()
972                                 ref = self.getCurrentSelection()
973                                 self.enterPath(ref)
974
975         def inBouquet(self):
976                 if self.servicePath and self.servicePath[0] == self.bouquet_root:
977                         return True
978                 return False
979
980         def atBegin(self):
981                 return self.servicelist.atBegin()
982
983         def atEnd(self):
984                 return self.servicelist.atEnd()
985
986         def nextBouquet(self):
987                 self.changeBouquet(+1)
988
989         def prevBouquet(self):
990                 self.changeBouquet(-1)
991
992         def showFavourites(self):
993                 if not self.pathChangeDisabled:
994                         if not self.preEnterPath(self.bouquet_rootstr):
995                                 if self.isBasePathEqual(self.bouquet_root):
996                                         self.pathUp()
997                                 else:
998                                         currentRoot = self.getRoot()
999                                         if currentRoot is None or currentRoot != self.bouquet_root:
1000                                                 self.clearPath()
1001                                                 self.enterPath(self.bouquet_root)
1002
1003         def keyNumberGlobal(self, number):
1004                 unichar = self.numericalTextInput.getKey(number)
1005                 charstr = unichar.encode("utf-8")
1006                 if len(charstr) == 1:
1007                         self.servicelist.moveToChar(charstr[0])
1008
1009         def keyAsciiCode(self):
1010                 unichar = unichr(getPrevAsciiCode())
1011                 charstr = unichar.encode("utf-8")
1012                 if len(charstr) == 1:
1013                         self.servicelist.moveToChar(charstr[0])
1014
1015         def getRoot(self):
1016                 return self.servicelist.getRoot()
1017
1018         def getCurrentSelection(self):
1019                 return self.servicelist.getCurrent()
1020
1021         def setCurrentSelection(self, service):
1022                 self.servicelist.setCurrent(service)
1023
1024         def getBouquetList(self):
1025                 bouquets = [ ]
1026                 serviceHandler = eServiceCenter.getInstance()
1027                 if config.usage.multibouquet.value:
1028                         list = serviceHandler.list(self.bouquet_root)
1029                         if list:
1030                                 while True:
1031                                         s = list.getNext()
1032                                         if not s.valid():
1033                                                 break
1034                                         if s.flags & eServiceReference.isDirectory:
1035                                                 info = serviceHandler.info(s)
1036                                                 if info:
1037                                                         bouquets.append((info.getName(s), s))
1038                                 return bouquets
1039                 else:
1040                         info = serviceHandler.info(self.bouquet_root)
1041                         if info:
1042                                 bouquets.append((info.getName(self.bouquet_root), self.bouquet_root))
1043                         return bouquets
1044                 return None
1045
1046         def keyNumber0(self, num):
1047                 if len(self.servicePath) > 1:
1048                         self.keyGoUp()
1049                 else:
1050                         self.keyNumberGlobal(num)
1051
1052         def keyGoUp(self):
1053                 if len(self.servicePath) > 1:
1054                         if self.isBasePathEqual(self.bouquet_root):
1055                                 self.showFavourites()
1056                         else:
1057                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
1058                                 if self.isBasePathEqual(ref):
1059                                         self.showSatellites()
1060                                 else:
1061                                         ref = eServiceReference('%s FROM PROVIDERS ORDER BY name'%(self.service_types))
1062                                         if self.isBasePathEqual(ref):
1063                                                 self.showProviders()
1064                                         else:
1065                                                 self.showAllServices()
1066
1067         def nextMarker(self):
1068                 self.servicelist.moveToNextMarker()
1069
1070         def prevMarker(self):
1071                 self.servicelist.moveToPrevMarker()
1072
1073 HISTORYSIZE = 20
1074
1075 #config for lastservice
1076 config.tv = ConfigSubsection()
1077 config.tv.lastservice = ConfigText()
1078 config.tv.lastroot = ConfigText()
1079 config.radio = ConfigSubsection()
1080 config.radio.lastservice = ConfigText()
1081 config.radio.lastroot = ConfigText()
1082 config.servicelist = ConfigSubsection()
1083 config.servicelist.lastmode = ConfigText(default = "tv")
1084
1085 class ChannelSelection(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, SelectionEventInfo):
1086         def __init__(self, session):
1087                 ChannelSelectionBase.__init__(self,session)
1088                 ChannelSelectionEdit.__init__(self)
1089                 ChannelSelectionEPG.__init__(self)
1090                 SelectionEventInfo.__init__(self)
1091
1092                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1093                         {
1094                                 "cancel": self.cancel,
1095                                 "ok": self.channelSelected,
1096                                 "keyRadio": self.setModeRadio,
1097                                 "keyTV": self.setModeTv,
1098                         })
1099
1100                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1101                         {
1102                                 iPlayableService.evStart: self.__evServiceStart,
1103                                 iPlayableService.evEnd: self.__evServiceEnd
1104                         })
1105
1106                 self.lastChannelRootTimer = eTimer()
1107                 self.lastChannelRootTimer.callback.append(self.__onCreate)
1108                 self.lastChannelRootTimer.start(100,True)
1109
1110                 self.history_tv = [ ]
1111                 self.history_radio = [ ]
1112                 self.history = self.history_tv
1113                 self.history_pos = 0
1114
1115                 self.lastservice = config.tv.lastservice
1116                 self.lastroot = config.tv.lastroot
1117                 self.revertMode = None
1118                 config.usage.multibouquet.addNotifier(self.multibouquet_config_changed)
1119                 self.new_service_played = False
1120                 self.onExecBegin.append(self.asciiOn)
1121
1122         def asciiOn(self):
1123                 rcinput = eRCInput.getInstance()
1124                 rcinput.setKeyboardMode(rcinput.kmAscii)
1125
1126         def asciiOff(self):
1127                 rcinput = eRCInput.getInstance()
1128                 rcinput.setKeyboardMode(rcinput.kmNone)
1129
1130         def multibouquet_config_changed(self, val):
1131                 self.recallBouquetMode()
1132
1133         def __evServiceStart(self):
1134                 service = self.session.nav.getCurrentService()
1135                 if service:
1136                         info = service.info()
1137                         if info:
1138                                 refstr = info.getInfoString(iServiceInformation.sServiceref)
1139                                 self.servicelist.setPlayableIgnoreService(eServiceReference(refstr))
1140
1141         def __evServiceEnd(self):
1142                 self.servicelist.setPlayableIgnoreService(eServiceReference())
1143
1144         def setMode(self):
1145                 self.restoreRoot()
1146                 lastservice=eServiceReference(self.lastservice.value)
1147                 if lastservice.valid():
1148                         self.setCurrentSelection(lastservice)
1149
1150         def setModeTv(self):
1151                 if self.revertMode is None and config.servicelist.lastmode.value == "radio":
1152                         self.revertMode = MODE_RADIO
1153                 self.history = self.history_tv
1154                 self.lastservice = config.tv.lastservice
1155                 self.lastroot = config.tv.lastroot
1156                 config.servicelist.lastmode.value = "tv"
1157                 self.setTvMode()
1158                 self.setMode()
1159
1160         def setModeRadio(self):
1161                 if self.revertMode is None and config.servicelist.lastmode.value == "tv":
1162                         self.revertMode = MODE_TV
1163                 if config.usage.e1like_radio_mode.value:
1164                         self.history = self.history_radio
1165                         self.lastservice = config.radio.lastservice
1166                         self.lastroot = config.radio.lastroot
1167                         config.servicelist.lastmode.value = "radio"
1168                         self.setRadioMode()
1169                         self.setMode()
1170
1171         def __onCreate(self):
1172                 if config.usage.e1like_radio_mode.value:
1173                         if config.servicelist.lastmode.value == "tv":
1174                                 self.setModeTv()
1175                         else:
1176                                 self.setModeRadio()
1177                 else:
1178                         self.setModeTv()
1179                 lastservice=eServiceReference(self.lastservice.value)
1180                 if lastservice.valid():
1181                         self.zap()
1182
1183         def channelSelected(self):
1184                 ref = self.getCurrentSelection()
1185                 if self.movemode:
1186                         self.toggleMoveMarked()
1187                 elif (ref.flags & 7) == 7:
1188                         self.enterPath(ref)
1189                 elif self.bouquet_mark_edit != OFF:
1190                         if not (self.bouquet_mark_edit == EDIT_ALTERNATIVES and ref.flags & eServiceReference.isGroup):
1191                                 self.doMark()
1192                 elif not (ref.flags & eServiceReference.isMarker): # no marker
1193                         root = self.getRoot()
1194                         if not root or not (root.flags & eServiceReference.isGroup):
1195                                 self.zap()
1196                                 self.asciiOff()
1197                                 self.close(ref)
1198
1199         #called from infoBar and channelSelected
1200         def zap(self):
1201                 self.revertMode=None
1202                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1203                 nref = self.getCurrentSelection()
1204                 if ref is None or ref != nref:
1205                         self.new_service_played = True
1206                         self.session.nav.playService(nref)
1207                         self.saveRoot()
1208                         self.saveChannel(nref)
1209                         config.servicelist.lastmode.save()
1210                         self.addToHistory(nref)
1211
1212         def newServicePlayed(self):
1213                 ret = self.new_service_played
1214                 self.new_service_played = False
1215                 return ret
1216
1217         def addToHistory(self, ref):
1218                 if self.servicePath is not None:
1219                         tmp=self.servicePath[:]
1220                         tmp.append(ref)
1221                         try:
1222                                 del self.history[self.history_pos+1:]
1223                         except:
1224                                 pass
1225                         self.history.append(tmp)
1226                         hlen = len(self.history)
1227                         if hlen > HISTORYSIZE:
1228                                 del self.history[0]
1229                                 hlen -= 1
1230                         self.history_pos = hlen-1
1231
1232         def historyBack(self):
1233                 hlen = len(self.history)
1234                 if hlen > 1 and self.history_pos > 0:
1235                         self.history_pos -= 1
1236                         self.setHistoryPath()
1237
1238         def historyNext(self):
1239                 hlen = len(self.history)
1240                 if hlen > 1 and self.history_pos < (hlen-1):
1241                         self.history_pos += 1
1242                         self.setHistoryPath()
1243
1244         def setHistoryPath(self):
1245                 path = self.history[self.history_pos][:]
1246                 ref = path.pop()
1247                 del self.servicePath[:]
1248                 self.servicePath += path
1249                 self.saveRoot()
1250                 root = path[-1]
1251                 cur_root = self.getRoot()
1252                 if cur_root and cur_root != root:
1253                         self.setRoot(root)
1254                 self.session.nav.playService(ref)
1255                 self.setCurrentSelection(ref)
1256                 self.saveChannel(ref)
1257
1258         def saveRoot(self):
1259                 path = ''
1260                 for i in self.servicePath:
1261                         path += i.toString()
1262                         path += ';'
1263                 if path and path != self.lastroot.value:
1264                         self.lastroot.value = path
1265                         self.lastroot.save()
1266
1267         def restoreRoot(self):
1268                 self.clearPath()
1269                 re = compile('.+?;')
1270                 tmp = re.findall(self.lastroot.value)
1271                 cnt = 0
1272                 for i in tmp:
1273                         self.servicePath.append(eServiceReference(i[:-1]))
1274                         cnt += 1
1275                 if cnt:
1276                         path = self.servicePath.pop()
1277                         self.enterPath(path)
1278                 else:
1279                         self.showFavourites()
1280                         self.saveRoot()
1281
1282         def preEnterPath(self, refstr):
1283                 if self.servicePath and self.servicePath[0] != eServiceReference(refstr):
1284                         pathstr = self.lastroot.value
1285                         if pathstr is not None and pathstr.find(refstr) == 0:
1286                                 self.restoreRoot()
1287                                 lastservice=eServiceReference(self.lastservice.value)
1288                                 if lastservice.valid():
1289                                         self.setCurrentSelection(lastservice)
1290                                 return True
1291                 return False
1292
1293         def saveChannel(self, ref):
1294                 if ref is not None:
1295                         refstr = ref.toString()
1296                 else:
1297                         refstr = ""
1298                 if refstr != self.lastservice.value:
1299                         self.lastservice.value = refstr
1300                         self.lastservice.save()
1301
1302         def setCurrentServicePath(self, path):
1303                 if self.history:
1304                         self.history[self.history_pos] = path
1305                 else:
1306                         self.history.append(path)
1307                 self.setHistoryPath()
1308
1309         def getCurrentServicePath(self):
1310                 if self.history:
1311                         return self.history[self.history_pos]
1312                 return None
1313
1314         def recallPrevService(self):
1315                 hlen = len(self.history)
1316                 if hlen > 1:
1317                         if self.history_pos == hlen-1:
1318                                 tmp = self.history[self.history_pos]
1319                                 self.history[self.history_pos] = self.history[self.history_pos-1]
1320                                 self.history[self.history_pos-1] = tmp
1321                         else:
1322                                 tmp = self.history[self.history_pos+1]
1323                                 self.history[self.history_pos+1] = self.history[self.history_pos]
1324                                 self.history[self.history_pos] = tmp
1325                         self.setHistoryPath()
1326
1327         def cancel(self):
1328                 if self.revertMode is None:
1329                         self.restoreRoot()
1330                         lastservice=eServiceReference(self.lastservice.value)
1331                         if lastservice.valid() and self.getCurrentSelection() != lastservice:
1332                                 self.setCurrentSelection(lastservice)
1333                 elif self.revertMode == MODE_TV:
1334                         self.setModeTv()
1335                 elif self.revertMode == MODE_RADIO:
1336                         self.setModeRadio()
1337                 self.revertMode = None
1338                 self.asciiOff()
1339                 self.close(None)
1340
1341 class RadioInfoBar(Screen):
1342         def __init__(self, session):
1343                 Screen.__init__(self, session)
1344                 self["RdsDecoder"] = RdsDecoder(self.session.nav)
1345
1346 class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, InfoBarBase):
1347         ALLOW_SUSPEND = True
1348
1349         def __init__(self, session, infobar):
1350                 ChannelSelectionBase.__init__(self, session)
1351                 ChannelSelectionEdit.__init__(self)
1352                 ChannelSelectionEPG.__init__(self)
1353                 InfoBarBase.__init__(self)
1354                 self.infobar = infobar
1355                 self.onLayoutFinish.append(self.onCreate)
1356
1357                 self.info = session.instantiateDialog(RadioInfoBar) # our simple infobar
1358
1359                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1360                         {
1361                                 "keyTV": self.closeRadio,
1362                                 "keyRadio": self.closeRadio,
1363                                 "cancel": self.closeRadio,
1364                                 "ok": self.channelSelected,
1365                         })
1366
1367                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1368                         {
1369                                 iPlayableService.evStart: self.__evServiceStart,
1370                                 iPlayableService.evEnd: self.__evServiceEnd
1371                         })
1372
1373 ########## RDS Radiotext / Rass Support BEGIN
1374                 self.infobar = infobar # reference to real infobar (the one and only)
1375                 self["RdsDecoder"] = self.info["RdsDecoder"]
1376                 self["RdsActions"] = HelpableActionMap(self, "InfobarRdsActions",
1377                 {
1378                         "startRassInteractive": (self.startRassInteractive, _("View Rass interactive..."))
1379                 },-1)
1380                 self["RdsActions"].setEnabled(False)
1381                 infobar.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
1382                 self.onClose.append(self.__onClose)
1383
1384         def __onClose(self):
1385                 lastservice=eServiceReference(config.tv.lastservice.value)
1386                 self.session.nav.playService(lastservice)
1387
1388         def startRassInteractive(self):
1389                 self.info.hide();
1390                 self.infobar.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
1391
1392         def RassInteractiveClosed(self):
1393                 self.info.show()
1394                 self.infobar.rass_interactive = None
1395                 self.infobar.RassSlidePicChanged()
1396
1397         def RassInteractivePossibilityChanged(self, state):
1398                 self["RdsActions"].setEnabled(state)
1399 ########## RDS Radiotext / Rass Support END
1400
1401         def closeRadio(self):
1402                 self.infobar.rds_display.onRassInteractivePossibilityChanged.remove(self.RassInteractivePossibilityChanged)
1403                 self.info.hide()
1404                 #set previous tv service
1405                 self.close(None)
1406
1407         def __evServiceStart(self):
1408                 service = self.session.nav.getCurrentService()
1409                 if service:
1410                         info = service.info()
1411                         if info:
1412                                 refstr = info.getInfoString(iServiceInformation.sServiceref)
1413                                 self.servicelist.setPlayableIgnoreService(eServiceReference(refstr))
1414
1415         def __evServiceEnd(self):
1416                 self.servicelist.setPlayableIgnoreService(eServiceReference())
1417
1418         def saveRoot(self):
1419                 path = ''
1420                 for i in self.servicePathRadio:
1421                         path += i.toString()
1422                         path += ';'
1423                 if path and path != config.radio.lastroot.value:
1424                         config.radio.lastroot.value = path
1425                         config.radio.lastroot.save()
1426
1427         def restoreRoot(self):
1428                 self.clearPath()
1429                 re = compile('.+?;')
1430                 tmp = re.findall(config.radio.lastroot.value)
1431                 cnt = 0
1432                 for i in tmp:
1433                         self.servicePathRadio.append(eServiceReference(i[:-1]))
1434                         cnt += 1
1435                 if cnt:
1436                         path = self.servicePathRadio.pop()
1437                         self.enterPath(path)
1438                 else:
1439                         self.showFavourites()
1440                         self.saveRoot()
1441
1442         def preEnterPath(self, refstr):
1443                 if self.servicePathRadio and self.servicePathRadio[0] != eServiceReference(refstr):
1444                         pathstr = config.radio.lastroot.value
1445                         if pathstr is not None and pathstr.find(refstr) == 0:
1446                                 self.restoreRoot()
1447                                 lastservice=eServiceReference(config.radio.lastservice.value)
1448                                 if lastservice.valid():
1449                                         self.setCurrentSelection(lastservice)
1450                                 return True
1451                 return False
1452
1453         def onCreate(self):
1454                 self.setRadioMode()
1455                 self.restoreRoot()
1456                 lastservice=eServiceReference(config.radio.lastservice.value)
1457                 if lastservice.valid():
1458                         self.servicelist.setCurrent(lastservice)
1459                         self.session.nav.playService(lastservice)
1460                 else:
1461                         self.session.nav.stopService()
1462                 self.info.show()
1463
1464         def channelSelected(self): # just return selected service
1465                 ref = self.getCurrentSelection()
1466                 if self.movemode:
1467                         self.toggleMoveMarked()
1468                 elif (ref.flags & 7) == 7:
1469                         self.enterPath(ref)
1470                 elif self.bouquet_mark_edit != OFF:
1471                         if not (self.bouquet_mark_edit == EDIT_ALTERNATIVES and ref.flags & eServiceReference.isGroup):
1472                                 self.doMark()
1473                 elif not (ref.flags & eServiceReference.isMarker): # no marker
1474                         cur_root = self.getRoot()
1475                         if not cur_root or not (cur_root.flags & eServiceReference.isGroup):
1476                                 playingref = self.session.nav.getCurrentlyPlayingServiceReference()
1477                                 if playingref is None or playingref != ref:
1478                                         self.session.nav.playService(ref)
1479                                         config.radio.lastservice.value = ref.toString()
1480                                         config.radio.lastservice.save()
1481                                 self.saveRoot()
1482
1483 class SimpleChannelSelection(ChannelSelectionBase):
1484         def __init__(self, session, title):
1485                 ChannelSelectionBase.__init__(self, session)
1486                 self.title = title
1487                 self.onShown.append(self.__onExecCallback)
1488
1489                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1490                         {
1491                                 "cancel": self.close,
1492                                 "ok": self.channelSelected,
1493                                 "keyRadio": self.setModeRadio,
1494                                 "keyTV": self.setModeTv,
1495                         })
1496
1497         def __onExecCallback(self):
1498                 self.setTitle(self.title)
1499                 self.setModeTv()
1500
1501         def channelSelected(self): # just return selected service
1502                 ref = self.getCurrentSelection()
1503                 if (ref.flags & 7) == 7:
1504                         self.enterPath(ref)
1505                 elif not (ref.flags & eServiceReference.isMarker):
1506                         ref = self.getCurrentSelection()
1507                         self.close(ref)
1508
1509         def setModeTv(self):
1510                 self.setTvMode()
1511                 self.showFavourites()
1512
1513         def setModeRadio(self):
1514                 self.setRadioMode()
1515                 self.showFavourites()