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