workaround for config entry dependencies
[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 "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 (len(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                 ptr=eEPGCache.getInstance()
321                 if ptr.startTimeQuery(ref) != -1:
322                         self.session.open(EPGSelection, ref)
323                 else:
324                         print 'no epg for service', ref.toString()
325
326 class ChannelSelectionEdit:
327         def __init__(self):
328                 self.entry_marked = False
329                 self.movemode = False
330                 self.bouquet_mark_edit = OFF
331                 self.mutableList = None
332                 self.__marked = [ ]
333                 self.saved_title = None
334                 self.saved_root = None
335
336                 class ChannelSelectionEditActionMap(ActionMap):
337                         def __init__(self, csel, contexts = [ ], actions = { }, prio=0):
338                                 ActionMap.__init__(self, contexts, actions, prio)
339                                 self.csel = csel
340
341                         def action(self, contexts, action):
342                                 if action == "cancel":
343                                         self.csel.handleEditCancel()
344                                         return 0 # fall-trough
345                                 elif action == "ok":
346                                         return 0 # fall-trough
347                                 else:
348                                         return ActionMap.action(self, contexts, action)
349
350                 self["ChannelSelectEditActions"] = ChannelSelectionEditActionMap(self, ["ChannelSelectEditActions", "OkCancelActions"],
351                         {
352                                 "contextMenu": self.doContext,
353                         })
354
355         def getMutableList(self, root=eServiceReference()):
356                 if not self.mutableList is None:
357                         return self.mutableList
358                 serviceHandler = eServiceCenter.getInstance()
359                 if not root.valid():
360                         root=self.getRoot()
361                 list = root and serviceHandler.list(root)
362                 if list is not None:
363                         return list.startEdit()
364                 return None
365
366         def buildBouquetID(self, str):
367                 tmp = str.lower()
368                 name = ''
369                 for c in tmp:
370                         if (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9'):
371                                 name += c
372                         else:
373                                 name += '_'
374                 return name
375
376         def addMarker(self, name):
377                 current = self.servicelist.getCurrent()
378                 mutableList = self.getMutableList()
379                 cnt = 0
380                 while mutableList:
381                         str = '1:64:%d:0:0:0:0:0:0:0::%s'%(cnt, name)
382                         ref = eServiceReference(str)
383                         if current and current.valid():
384                                 if not mutableList.addService(ref, current):
385                                         self.servicelist.addService(ref, True)
386                                         mutableList.flushChanges()
387                                         break
388                         elif not mutableList.addService(ref):
389                                 self.servicelist.addService(ref, True)
390                                 mutableList.flushChanges()
391                                 break
392                         cnt+=1
393
394         def addAlternativeServices(self):
395                 cur_service = ServiceReference(self.getCurrentSelection())
396                 root = self.getRoot()
397                 cur_root = root and ServiceReference(root)
398                 mutableBouquet = cur_root.list().startEdit()
399                 if mutableBouquet:
400                         name = cur_service.getServiceName()
401                         print "NAME", name
402                         if self.mode == MODE_TV:
403                                 str = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"alternatives.%s.tv\" ORDER BY bouquet'%(self.buildBouquetID(name))
404                         else:
405                                 str = '1:134:2:0:0:0:0:0:0:0:FROM BOUQUET \"alternatives.%s.radio\" ORDER BY bouquet'%(self.buildBouquetID(name))
406                         new_ref = ServiceReference(str)
407                         if not mutableBouquet.addService(new_ref.ref, cur_service.ref):
408                                 mutableBouquet.removeService(cur_service.ref)
409                                 mutableBouquet.flushChanges()
410                                 eDVBDB.getInstance().reloadBouquets()
411                                 mutableAlternatives = new_ref.list().startEdit()
412                                 if mutableAlternatives:
413                                         mutableAlternatives.setListName(name)
414                                         if mutableAlternatives.addService(cur_service.ref):
415                                                 print "add", cur_service.toString(), "to new alternatives failed"
416                                         mutableAlternatives.flushChanges()
417                                         self.servicelist.addService(new_ref.ref, True)
418                                         self.servicelist.removeCurrent()
419                                         self.servicelist.moveUp()
420                                 else:
421                                         print "get mutable list for new created alternatives failed"
422                         else:
423                                 print "add", str, "to", cur_root.getServiceName(), "failed"
424                 else:
425                         print "bouquetlist is not editable"
426
427         def addBouquet(self, bName, services):
428                 serviceHandler = eServiceCenter.getInstance()
429                 mutableBouquetList = serviceHandler.list(self.bouquet_root).startEdit()
430                 if mutableBouquetList:
431                         if self.mode == MODE_TV:
432                                 bName += " (TV)"
433                                 str = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET \"userbouquet.%s.tv\" ORDER BY bouquet'%(self.buildBouquetID(bName))
434                         else:
435                                 bName += " (Radio)"
436                                 str = '1:7:2:0:0:0:0:0:0:0:FROM BOUQUET \"userbouquet.%s.radio\" ORDER BY bouquet'%(self.buildBouquetID(bName))
437                         new_bouquet_ref = eServiceReference(str)
438                         if not mutableBouquetList.addService(new_bouquet_ref):
439                                 mutableBouquetList.flushChanges()
440                                 eDVBDB.getInstance().reloadBouquets()
441                                 mutableBouquet = serviceHandler.list(new_bouquet_ref).startEdit()
442                                 if mutableBouquet:
443                                         mutableBouquet.setListName(bName)
444                                         if services is not None:
445                                                 for service in services:
446                                                         if mutableBouquet.addService(service):
447                                                                 print "add", service.toString(), "to new bouquet failed"
448                                         mutableBouquet.flushChanges()
449                                 else:
450                                         print "get mutable list for new created bouquet failed"
451                                 # do some voodoo to check if current_root is equal to bouquet_root
452                                 cur_root = self.getRoot();
453                                 str1 = cur_root and cur_root.toString()
454                                 pos1 = str1 and str1.find("FROM BOUQUET") or -1
455                                 pos2 = self.bouquet_rootstr.find("FROM BOUQUET")
456                                 if pos1 != -1 and pos2 != -1 and str1[pos1:] == self.bouquet_rootstr[pos2:]:
457                                         self.servicelist.addService(new_bouquet_ref)
458                         else:
459                                 print "add", str, "to bouquets failed"
460                 else:
461                         print "bouquetlist is not editable"
462
463         def copyCurrentToBouquetList(self):
464                 provider = ServiceReference(self.getCurrentSelection())
465                 providerName = provider.getServiceName()
466                 serviceHandler = eServiceCenter.getInstance()
467                 services = serviceHandler.list(provider.ref)
468                 self.addBouquet(providerName, services and services.getContent('R', True))
469
470         def removeAlternativeServices(self):
471                 cur_service = ServiceReference(self.getCurrentSelection())
472                 root = self.getRoot()
473                 cur_root = root and ServiceReference(root)
474                 list = cur_service.list()
475                 first_in_alternative = list and list.getNext()
476                 if first_in_alternative:
477                         edit_root = cur_root and cur_root.list().startEdit()
478                         if edit_root:
479                                 if not edit_root.addService(first_in_alternative, cur_service.ref):
480                                         self.servicelist.addService(first_in_alternative, True)
481                                 else:
482                                         print "couldn't add first alternative service to current root"
483                         else:
484                                 print "couldn't edit current root!!"
485                 else:
486                         print "remove empty alternative list !!"
487                 self.removeBouquet()
488                 self.servicelist.moveUp()
489
490         def removeBouquet(self):
491                 refstr = self.getCurrentSelection().toString()
492                 print "removeBouquet", refstr
493                 self.bouquetNumOffsetCache = { }
494                 pos = refstr.find('FROM BOUQUET "')
495                 filename = None
496                 if pos != -1:
497                         refstr = refstr[pos+14:]
498                         pos = refstr.find('"')
499                         if pos != -1:
500                                 filename = '/etc/enigma2/' + refstr[:pos] # FIXMEEE !!! HARDCODED /etc/enigma2
501                 self.removeCurrentService()
502                 try:
503                         if filename is not None:
504                                 remove(filename)
505                 except OSError:
506                         print "error during remove of", filename
507
508 #  multiple marked entry stuff ( edit mode, later multiepg selection )
509         def startMarkedEdit(self, type):
510                 self.savedPath = self.servicePath[:]
511                 if type == EDIT_ALTERNATIVES:
512                         self.enterPath(self.getCurrentSelection())
513                 self.mutableList = self.getMutableList()
514                 # add all services from the current list to internal marked set in listboxservicecontent
515                 self.clearMarks() # this clears the internal marked set in the listboxservicecontent
516                 self.saved_title = self.instance.getTitle()
517                 pos = self.saved_title.find(')')
518                 new_title = self.saved_title[:pos+1]
519                 if type == EDIT_ALTERNATIVES:
520                         self.bouquet_mark_edit = EDIT_ALTERNATIVES
521                         new_title += ' ' + _("[alternative edit]")
522                 else:
523                         self.bouquet_mark_edit = EDIT_BOUQUET
524                         if config.usage.multibouquet.value:
525                                 new_title += ' ' + _("[bouquet edit]")
526                         else:
527                                 new_title += ' ' + _("[favourite edit]")
528                 self.setTitle(new_title)
529                 self.__marked = self.servicelist.getRootServices()
530                 for x in self.__marked:
531                         self.servicelist.addMarked(eServiceReference(x))
532                 self.showAllServices()
533
534         def endMarkedEdit(self, abort):
535                 if not abort and self.mutableList is not None:
536                         self.bouquetNumOffsetCache = { }
537                         new_marked = set(self.servicelist.getMarked())
538                         old_marked = set(self.__marked)
539                         removed = old_marked - new_marked
540                         added = new_marked - old_marked
541                         changed = False
542                         for x in removed:
543                                 changed = True
544                                 self.mutableList.removeService(eServiceReference(x))
545                         for x in added:
546                                 changed = True
547                                 self.mutableList.addService(eServiceReference(x))
548                         if changed:
549                                 self.mutableList.flushChanges()
550                 self.__marked = []
551                 self.clearMarks()
552                 self.bouquet_mark_edit = OFF
553                 self.mutableList = None
554                 self.setTitle(self.saved_title)
555                 self.saved_title = None
556                 # self.servicePath is just a reference to servicePathTv or Radio...
557                 # so we never ever do use the asignment operator in self.servicePath
558                 del self.servicePath[:] # remove all elements
559                 self.servicePath += self.savedPath # add saved elements
560                 del self.savedPath
561                 self.setRoot(self.servicePath[len(self.servicePath)-1])
562
563         def clearMarks(self):
564                 self.servicelist.clearMarks()
565
566         def doMark(self):
567                 ref = self.servicelist.getCurrent()
568                 if self.servicelist.isMarked(ref):
569                         self.servicelist.removeMarked(ref)
570                 else:
571                         self.servicelist.addMarked(ref)
572
573         def removeCurrentService(self):
574                 ref = self.servicelist.getCurrent()
575                 mutableList = self.getMutableList()
576                 if ref.valid() and mutableList is not None:
577                         if not mutableList.removeService(ref):
578                                 self.bouquetNumOffsetCache = { }
579                                 mutableList.flushChanges() #FIXME dont flush on each single removed service
580                                 self.servicelist.removeCurrent()
581
582         def addServiceToBouquet(self, dest, service=None):
583                 mutableList = self.getMutableList(dest)
584                 if not mutableList is None:
585                         if service is None: #use current selected service
586                                 service = self.servicelist.getCurrent()
587                         if not mutableList.addService(service):
588                                 self.bouquetNumOffsetCache = { }
589                                 mutableList.flushChanges()
590                                 # do some voodoo to check if current_root is equal to dest
591                                 cur_root = self.getRoot();
592                                 str1 = cur_root and cur_root.toString() or -1
593                                 str2 = dest.toString()
594                                 pos1 = str1.find("FROM BOUQUET")
595                                 pos2 = str2.find("FROM BOUQUET")
596                                 if pos1 != -1 and pos2 != -1 and str1[pos1:] == str2[pos2:]:
597                                         self.servicelist.addService(service)
598
599         def toggleMoveMode(self):
600                 if self.movemode:
601                         if self.entry_marked:
602                                 self.toggleMoveMarked() # unmark current entry
603                         self.movemode = False
604                         self.pathChangeDisabled = False # re-enable path change
605                         self.mutableList.flushChanges() # FIXME add check if changes was made
606                         self.mutableList = None
607                         self.setTitle(self.saved_title)
608                         self.saved_title = None
609                         cur_root = self.getRoot()
610                         if cur_root and cur_root == self.bouquet_root:
611                                 self.bouquetNumOffsetCache = { }
612                 else:
613                         self.mutableList = self.getMutableList()
614                         self.movemode = True
615                         self.pathChangeDisabled = True # no path change allowed in movemode
616                         self.saved_title = self.instance.getTitle()
617                         new_title = self.saved_title
618                         pos = self.saved_title.find(')')
619                         new_title = self.saved_title[:pos+1] + ' ' + _("[move mode]") + self.saved_title[pos+1:]
620                         self.setTitle(new_title);
621
622         def handleEditCancel(self):
623                 if self.movemode: #movemode active?
624                         self.channelSelected() # unmark
625                         self.toggleMoveMode() # disable move mode
626                 elif self.bouquet_mark_edit != OFF:
627                         self.endMarkedEdit(True) # abort edit mode
628
629         def toggleMoveMarked(self):
630                 if self.entry_marked:
631                         self.servicelist.setCurrentMarked(False)
632                         self.entry_marked = False
633                 else:
634                         self.servicelist.setCurrentMarked(True)
635                         self.entry_marked = True
636
637         def doContext(self):
638                 self.session.open(ChannelContextMenu, self)
639
640 MODE_TV = 0
641 MODE_RADIO = 1
642
643 # this makes it much simple to implement a selectable radio or tv mode :)
644 service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 195) || (type == 25) || (type == 134)'
645 service_types_radio = '1:7:2:0:0:0:0:0:0:0:(type == 2)'
646
647 class ChannelSelectionBase(Screen):
648         def __init__(self, session):
649                 Screen.__init__(self, session)
650
651                 self["key_red"] = Button(_("All"))
652                 self["key_green"] = Button(_("Satellites"))
653                 self["key_yellow"] = Button(_("Provider"))
654                 self["key_blue"] = Button(_("Favourites"))
655
656                 self["list"] = ServiceList()
657                 self.servicelist = self["list"]
658
659                 self.numericalTextInput = NumericalTextInput()
660                 self.numericalTextInput.setUseableChars(u'1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ')
661
662                 self.servicePathTV = [ ]
663                 self.servicePathRadio = [ ]
664                 self.servicePath = [ ]
665
666                 self.mode = MODE_TV
667
668                 self.pathChangeDisabled = False
669
670                 self.bouquetNumOffsetCache = { }
671
672                 self["ChannelSelectBaseActions"] = NumberActionMap(["ChannelSelectBaseActions", "NumberActions", "InputAsciiActions"],
673                         {
674                                 "showFavourites": self.showFavourites,
675                                 "showAllServices": self.showAllServices,
676                                 "showProviders": self.showProviders,
677                                 "showSatellites": self.showSatellites,
678                                 "nextBouquet": self.nextBouquet,
679                                 "prevBouquet": self.prevBouquet,
680                                 "nextMarker": self.nextMarker,
681                                 "prevMarker": self.prevMarker,
682                                 "gotAsciiCode": self.keyAsciiCode,
683                                 "1": self.keyNumberGlobal,
684                                 "2": self.keyNumberGlobal,
685                                 "3": self.keyNumberGlobal,
686                                 "4": self.keyNumberGlobal,
687                                 "5": self.keyNumberGlobal,
688                                 "6": self.keyNumberGlobal,
689                                 "7": self.keyNumberGlobal,
690                                 "8": self.keyNumberGlobal,
691                                 "9": self.keyNumberGlobal,
692                                 "0": self.keyNumber0
693                         })
694                 self.recallBouquetMode()
695
696         def getBouquetNumOffset(self, bouquet):
697                 if not config.usage.multibouquet.value:
698                         return 0
699                 str = bouquet.toString()
700                 offsetCount = 0
701                 if not self.bouquetNumOffsetCache.has_key(str):
702                         serviceHandler = eServiceCenter.getInstance()
703                         bouquetlist = serviceHandler.list(self.bouquet_root)
704                         if not bouquetlist is None:
705                                 while True:
706                                         bouquetIterator = bouquetlist.getNext()
707                                         if not bouquetIterator.valid(): #end of list
708                                                 break
709                                         self.bouquetNumOffsetCache[bouquetIterator.toString()]=offsetCount
710                                         if not (bouquetIterator.flags & eServiceReference.isDirectory):
711                                                 continue
712                                         servicelist = serviceHandler.list(bouquetIterator)
713                                         if not servicelist is None:
714                                                 while True:
715                                                         serviceIterator = servicelist.getNext()
716                                                         if not serviceIterator.valid(): #check if end of list
717                                                                 break
718                                                         playable = not (serviceIterator.flags & (eServiceReference.isDirectory|eServiceReference.isMarker))
719                                                         if playable:
720                                                                 offsetCount += 1
721                 return self.bouquetNumOffsetCache.get(str, offsetCount)
722
723         def recallBouquetMode(self):
724                 if self.mode == MODE_TV:
725                         self.service_types = service_types_tv
726                         if config.usage.multibouquet.value:
727                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
728                         else:
729                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'%(self.service_types)
730                 else:
731                         self.service_types = service_types_radio
732                         if config.usage.multibouquet.value:
733                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.radio" ORDER BY bouquet'
734                         else:
735                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.radio" ORDER BY bouquet'%(self.service_types)
736                 self.bouquet_root = eServiceReference(self.bouquet_rootstr)
737
738         def setTvMode(self):
739                 self.mode = MODE_TV
740                 self.servicePath = self.servicePathTV
741                 self.recallBouquetMode()
742                 title = self.instance.getTitle()
743                 pos = title.find(" (")
744                 if pos != -1:
745                         title = title[:pos]
746                 title += " (TV)"
747                 self.setTitle(title)
748
749         def setRadioMode(self):
750                 self.mode = MODE_RADIO
751                 self.servicePath = self.servicePathRadio
752                 self.recallBouquetMode()
753                 title = self.instance.getTitle()
754                 pos = title.find(" (")
755                 if pos != -1:
756                         title = title[:pos]
757                 title += " (Radio)"
758                 self.setTitle(title)
759
760         def setRoot(self, root, justSet=False):
761                 path = root.getPath()
762                 inBouquetRootList = path.find('FROM BOUQUET "bouquets.') != -1 #FIXME HACK
763                 pos = path.find('FROM BOUQUET')
764                 isBouquet = (pos != -1) and (root.flags & eServiceReference.isDirectory)
765                 if not inBouquetRootList and isBouquet:
766                         self.servicelist.setMode(ServiceList.MODE_FAVOURITES)
767                         self.servicelist.setNumberOffset(self.getBouquetNumOffset(root))
768                 else:
769                         self.servicelist.setMode(ServiceList.MODE_NORMAL)
770                 self.servicelist.setRoot(root, justSet)
771                 self.buildTitleString()
772
773         def removeModeStr(self, str):
774                 if self.mode == MODE_TV:
775                         pos = str.find(' (TV)')
776                 else:
777                         pos = str.find(' (Radio)')
778                 if pos != -1:
779                         return str[:pos]
780                 return str
781
782         def getServiceName(self, ref):
783                 str = self.removeModeStr(ServiceReference(ref).getServiceName())
784                 if not len(str):
785                         pathstr = ref.getPath()
786                         if pathstr.find('FROM PROVIDERS') != -1:
787                                 return _("Provider")
788                         if pathstr.find('FROM SATELLITES') != -1:
789                                 return _("Satellites")
790                         if pathstr.find(') ORDER BY name') != -1:
791                                 return _("All")
792                 return str
793
794         def buildTitleString(self):
795                 titleStr = self.instance.getTitle()
796                 pos = titleStr.find(']')
797                 if pos == -1:
798                         pos = titleStr.find(')')
799                 if pos != -1:
800                         titleStr = titleStr[:pos+1]
801                         Len = len(self.servicePath)
802                         if Len > 0:
803                                 base_ref = self.servicePath[0]
804                                 if Len > 1:
805                                         end_ref = self.servicePath[Len-1]
806                                 else:
807                                         end_ref = None
808                                 nameStr = self.getServiceName(base_ref)
809                                 titleStr += ' ' + nameStr
810                                 if end_ref is not None:
811                                         if Len > 2:
812                                                 titleStr += '/../'
813                                         else:
814                                                 titleStr += '/'
815                                         nameStr = self.getServiceName(end_ref)
816                                         titleStr += nameStr
817                                 self.setTitle(titleStr)
818
819         def moveUp(self):
820                 self.servicelist.moveUp()
821
822         def moveDown(self):
823                 self.servicelist.moveDown()
824
825         def clearPath(self):
826                 del self.servicePath[:]
827
828         def enterPath(self, ref, justSet=False):
829                 self.servicePath.append(ref)
830                 self.setRoot(ref, justSet)
831
832         def pathUp(self, justSet=False):
833                 prev = self.servicePath.pop()
834                 length = len(self.servicePath)
835                 if length:
836                         current = self.servicePath[length-1]
837                         self.setRoot(current, justSet)
838                         if not justSet:
839                                 self.setCurrentSelection(prev)
840                 return prev
841
842         def isBasePathEqual(self, ref):
843                 if len(self.servicePath) > 1 and self.servicePath[0] == ref:
844                         return True
845                 return False
846
847         def isPrevPathEqual(self, ref):
848                 length = len(self.servicePath)
849                 if length > 1 and self.servicePath[length-2] == ref:
850                         return True
851                 return False
852
853         def preEnterPath(self, refstr):
854                 return False
855
856         def showAllServices(self):
857                 if not self.pathChangeDisabled:
858                         refstr = '%s ORDER BY name'%(self.service_types)
859                         if not self.preEnterPath(refstr):
860                                 ref = eServiceReference(refstr)
861                                 currentRoot = self.getRoot()
862                                 if currentRoot is None or currentRoot != ref:
863                                         self.clearPath()
864                                         self.enterPath(ref)
865
866         def showSatellites(self):
867                 if not self.pathChangeDisabled:
868                         refstr = '%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types)
869                         if not self.preEnterPath(refstr):
870                                 ref = eServiceReference(refstr)
871                                 justSet=False
872                                 prev = None
873
874                                 if self.isBasePathEqual(ref):
875                                         if self.isPrevPathEqual(ref):
876                                                 justSet=True
877                                         prev = self.pathUp(justSet)
878                                 else:
879                                         currentRoot = self.getRoot()
880                                         if currentRoot is None or currentRoot != ref:
881                                                 justSet=True
882                                                 self.clearPath()
883                                                 self.enterPath(ref, True)
884                                 if justSet:
885                                         serviceHandler = eServiceCenter.getInstance()
886                                         servicelist = serviceHandler.list(ref)
887                                         if not servicelist is None:
888                                                 while True:
889                                                         service = servicelist.getNext()
890                                                         if not service.valid(): #check if end of list
891                                                                 break
892                                                         unsigned_orbpos = service.getUnsignedData(4) >> 16
893                                                         orbpos = service.getData(4) >> 16
894                                                         if orbpos < 0:
895                                                                 orbpos += 3600
896                                                         if service.getPath().find("FROM PROVIDER") != -1:
897                                                                 service_type = _("Providers")
898                                                         elif service.getPath().find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
899                                                                 service_type = _("New")
900                                                         else:
901                                                                 service_type = _("Services")
902                                                         try:
903                                                                 # why we need this cast?
904                                                                 service_name = str(nimmanager.getSatDescription(orbpos))
905                                                         except:
906                                                                 if unsigned_orbpos == 0xFFFF: #Cable
907                                                                         service_name = _("Cable")
908                                                                 elif unsigned_orbpos == 0xEEEE: #Terrestrial
909                                                                         service_name = _("Terrestrial")
910                                                                 else:
911                                                                         if orbpos > 1800: # west
912                                                                                 orbpos = 3600 - orbpos
913                                                                                 h = _("W")
914                                                                         else:
915                                                                                 h = _("E")
916                                                                         service_name = ("%d.%d" + h) % (orbpos / 10, orbpos % 10)
917                                                         service.setName("%s - %s" % (service_name, service_type))
918                                                         self.servicelist.addService(service)
919                                                 cur_ref = self.session.nav.getCurrentlyPlayingServiceReference()
920                                                 if cur_ref:
921                                                         pos = self.service_types.rfind(':')
922                                                         refstr = '%s (channelID == %08x%04x%04x) && %s ORDER BY name' %(self.service_types[:pos+1],
923                                                                 cur_ref.getUnsignedData(4), # NAMESPACE
924                                                                 cur_ref.getUnsignedData(2), # TSID
925                                                                 cur_ref.getUnsignedData(3), # ONID
926                                                                 self.service_types[pos+1:])
927                                                         ref = eServiceReference(refstr)
928                                                         ref.setName(_("Current Transponder"))
929                                                         self.servicelist.addService(ref)
930                                                 self.servicelist.finishFill()
931                                                 if prev is not None:
932                                                         self.setCurrentSelection(prev)
933
934         def showProviders(self):
935                 if not self.pathChangeDisabled:
936                         refstr = '%s FROM PROVIDERS ORDER BY name'%(self.service_types)
937                         if not self.preEnterPath(refstr):
938                                 ref = eServiceReference(refstr)
939                                 if self.isBasePathEqual(ref):
940                                         self.pathUp()
941                                 else:
942                                         currentRoot = self.getRoot()
943                                         if currentRoot is None or currentRoot != ref:
944                                                 self.clearPath()
945                                                 self.enterPath(ref)
946
947         def changeBouquet(self, direction):
948                 if not self.pathChangeDisabled:
949                         if len(self.servicePath) > 1:
950                                 #when enter satellite root list we must do some magic stuff..
951                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
952                                 if self.isBasePathEqual(ref):
953                                         self.showSatellites()
954                                 else:
955                                         self.pathUp()
956                                 if direction < 0:
957                                         self.moveUp()
958                                 else:
959                                         self.moveDown()
960                                 ref = self.getCurrentSelection()
961                                 self.enterPath(ref)
962
963         def inBouquet(self):
964                 if len(self.servicePath) > 0 and self.servicePath[0] == self.bouquet_root:
965                         return True
966                 return False
967
968         def atBegin(self):
969                 return self.servicelist.atBegin()
970
971         def atEnd(self):
972                 return self.servicelist.atEnd()
973
974         def nextBouquet(self):
975                 self.changeBouquet(+1)
976
977         def prevBouquet(self):
978                 self.changeBouquet(-1)
979
980         def showFavourites(self):
981                 if not self.pathChangeDisabled:
982                         if not self.preEnterPath(self.bouquet_rootstr):
983                                 if self.isBasePathEqual(self.bouquet_root):
984                                         self.pathUp()
985                                 else:
986                                         currentRoot = self.getRoot()
987                                         if currentRoot is None or currentRoot != self.bouquet_root:
988                                                 self.clearPath()
989                                                 self.enterPath(self.bouquet_root)
990
991         def keyNumberGlobal(self, number):
992                 unichar = self.numericalTextInput.getKey(number)
993                 charstr = unichar.encode("utf-8")
994                 if len(charstr) == 1:
995                         self.servicelist.moveToChar(charstr[0])
996
997         def keyAsciiCode(self):
998                 unichar = unichr(getPrevAsciiCode())
999                 charstr = unichar.encode("utf-8")
1000                 if len(charstr) == 1:
1001                         self.servicelist.moveToChar(charstr[0])
1002
1003         def getRoot(self):
1004                 return self.servicelist.getRoot()
1005
1006         def getCurrentSelection(self):
1007                 return self.servicelist.getCurrent()
1008
1009         def setCurrentSelection(self, service):
1010                 self.servicelist.setCurrent(service)
1011
1012         def getBouquetList(self):
1013                 bouquets = [ ]
1014                 serviceHandler = eServiceCenter.getInstance()
1015                 if config.usage.multibouquet.value:
1016                         list = serviceHandler.list(self.bouquet_root)
1017                         if list:
1018                                 while True:
1019                                         s = list.getNext()
1020                                         if not s.valid():
1021                                                 break
1022                                         if s.flags & eServiceReference.isDirectory:
1023                                                 info = serviceHandler.info(s)
1024                                                 if info:
1025                                                         bouquets.append((info.getName(s), s))
1026                                 return bouquets
1027                 else:
1028                         info = serviceHandler.info(self.bouquet_root)
1029                         if info:
1030                                 bouquets.append((info.getName(self.bouquet_root), self.bouquet_root))
1031                         return bouquets
1032                 return None
1033
1034         def keyNumber0(self, num):
1035                 if len(self.servicePath) > 1:
1036                         self.keyGoUp()
1037                 else:
1038                         self.keyNumberGlobal(num)
1039
1040         def keyGoUp(self):
1041                 if len(self.servicePath) > 1:
1042                         if self.isBasePathEqual(self.bouquet_root):
1043                                 self.showFavourites()
1044                         else:
1045                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
1046                                 if self.isBasePathEqual(ref):
1047                                         self.showSatellites()
1048                                 else:
1049                                         ref = eServiceReference('%s FROM PROVIDERS ORDER BY name'%(self.service_types))
1050                                         if self.isBasePathEqual(ref):
1051                                                 self.showProviders()
1052                                         else:
1053                                                 self.showAllServices()
1054
1055         def nextMarker(self):
1056                 self.servicelist.moveToNextMarker()
1057
1058         def prevMarker(self):
1059                 self.servicelist.moveToPrevMarker()
1060
1061 HISTORYSIZE = 20
1062
1063 #config for lastservice
1064 config.tv = ConfigSubsection()
1065 config.tv.lastservice = ConfigText()
1066 config.tv.lastroot = ConfigText()
1067 config.radio = ConfigSubsection()
1068 config.radio.lastservice = ConfigText()
1069 config.radio.lastroot = ConfigText()
1070 config.servicelist = ConfigSubsection()
1071 config.servicelist.lastmode = ConfigText(default = "tv")
1072
1073 class ChannelSelection(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, SelectionEventInfo):
1074         def __init__(self, session):
1075                 ChannelSelectionBase.__init__(self,session)
1076                 ChannelSelectionEdit.__init__(self)
1077                 ChannelSelectionEPG.__init__(self)
1078                 SelectionEventInfo.__init__(self)
1079
1080                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1081                         {
1082                                 "cancel": self.cancel,
1083                                 "ok": self.channelSelected,
1084                                 "keyRadio": self.setModeRadio,
1085                                 "keyTV": self.setModeTv,
1086                         })
1087
1088                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1089                         {
1090                                 iPlayableService.evStart: self.__evServiceStart,
1091                                 iPlayableService.evEnd: self.__evServiceEnd
1092                         })
1093
1094                 self.lastChannelRootTimer = eTimer()
1095                 self.lastChannelRootTimer.callback.append(self.__onCreate)
1096                 self.lastChannelRootTimer.start(100,True)
1097
1098                 self.history_tv = [ ]
1099                 self.history_radio = [ ]
1100                 self.history = self.history_tv
1101                 self.history_pos = 0
1102
1103                 self.lastservice = config.tv.lastservice
1104                 self.lastroot = config.tv.lastroot
1105                 self.revertMode = None
1106                 config.usage.multibouquet.addNotifier(self.multibouquet_config_changed)
1107                 self.new_service_played = False
1108                 self.onExecBegin.append(self.asciiOn)
1109
1110         def asciiOn(self):
1111                 rcinput = eRCInput.getInstance()
1112                 rcinput.setKeyboardMode(rcinput.kmAscii)
1113
1114         def asciiOff(self):
1115                 rcinput = eRCInput.getInstance()
1116                 rcinput.setKeyboardMode(rcinput.kmNone)
1117
1118         def multibouquet_config_changed(self, val):
1119                 self.recallBouquetMode()
1120
1121         def __evServiceStart(self):
1122                 service = self.session.nav.getCurrentService()
1123                 if service:
1124                         info = service.info()
1125                         if info:
1126                                 refstr = info.getInfoString(iServiceInformation.sServiceref)
1127                                 self.servicelist.setPlayableIgnoreService(eServiceReference(refstr))
1128
1129         def __evServiceEnd(self):
1130                 self.servicelist.setPlayableIgnoreService(eServiceReference())
1131
1132         def setMode(self):
1133                 self.restoreRoot()
1134                 lastservice=eServiceReference(self.lastservice.value)
1135                 if lastservice.valid():
1136                         self.setCurrentSelection(lastservice)
1137
1138         def setModeTv(self):
1139                 if self.revertMode is None and config.servicelist.lastmode.value == "radio":
1140                         self.revertMode = MODE_RADIO
1141                 self.history = self.history_tv
1142                 self.lastservice = config.tv.lastservice
1143                 self.lastroot = config.tv.lastroot
1144                 config.servicelist.lastmode.value = "tv"
1145                 self.setTvMode()
1146                 self.setMode()
1147
1148         def setModeRadio(self):
1149                 if self.revertMode is None and config.servicelist.lastmode.value == "tv":
1150                         self.revertMode = MODE_TV
1151                 if config.usage.e1like_radio_mode.value:
1152                         self.history = self.history_radio
1153                         self.lastservice = config.radio.lastservice
1154                         self.lastroot = config.radio.lastroot
1155                         config.servicelist.lastmode.value = "radio"
1156                         self.setRadioMode()
1157                         self.setMode()
1158
1159         def __onCreate(self):
1160                 if config.usage.e1like_radio_mode.value:
1161                         if config.servicelist.lastmode.value == "tv":
1162                                 self.setModeTv()
1163                         else:
1164                                 self.setModeRadio()
1165                 else:
1166                         self.setModeTv()
1167                 lastservice=eServiceReference(self.lastservice.value)
1168                 if lastservice.valid():
1169                         self.zap()
1170
1171         def channelSelected(self):
1172                 ref = self.getCurrentSelection()
1173                 if self.movemode:
1174                         self.toggleMoveMarked()
1175                 elif (ref.flags & 7) == 7:
1176                         self.enterPath(ref)
1177                 elif self.bouquet_mark_edit != OFF:
1178                         if not (self.bouquet_mark_edit == EDIT_ALTERNATIVES and ref.flags & eServiceReference.isGroup):
1179                                 self.doMark()
1180                 elif not (ref.flags & eServiceReference.isMarker): # no marker
1181                         root = self.getRoot()
1182                         if not root or not (root.flags & eServiceReference.isGroup):
1183                                 self.zap()
1184                                 self.asciiOff()
1185                                 self.close(ref)
1186
1187         #called from infoBar and channelSelected
1188         def zap(self):
1189                 self.revertMode=None
1190                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1191                 nref = self.getCurrentSelection()
1192                 if ref is None or ref != nref:
1193                         self.new_service_played = True
1194                         self.session.nav.playService(nref)
1195                         self.saveRoot()
1196                         self.saveChannel(nref)
1197                         config.servicelist.lastmode.save()
1198                         self.addToHistory(nref)
1199
1200         def newServicePlayed(self):
1201                 ret = self.new_service_played
1202                 self.new_service_played = False
1203                 return ret
1204
1205         def addToHistory(self, ref):
1206                 if self.servicePath is not None:
1207                         tmp=self.servicePath[:]
1208                         tmp.append(ref)
1209                         try:
1210                                 del self.history[self.history_pos+1:]
1211                         except:
1212                                 pass
1213                         self.history.append(tmp)
1214                         hlen = len(self.history)
1215                         if hlen > HISTORYSIZE:
1216                                 del self.history[0]
1217                                 hlen -= 1
1218                         self.history_pos = hlen-1
1219
1220         def historyBack(self):
1221                 hlen = len(self.history)
1222                 if hlen > 1 and self.history_pos > 0:
1223                         self.history_pos -= 1
1224                         self.setHistoryPath()
1225
1226         def historyNext(self):
1227                 hlen = len(self.history)
1228                 if hlen > 1 and self.history_pos < (hlen-1):
1229                         self.history_pos += 1
1230                         self.setHistoryPath()
1231
1232         def setHistoryPath(self):
1233                 path = self.history[self.history_pos][:]
1234                 ref = path.pop()
1235                 del self.servicePath[:]
1236                 self.servicePath += path
1237                 self.saveRoot()
1238                 plen = len(path)
1239                 root = path[plen-1]
1240                 cur_root = self.getRoot()
1241                 if cur_root and cur_root != root:
1242                         self.setRoot(root)
1243                 self.session.nav.playService(ref)
1244                 self.setCurrentSelection(ref)
1245                 self.saveChannel(ref)
1246
1247         def saveRoot(self):
1248                 path = ''
1249                 for i in self.servicePath:
1250                         path += i.toString()
1251                         path += ';'
1252                 if len(path) and path != self.lastroot.value:
1253                         self.lastroot.value = path
1254                         self.lastroot.save()
1255
1256         def restoreRoot(self):
1257                 self.clearPath()
1258                 re = compile('.+?;')
1259                 tmp = re.findall(self.lastroot.value)
1260                 cnt = 0
1261                 for i in tmp:
1262                         self.servicePath.append(eServiceReference(i[:len(i)-1]))
1263                         cnt += 1
1264                 if cnt:
1265                         path = self.servicePath.pop()
1266                         self.enterPath(path)
1267                 else:
1268                         self.showFavourites()
1269                         self.saveRoot()
1270
1271         def preEnterPath(self, refstr):
1272                 if len(self.servicePath) and self.servicePath[0] != eServiceReference(refstr):
1273                         pathstr = self.lastroot.value
1274                         if pathstr is not None and pathstr.find(refstr) == 0:
1275                                 self.restoreRoot()
1276                                 lastservice=eServiceReference(self.lastservice.value)
1277                                 if lastservice.valid():
1278                                         self.setCurrentSelection(lastservice)
1279                                 return True
1280                 return False
1281
1282         def saveChannel(self, ref):
1283                 if ref is not None:
1284                         refstr = ref.toString()
1285                 else:
1286                         refstr = ""
1287                 if refstr != self.lastservice.value:
1288                         self.lastservice.value = refstr
1289                         self.lastservice.save()
1290
1291         def setCurrentServicePath(self, path):
1292                 hlen = len(self.history)
1293                 if hlen > 0:
1294                         self.history[self.history_pos] = path
1295                 else:
1296                         self.history.append(path)
1297                 self.setHistoryPath()
1298
1299         def getCurrentServicePath(self):
1300                 hlen = len(self.history)
1301                 if hlen > 0:
1302                         return self.history[self.history_pos]
1303                 return None
1304
1305         def recallPrevService(self):
1306                 hlen = len(self.history)
1307                 if hlen > 1:
1308                         if self.history_pos == hlen-1:
1309                                 tmp = self.history[self.history_pos]
1310                                 self.history[self.history_pos] = self.history[self.history_pos-1]
1311                                 self.history[self.history_pos-1] = tmp
1312                         else:
1313                                 tmp = self.history[self.history_pos+1]
1314                                 self.history[self.history_pos+1] = self.history[self.history_pos]
1315                                 self.history[self.history_pos] = tmp
1316                         self.setHistoryPath()
1317
1318         def cancel(self):
1319                 if self.revertMode is None:
1320                         self.restoreRoot()
1321                         lastservice=eServiceReference(self.lastservice.value)
1322                         if lastservice.valid() and self.getCurrentSelection() != lastservice:
1323                                 self.setCurrentSelection(lastservice)
1324                 elif self.revertMode == MODE_TV:
1325                         self.setModeTv()
1326                 elif self.revertMode == MODE_RADIO:
1327                         self.setModeRadio()
1328                 self.revertMode = None
1329                 self.asciiOff()
1330                 self.close(None)
1331
1332 class RadioInfoBar(Screen):
1333         def __init__(self, session):
1334                 Screen.__init__(self, session)
1335                 self["RdsDecoder"] = RdsDecoder(self.session.nav)
1336
1337 class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG, InfoBarBase):
1338         ALLOW_SUSPEND = True
1339
1340         def __init__(self, session, infobar):
1341                 ChannelSelectionBase.__init__(self, session)
1342                 ChannelSelectionEdit.__init__(self)
1343                 ChannelSelectionEPG.__init__(self)
1344                 InfoBarBase.__init__(self)
1345                 self.infobar = infobar
1346                 self.onLayoutFinish.append(self.onCreate)
1347
1348                 self.info = session.instantiateDialog(RadioInfoBar) # our simple infobar
1349
1350                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1351                         {
1352                                 "keyTV": self.closeRadio,
1353                                 "keyRadio": self.closeRadio,
1354                                 "cancel": self.closeRadio,
1355                                 "ok": self.channelSelected,
1356                         })
1357
1358                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1359                         {
1360                                 iPlayableService.evStart: self.__evServiceStart,
1361                                 iPlayableService.evEnd: self.__evServiceEnd
1362                         })
1363
1364 ########## RDS Radiotext / Rass Support BEGIN
1365                 self.infobar = infobar # reference to real infobar (the one and only)
1366                 self["RdsDecoder"] = self.info["RdsDecoder"]
1367                 self["RdsActions"] = HelpableActionMap(self, "InfobarRdsActions",
1368                 {
1369                         "startRassInteractive": (self.startRassInteractive, _("View Rass interactive..."))
1370                 },-1)
1371                 self["RdsActions"].setEnabled(False)
1372                 infobar.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
1373                 self.onClose.append(self.__onClose)
1374
1375         def __onClose(self):
1376                 lastservice=eServiceReference(config.tv.lastservice.value)
1377                 self.session.nav.playService(lastservice)
1378
1379         def startRassInteractive(self):
1380                 self.info.hide();
1381                 self.infobar.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
1382
1383         def RassInteractiveClosed(self):
1384                 self.info.show()
1385                 self.infobar.rass_interactive = None
1386                 self.infobar.RassSlidePicChanged()
1387
1388         def RassInteractivePossibilityChanged(self, state):
1389                 self["RdsActions"].setEnabled(state)
1390 ########## RDS Radiotext / Rass Support END
1391
1392         def closeRadio(self):
1393                 self.infobar.rds_display.onRassInteractivePossibilityChanged.remove(self.RassInteractivePossibilityChanged)
1394                 self.info.hide()
1395                 #set previous tv service
1396                 self.close(None)
1397
1398         def __evServiceStart(self):
1399                 service = self.session.nav.getCurrentService()
1400                 if service:
1401                         info = service.info()
1402                         if info:
1403                                 refstr = info.getInfoString(iServiceInformation.sServiceref)
1404                                 self.servicelist.setPlayableIgnoreService(eServiceReference(refstr))
1405
1406         def __evServiceEnd(self):
1407                 self.servicelist.setPlayableIgnoreService(eServiceReference())
1408
1409         def saveRoot(self):
1410                 path = ''
1411                 for i in self.servicePathRadio:
1412                         path += i.toString()
1413                         path += ';'
1414                 if len(path) and path != config.radio.lastroot.value:
1415                         config.radio.lastroot.value = path
1416                         config.radio.lastroot.save()
1417
1418         def restoreRoot(self):
1419                 self.clearPath()
1420                 re = compile('.+?;')
1421                 tmp = re.findall(config.radio.lastroot.value)
1422                 cnt = 0
1423                 for i in tmp:
1424                         self.servicePathRadio.append(eServiceReference(i[:len(i)-1]))
1425                         cnt += 1
1426                 if cnt:
1427                         path = self.servicePathRadio.pop()
1428                         self.enterPath(path)
1429                 else:
1430                         self.showFavourites()
1431                         self.saveRoot()
1432
1433         def preEnterPath(self, refstr):
1434                 if len(self.servicePathRadio) and self.servicePathRadio[0] != eServiceReference(refstr):
1435                         pathstr = config.radio.lastroot.value
1436                         if pathstr is not None and pathstr.find(refstr) == 0:
1437                                 self.restoreRoot()
1438                                 lastservice=eServiceReference(config.radio.lastservice.value)
1439                                 if lastservice.valid():
1440                                         self.setCurrentSelection(lastservice)
1441                                 return True
1442                 return False
1443
1444         def onCreate(self):
1445                 self.setRadioMode()
1446                 self.restoreRoot()
1447                 lastservice=eServiceReference(config.radio.lastservice.value)
1448                 if lastservice.valid():
1449                         self.servicelist.setCurrent(lastservice)
1450                         self.session.nav.playService(lastservice)
1451                 else:
1452                         self.session.nav.stopService()
1453                 self.info.show()
1454
1455         def channelSelected(self): # just return selected service
1456                 ref = self.getCurrentSelection()
1457                 if self.movemode:
1458                         self.toggleMoveMarked()
1459                 elif (ref.flags & 7) == 7:
1460                         self.enterPath(ref)
1461                 elif self.bouquet_mark_edit != OFF:
1462                         if not (self.bouquet_mark_edit == EDIT_ALTERNATIVES and ref.flags & eServiceReference.isGroup):
1463                                 self.doMark()
1464                 elif not (ref.flags & eServiceReference.isMarker): # no marker
1465                         cur_root = self.getRoot()
1466                         if not cur_root or not (cur_root.flags & eServiceReference.isGroup):
1467                                 playingref = self.session.nav.getCurrentlyPlayingServiceReference()
1468                                 if playingref is None or playingref != ref:
1469                                         self.session.nav.playService(ref)
1470                                         config.radio.lastservice.value = ref.toString()
1471                                         config.radio.lastservice.save()
1472                                 self.saveRoot()
1473
1474 class SimpleChannelSelection(ChannelSelectionBase):
1475         def __init__(self, session, title):
1476                 ChannelSelectionBase.__init__(self, session)
1477                 self.title = title
1478                 self.onShown.append(self.__onExecCallback)
1479
1480                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1481                         {
1482                                 "cancel": self.close,
1483                                 "ok": self.channelSelected,
1484                                 "keyRadio": self.setModeRadio,
1485                                 "keyTV": self.setModeTv,
1486                         })
1487
1488         def __onExecCallback(self):
1489                 self.setTitle(self.title)
1490                 self.setModeTv()
1491
1492         def channelSelected(self): # just return selected service
1493                 ref = self.getCurrentSelection()
1494                 if (ref.flags & 7) == 7:
1495                         self.enterPath(ref)
1496                 elif not (ref.flags & eServiceReference.isMarker):
1497                         ref = self.getCurrentSelection()
1498                         self.close(ref)
1499
1500         def setModeTv(self):
1501                 self.setTvMode()
1502                 self.showFavourites()
1503
1504         def setModeRadio(self):
1505                 self.setRadioMode()
1506                 self.showFavourites()