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