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