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