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