more work on keyboard support
[enigma2.git] / lib / python / Screens / ChannelSelection.py
1 from Screen import Screen
2 from Components.Button import Button
3 from Components.ServiceList import ServiceList
4 from Components.ActionMap import NumberActionMap, ActionMap
5 from Components.MenuList import MenuList
6 from EpgSelection import EPGSelection
7 from enigma import eServiceReference, eEPGCache, eEPGCachePtr, eServiceCenter, eServiceCenterPtr, iMutableServiceListPtr, iStaticServiceInformationPtr, eTimer, eDVBDB
8 from Components.config import config, configElement, ConfigSubsection, configText, currentConfigSelectionElement
9 from Screens.FixedMenu import FixedMenu
10 from Tools.NumericalTextInput import NumericalTextInput
11 from Components.NimManager import nimmanager
12 from Components.ServiceName import ServiceName
13 from Components.Clock import Clock
14 from Components.EventInfo import EventInfo
15 from Components.Input import Input
16 from Screens.InputBox import InputBox
17 from ServiceReference import ServiceReference
18 from re import *
19 from os import remove
20
21 FLAG_SERVICE_NEW_FOUND = 64 #define in lib/dvb/idvb.h as dxNewFound = 64
22
23 import xml.dom.minidom
24
25 class BouquetSelector(Screen):
26         def __init__(self, session, bouquets, selectedFunc, enableWrapAround=False):
27                 Screen.__init__(self, session)
28
29                 self.selectedFunc=selectedFunc
30
31                 self["actions"] = ActionMap(["OkCancelActions"],
32                         {
33                                 "ok": self.okbuttonClick,
34                                 "cancel": self.cancelClick
35                         })
36                 entrys = [ ]
37                 for x in bouquets:
38                         entrys.append((x[0], x[1]))
39                 self["menu"] = MenuList(entrys, enableWrapAround)
40
41         def getCurrent(self):
42                 cur = self["menu"].getCurrent()
43                 return cur and cur[1]
44
45         def okbuttonClick(self):
46                 self.selectedFunc(self.getCurrent())
47
48         def up(self):
49                 self["menu"].up()
50
51         def down(self):
52                 self["menu"].down()
53
54         def cancelClick(self):
55                 self.close(False)
56
57 class ChannelContextMenu(Screen):
58         def __init__(self, session, csel):
59                 Screen.__init__(self, session)
60                 self.csel = csel
61                 self.bsel = None
62
63                 self["actions"] = ActionMap(["OkCancelActions"],
64                         {
65                                 "ok": self.okbuttonClick,
66                                 "cancel": self.cancelClick
67                         })
68                 menu = [ ]
69
70                 current_root = csel.getRoot()
71                 current_sel_path = csel.getCurrentSelection().getPath()
72                 current_sel_flags = csel.getCurrentSelection().flags
73                 inBouquetRootList = current_root and current_root.getPath().find('FROM BOUQUET "bouquets.') != -1 #FIXME HACK
74                 inBouquet = csel.getMutableList() is not None
75                 haveBouquets = csel.bouquet_root.getPath().find('FROM BOUQUET "bouquets.') != -1
76
77                 if not csel.bouquet_mark_edit and not csel.movemode:
78                         if not inBouquetRootList:
79                                 if (csel.getCurrentSelection().flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
80                                         if haveBouquets:
81                                                 menu.append((_("add service to bouquet"), self.addServiceToBouquetSelected))
82                                         else:
83                                                 menu.append((_("add service to favourites"), self.addServiceToBouquetSelected))
84                                 else:
85                                         if haveBouquets:
86                                                 if not inBouquet and current_sel_path.find("PROVIDERS") == -1:
87                                                         menu.append((_("copy to favourites"), self.copyCurrentToBouquetList))
88                                         if current_sel_path.find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
89                                                 menu.append((_("remove all new found flags"), self.removeAllNewFoundFlags))
90                                 if inBouquet:
91                                         menu.append((_("remove service"), self.removeCurrentService))
92                                 if current_root.getPath().find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
93                                         menu.append((_("remove new found flag"), self.removeNewFoundFlag))
94                         elif haveBouquets:
95                                 menu.append((_("remove bouquet"), self.removeBouquet))
96
97                 if inBouquet: # current list is editable?
98                         if not csel.bouquet_mark_edit:
99                                 if not csel.movemode:
100                                         menu.append((_("enable move mode"), self.toggleMoveMode))
101                                         menu.append((_("add bouquet..."), self.showBouquetInputBox))
102                                         if not inBouquetRootList:
103                                                 if haveBouquets:
104                                                         menu.append((_("enable bouquet edit"), self.bouquetMarkStart))
105                                                 else:
106                                                         menu.append((_("enable favourite edit"), self.bouquetMarkStart))
107                                 else:
108                                         menu.append((_("disable move mode"), self.toggleMoveMode))
109                         elif not inBouquetRootList:
110                                 if haveBouquets:
111                                         menu.append((_("end bouquet edit"), self.bouquetMarkEnd))
112                                         menu.append((_("abort bouquet edit"), self.bouquetMarkAbort))
113                                 else:
114                                         menu.append((_("end favourites edit"), self.bouquetMarkEnd))
115                                         menu.append((_("abort favourites edit"), self.bouquetMarkAbort))
116
117                 menu.append((_("back"), self.cancelClick))
118                 self["menu"] = MenuList(menu)
119
120         def okbuttonClick(self):
121                 self["menu"].getCurrent()[1]()
122
123         def cancelClick(self):
124                 self.close(False)
125                 
126         def showBouquetInputBox(self):
127                 self.session.openWithCallback(self.bouquetInputCallback, InputBox, title=_("Please enter a name for the new bouquet"), text="bouquetname", maxSize=False, type=Input.TEXT)
128
129         def bouquetInputCallback(self, bouquet):
130                 if bouquet is not None:
131                         self.csel.addBouquet(bouquet, None, True)
132
133         def addServiceToBouquetSelected(self):
134                 bouquets = self.csel.getBouquetList()
135                 if bouquets is None:
136                         cnt = 0
137                 else:
138                         cnt = len(bouquets)
139                 if cnt > 1: # show bouquet list
140                         self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, bouquets, self.addCurrentServiceToBouquet)
141                 elif cnt == 1: # add to only one existing bouquet
142                         self.addCurrentServiceToBouquet(bouquets[0][1])
143                 else: #no bouquets in root.. so assume only one favourite list is used
144                         self.addCurrentServiceToBouquet(self.csel.bouquet_root)
145
146         def bouquetSelClosed(self, recursive):
147                 self.bsel = None
148                 if recursive:
149                         self.close(False)
150
151         def copyCurrentToBouquetList(self):
152                 self.csel.copyCurrentToBouquetList()
153                 self.close()
154
155         def removeBouquet(self):
156                 self.csel.removeBouquet()
157                 self.close()
158
159         def addCurrentServiceToBouquet(self, dest):
160                 self.csel.addCurrentServiceToBouquet(dest)
161                 if self.bsel is not None:
162                         self.bsel.close(True)
163                 else:
164                         self.close(True) # close bouquet selection
165
166         def removeCurrentService(self):
167                 self.csel.removeCurrentService()
168                 self.close()
169
170         def toggleMoveMode(self):
171                 self.csel.toggleMoveMode()
172                 self.close()
173
174         def bouquetMarkStart(self):
175                 self.csel.startMarkedEdit()
176                 self.close()
177
178         def bouquetMarkEnd(self):
179                 self.csel.endMarkedEdit(abort=False)
180                 self.close()
181
182         def bouquetMarkAbort(self):
183                 self.csel.endMarkedEdit(abort=True)
184                 self.close()
185
186         def removeNewFoundFlag(self):
187                 eDVBDB.getInstance().removeFlag(self.csel.getCurrentSelection(), FLAG_SERVICE_NEW_FOUND)
188                 self.close()
189
190         def removeAllNewFoundFlags(self):
191                 curpath = self.csel.getCurrentSelection().getPath()
192                 idx = curpath.find("satellitePosition == ")
193                 if idx != -1:
194                         tmp = curpath[idx+21:]
195                         idx = tmp.find(')')
196                         if idx != -1:
197                                 satpos = int(tmp[:idx])
198                                 eDVBDB.getInstance().removeFlags(FLAG_SERVICE_NEW_FOUND, -1, -1, -1, satpos)
199                 self.close()
200
201 class ChannelSelectionEPG:
202         def __init__(self):
203                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
204                         {
205                                 "showEPGList": self.showEPGList,
206                         })
207
208         def showEPGList(self):
209                 ref=self.getCurrentSelection()
210                 ptr=eEPGCache.getInstance()
211                 if ptr.startTimeQuery(ref) != -1:
212                         self.session.open(EPGSelection, ref)
213                 else:
214                         print 'no epg for service', ref.toString()
215
216 class ChannelSelectionEdit:
217         def __init__(self):
218                 self.entry_marked = False
219                 self.movemode = False
220                 self.bouquet_mark_edit = False
221                 self.mutableList = None
222                 self.__marked = [ ]
223                 self.saved_title = None
224                 self.saved_root = None
225
226                 class ChannelSelectionEditActionMap(ActionMap):
227                         def __init__(self, csel, contexts = [ ], actions = { }, prio=0):
228                                 ActionMap.__init__(self, contexts, actions, prio)
229                                 self.csel = csel
230
231                         def action(self, contexts, action):
232                                 if action == "cancel":
233                                         self.csel.handleEditCancel()
234                                         return 0 # fall-trough
235                                 elif action == "ok":
236                                         return 0 # fall-trough
237                                 else:
238                                         return ActionMap.action(self, contexts, action)
239
240                 self["ChannelSelectEditActions"] = ChannelSelectionEditActionMap(self, ["ChannelSelectEditActions", "OkCancelActions"],
241                         {
242                                 "contextMenu": self.doContext,
243                         })
244
245         def getMutableList(self, root=eServiceReference()):
246                 if not self.mutableList is None:
247                         return self.mutableList
248                 serviceHandler = eServiceCenter.getInstance()
249                 if not root.valid():
250                         root=self.getRoot()
251                 list = root and serviceHandler.list(root)
252                 if list is not None:
253                         return list.startEdit()
254                 return None
255
256         def buildBouquetID(self, str):
257                 tmp = str.lower()
258                 name = ''
259                 for c in tmp:
260                         if (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9'):
261                                 name += c
262                         else:
263                                 name += '_'
264                 return name
265
266         def addBouquet(self, bName, services, refresh=False):
267                 serviceHandler = eServiceCenter.getInstance()
268                 mutableBouquetList = serviceHandler.list(self.bouquet_root).startEdit()
269                 if mutableBouquetList:
270                         if self.mode == MODE_TV:
271                                 bName += " (TV)"
272                                 str = '1:7:1:0:0:0:0:0:0:0:(type == 1) FROM BOUQUET \"userbouquet.%s.tv\" ORDER BY bouquet'%(self.buildBouquetID(bName))
273                         else:
274                                 bName += " (Radio)"
275                                 str = '1:7:2:0:0:0:0:0:0:0:(type == 2) FROM BOUQUET \"userbouquet.%s.radio\" ORDER BY bouquet'%(self.buildBouquetID(bName))
276                         new_bouquet_ref = eServiceReference(str)
277                         if not mutableBouquetList.addService(new_bouquet_ref):
278                                 self.bouquetNumOffsetCache = { }
279                                 mutableBouquetList.flushChanges()
280                                 eDVBDB.getInstance().reloadBouquets()
281                                 mutableBouquet = serviceHandler.list(new_bouquet_ref).startEdit()
282                                 if mutableBouquet:
283                                         mutableBouquet.setListName(bName)
284                                         if services is not None:
285                                                 for service in services:
286                                                         if mutableBouquet.addService(service):
287                                                                 print "add", service.toString(), "to new bouquet failed"
288                                         mutableBouquet.flushChanges()
289                                         if refresh:
290                                                 self.setRoot(self.getRoot())
291                                 else:
292                                         print "get mutable list for new created bouquet failed"
293                         else:
294                                 print "add", str, "to bouquets failed"
295                 else:
296                         print "bouquetlist is not editable"
297
298         def copyCurrentToBouquetList(self):
299                 provider = ServiceReference(self.getCurrentSelection())
300                 providerName = provider.getServiceName()
301                 serviceHandler = eServiceCenter.getInstance()
302                 services = serviceHandler.list(provider.ref)
303                 self.addBouquet(providerName, services and services.getContent('R', True))
304
305         def removeBouquet(self):
306                 refstr = self.getCurrentSelection().toString()
307                 self.bouquetNumOffsetCache = { }
308                 pos = refstr.find('FROM BOUQUET "')
309                 if pos != -1:
310                         refstr = refstr[pos+14:]
311                         pos = refstr.find('"')
312                         if pos != -1:
313                                 filename = '/etc/enigma2/' + refstr[:pos] # FIXMEEE !!! HARDCODED /etc/enigma2
314                 self.removeCurrentService()
315                 try:
316                         remove(filename)
317                 except OSError:
318                         print "error during remove of", filename
319                 eDVBDB.getInstance().reloadBouquets()
320
321 #  multiple marked entry stuff ( edit mode, later multiepg selection )
322         def startMarkedEdit(self):
323                 self.mutableList = self.getMutableList()
324                 # add all services from the current list to internal marked set in listboxservicecontent
325                 self.clearMarks() # this clears the internal marked set in the listboxservicecontent
326                 self.saved_title = self.instance.getTitle()
327                 pos = self.saved_title.find(')')
328                 new_title = self.saved_title[:pos+1]
329                 if self.bouquet_root.getPath().find('FROM BOUQUET "bouquets.') != -1:
330                         new_title += ' ' + _("[bouquet edit]")
331                 else:
332                         new_title += ' ' + _("[favourite edit]")
333                 self.setTitle(new_title)
334                 self.bouquet_mark_edit = True
335                 self.__marked = self.servicelist.getRootServices()
336                 for x in self.__marked:
337                         self.servicelist.addMarked(eServiceReference(x))
338                 self.savedPath = self.servicePath[:]
339                 self.showAllServices()
340
341         def endMarkedEdit(self, abort):
342                 if not abort and self.mutableList is not None:
343                         self.bouquetNumOffsetCache = { }
344                         new_marked = set(self.servicelist.getMarked())
345                         old_marked = set(self.__marked)
346                         removed = old_marked - new_marked
347                         added = new_marked - old_marked
348                         changed = False
349                         for x in removed:
350                                 changed = True
351                                 self.mutableList.removeService(eServiceReference(x))
352                         for x in added:
353                                 changed = True
354                                 self.mutableList.addService(eServiceReference(x))
355                         if changed:
356                                 self.mutableList.flushChanges()
357                 self.__marked = []
358                 self.clearMarks()
359                 self.bouquet_mark_edit = False
360                 self.mutableList = None
361                 self.setTitle(self.saved_title)
362                 self.saved_title = None
363                 # self.servicePath is just a reference to servicePathTv or Radio...
364                 # so we never ever do use the asignment operator in self.servicePath
365                 del self.servicePath[:] # remove all elements
366                 self.servicePath += self.savedPath # add saved elements
367                 del self.savedPath
368                 self.setRoot(self.servicePath[len(self.servicePath)-1])
369
370         def clearMarks(self):
371                 self.servicelist.clearMarks()
372
373         def doMark(self):
374                 ref = self.servicelist.getCurrent()
375                 if self.servicelist.isMarked(ref):
376                         self.servicelist.removeMarked(ref)
377                 else:
378                         self.servicelist.addMarked(ref)
379
380         def removeCurrentService(self):
381                 ref = self.servicelist.getCurrent()
382                 mutableList = self.getMutableList()
383                 if ref.valid() and mutableList is not None:
384                         if not mutableList.removeService(ref):
385                                 self.bouquetNumOffsetCache = { }
386                                 mutableList.flushChanges() #FIXME dont flush on each single removed service
387                                 self.setRoot(self.getRoot())
388
389         def addCurrentServiceToBouquet(self, dest):
390                 mutableList = self.getMutableList(dest)
391                 if not mutableList is None:
392                         if not mutableList.addService(self.servicelist.getCurrent()):
393                                 self.bouquetNumOffsetCache = { }
394                                 mutableList.flushChanges()
395
396         def toggleMoveMode(self):
397                 if self.movemode:
398                         if self.entry_marked:
399                                 self.toggleMoveMarked() # unmark current entry
400                         self.movemode = False
401                         self.pathChangedDisabled = False # re-enable path change
402                         self.mutableList.flushChanges() # FIXME add check if changes was made
403                         self.mutableList = None
404                         self.setTitle(self.saved_title)
405                         self.saved_title = None
406                         if self.getRoot() == self.bouquet_root:
407                                 self.bouquetNumOffsetCache = { }
408                 else:
409                         self.mutableList = self.getMutableList()
410                         self.movemode = True
411                         self.pathChangedDisabled = True # no path change allowed in movemode
412                         self.saved_title = self.instance.getTitle()
413                         new_title = self.saved_title
414                         pos = self.saved_title.find(')')
415                         new_title = self.saved_title[:pos+1] + ' ' + _("[move mode]") + self.saved_title[pos+1:]
416                         self.setTitle(new_title);
417
418         def handleEditCancel(self):
419                 if self.movemode: #movemode active?
420                         self.channelSelected() # unmark
421                         self.toggleMoveMode() # disable move mode
422                 elif self.bouquet_mark_edit:
423                         self.endMarkedEdit(True) # abort edit mode
424
425         def toggleMoveMarked(self):
426                 if self.entry_marked:
427                         self.servicelist.setCurrentMarked(False)
428                         self.entry_marked = False
429                 else:
430                         self.servicelist.setCurrentMarked(True)
431                         self.entry_marked = True
432
433         def doContext(self):
434                 self.session.open(ChannelContextMenu, self)
435
436 MODE_TV = 0
437 MODE_RADIO = 1
438
439 class ChannelSelectionBase(Screen):
440         def __init__(self, session):
441                 Screen.__init__(self, session)
442
443                 # this makes it much simple to implement a selectable radio or tv mode :)
444                 self.service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 195) || (type == 25)'
445                 self.service_types_radio = '1:7:2:0:0:0:0:0:0:0:(type == 2)'
446
447                 self["key_red"] = Button(_("All"))
448                 self["key_green"] = Button(_("Satellites"))
449                 self["key_yellow"] = Button(_("Provider"))
450                 self["key_blue"] = Button(_("Favourites"))
451
452                 self["list"] = ServiceList()
453                 self.servicelist = self["list"]
454
455                 self.numericalTextInput = NumericalTextInput()
456
457                 self.servicePathTV = [ ]
458                 self.servicePathRadio = [ ]
459                 self.servicePath = [ ]
460
461                 self.mode = MODE_TV
462
463                 self.pathChangedDisabled = False
464
465                 self.bouquetNumOffsetCache = { }
466
467                 self["ChannelSelectBaseActions"] = NumberActionMap(["ChannelSelectBaseActions", "NumberActions"],
468                         {
469                                 "showFavourites": self.showFavourites,
470                                 "showAllServices": self.showAllServices,
471                                 "showProviders": self.showProviders,
472                                 "showSatellites": self.showSatellites,
473                                 "nextBouquet": self.nextBouquet,
474                                 "prevBouquet": self.prevBouquet,
475                                 "1": self.keyNumberGlobal,
476                                 "2": self.keyNumberGlobal,
477                                 "3": self.keyNumberGlobal,
478                                 "4": self.keyNumberGlobal,
479                                 "5": self.keyNumberGlobal,
480                                 "6": self.keyNumberGlobal,
481                                 "7": self.keyNumberGlobal,
482                                 "8": self.keyNumberGlobal,
483                                 "9": self.keyNumberGlobal,
484                                 "0": self.keyNumber0
485                         })
486
487         def appendDVBTypes(self, ref):
488                 path = ref.getPath()
489                 pos = path.find(' FROM BOUQUET')
490                 if pos != -1:
491                         return eServiceReference(self.service_types + path[pos:])
492                 return ref
493
494         def getBouquetNumOffset(self, bouquet):
495                 if self.bouquet_root.getPath().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
496                         return 0
497                 bouquet = self.appendDVBTypes(bouquet)
498                 try:
499                         return self.bouquetNumOffsetCache[bouquet.toString()]
500                 except:
501                         offsetCount = 0
502                         serviceHandler = eServiceCenter.getInstance()
503                         bouquetlist = serviceHandler.list(self.bouquet_root)
504                         if not bouquetlist is None:
505                                 while True:
506                                         bouquetIterator = self.appendDVBTypes(bouquetlist.getNext())
507                                         if not bouquetIterator.valid(): #end of list
508                                                 break
509                                         self.bouquetNumOffsetCache[bouquetIterator.toString()]=offsetCount
510                                         if ((bouquetIterator.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory):
511                                                 continue
512                                         servicelist = serviceHandler.list(bouquetIterator)
513                                         if not servicelist is None:
514                                                 while True:
515                                                         serviceIterator = servicelist.getNext()
516                                                         if not serviceIterator.valid(): #check if end of list
517                                                                 break
518                                                         if serviceIterator.flags: #playable services have no flags
519                                                                 continue
520                                                         offsetCount += 1
521                 return self.bouquetNumOffsetCache.get(bouquet.toString(), offsetCount)
522
523         def recallBouquetMode(self):
524                 if self.mode == MODE_TV:
525                         self.service_types = self.service_types_tv
526                         if currentConfigSelectionElement(config.usage.multibouquet) == "yes":
527                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:(type == 1) FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
528                         else:
529                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'%(self.service_types)
530                 else:
531                         self.service_types = self.service_types_radio
532                         if currentConfigSelectionElement(config.usage.multibouquet) == "yes":
533                                 self.bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:(type == 1) FROM BOUQUET "bouquets.radio" ORDER BY bouquet'
534                         else:
535                                 self.bouquet_rootstr = '%s FROM BOUQUET "userbouquet.favourites.radio" ORDER BY bouquet'%(self.service_types)
536                 self.bouquet_root = eServiceReference(self.bouquet_rootstr)
537
538         def setTvMode(self):
539                 self.mode = MODE_TV
540                 self.servicePath = self.servicePathTV
541                 self.recallBouquetMode()
542                 title = self.instance.getTitle()
543                 pos = title.find(" (")
544                 if pos != -1:
545                         title = title[:pos]
546                 title += " (TV)"
547                 self.setTitle(title)
548
549         def setRadioMode(self):
550                 self.mode = MODE_RADIO
551                 self.servicePath = self.servicePathRadio
552                 self.recallBouquetMode()
553                 title = self.instance.getTitle()
554                 pos = title.find(" (")
555                 if pos != -1:
556                         title = title[:pos]
557                 title += " (Radio)"
558                 self.setTitle(title)
559
560         def setRoot(self, root, justSet=False):
561                 path = root.getPath()
562                 inBouquetRootList = path.find('FROM BOUQUET "bouquets.') != -1 #FIXME HACK
563                 pos = path.find(' FROM BOUQUET')
564                 isBouquet = pos != -1
565                 if not inBouquetRootList and isBouquet:
566                         self.servicelist.setMode(ServiceList.MODE_FAVOURITES)
567                         self.servicelist.setNumberOffset(self.getBouquetNumOffset(root))
568                         refstr = self.service_types + path[pos:]
569                         root = eServiceReference(refstr)
570                 else:
571                         self.servicelist.setMode(ServiceList.MODE_NORMAL)
572                 self.servicelist.setRoot(root, justSet)
573                 self.buildTitleString()
574
575         def removeModeStr(self, str):
576                 if self.mode == MODE_TV:
577                         pos = str.find(' (TV)')
578                 else:
579                         pos = str.find(' (Radio)')
580                 if pos != -1:
581                         return str[:pos]
582                 return str
583
584         def getServiceName(self, ref):
585                 str = self.removeModeStr(ServiceReference(ref).getServiceName())
586                 if not len(str):
587                         pathstr = ref.getPath()
588                         if pathstr.find('FROM PROVIDERS') != -1:
589                                 return _("Provider")
590                         if pathstr.find('FROM SATELLITES') != -1:
591                                 return _("Satellites")
592                         if pathstr.find(') ORDER BY name') != -1:
593                                 return _("All")
594                 return str
595
596         def buildTitleString(self):
597                 titleStr = self.instance.getTitle()
598                 pos = titleStr.find(']')
599                 if pos == -1:
600                         pos = titleStr.find(')')
601                 if pos != -1:
602                         titleStr = titleStr[:pos+1]
603                         Len = len(self.servicePath)
604                         if Len > 0:
605                                 base_ref = self.servicePath[0]
606                                 if Len > 1:
607                                         end_ref = self.servicePath[Len-1]
608                                 else:
609                                         end_ref = None
610                                 nameStr = self.getServiceName(base_ref)
611                                 titleStr += ' ' + nameStr
612                                 if end_ref is not None:
613                                         if Len > 2:
614                                                 titleStr += '/../'
615                                         else:
616                                                 titleStr += '/'
617                                         nameStr = self.getServiceName(end_ref)
618                                         titleStr += nameStr
619                                 self.setTitle(titleStr)
620
621         def moveUp(self):
622                 self.servicelist.moveUp()
623
624         def moveDown(self):
625                 self.servicelist.moveDown()
626
627         def clearPath(self):
628                 del self.servicePath[:]
629
630         def enterPath(self, ref, justSet=False):
631                 self.servicePath.append(ref)
632                 self.setRoot(ref, justSet)
633
634         def pathUp(self, justSet=False):
635                 prev = self.servicePath.pop()
636                 length = len(self.servicePath)
637                 if length:
638                         current = self.servicePath[length-1]
639                         self.setRoot(current, justSet)
640                         if not justSet:
641                                 self.setCurrentSelection(prev)
642                 return prev
643
644         def isBasePathEqual(self, ref):
645                 if len(self.servicePath) > 1 and self.servicePath[0] == ref:
646                         return True
647                 return False
648
649         def isPrevPathEqual(self, ref):
650                 length = len(self.servicePath)
651                 if length > 1 and self.servicePath[length-2] == ref:
652                         return True
653                 return False
654
655         def preEnterPath(self, refstr):
656                 return False
657
658         def showAllServices(self):
659                 if not self.pathChangedDisabled:
660                         refstr = '%s ORDER BY name'%(self.service_types)
661                         if not self.preEnterPath(refstr):
662                                 ref = eServiceReference(refstr)
663                                 currentRoot = self.getRoot()
664                                 if currentRoot is None or currentRoot != ref:
665                                         self.clearPath()
666                                         self.enterPath(ref)
667
668         def showSatellites(self):
669                 if not self.pathChangedDisabled:
670                         refstr = '%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types)
671                         if not self.preEnterPath(refstr):
672                                 ref = eServiceReference(refstr)
673                                 justSet=False
674                                 prev = None
675
676                                 if self.isBasePathEqual(ref):
677                                         if self.isPrevPathEqual(ref):
678                                                 justSet=True
679                                         prev = self.pathUp(justSet)
680                                 else:
681                                         currentRoot = self.getRoot()
682                                         if currentRoot is None or currentRoot != ref:
683                                                 justSet=True
684                                                 self.clearPath()
685                                                 self.enterPath(ref, True)
686                                 if justSet:
687                                         serviceHandler = eServiceCenter.getInstance()
688                                         servicelist = serviceHandler.list(ref)
689                                         if not servicelist is None:
690                                                 while True:
691                                                         service = servicelist.getNext()
692                                                         if not service.valid(): #check if end of list
693                                                                 break
694                                                         orbpos = service.getUnsignedData(4) >> 16
695                                                         if service.getPath().find("FROM PROVIDER") != -1:
696                                                                 service_name = _("Providers")
697                                                         elif service.getPath().find("flags == %d" %(FLAG_SERVICE_NEW_FOUND)) != -1:
698                                                                 service_name = _("New")
699                                                         else:
700                                                                 service_name = _("Services")
701                                                         try:
702                                                                 service_name += str(' - %s'%(nimmanager.getSatDescription(orbpos)))
703                                                                 service.setName(service_name) # why we need this cast?
704                                                         except:
705                                                                 if orbpos == 0xFFFF: #Cable
706                                                                         n = ("%s (%s)") % (service_name, _("Cable"))
707                                                                 elif orbpos == 0xEEEE: #Terrestrial
708                                                                         n = ("%s (%s)") % (service_name, _("Terrestrial"))
709                                                                 else:
710                                                                         if orbpos > 1800: # west
711                                                                                 orbpos = 3600 - orbpos
712                                                                                 h = _("W")
713                                                                         else:
714                                                                                 h = _("E")
715                                                                         n = ("%s (%d.%d" + h + ")") % (service_name, orbpos / 10, orbpos % 10)
716                                                                 service.setName(n)
717                                                         self.servicelist.addService(service)
718                                                         self.servicelist.finishFill()
719                                                         if prev is not None:
720                                                                 self.setCurrentSelection(prev)
721
722         def showProviders(self):
723                 if not self.pathChangedDisabled:
724                         refstr = '%s FROM PROVIDERS ORDER BY name'%(self.service_types)
725                         if not self.preEnterPath(refstr):
726                                 ref = eServiceReference(refstr)
727                                 if self.isBasePathEqual(ref):
728                                         self.pathUp()
729                                 else:
730                                         currentRoot = self.getRoot()
731                                         if currentRoot is None or currentRoot != ref:
732                                                 self.clearPath()
733                                                 self.enterPath(ref)
734
735         def changeBouquet(self, direction):
736                 if not self.pathChangedDisabled:
737                         if self.isBasePathEqual(self.bouquet_root):
738                                 self.pathUp()
739                                 if direction < 0:
740                                         self.moveUp()
741                                 else:
742                                         self.moveDown()
743                                 ref = self.getCurrentSelection()
744                                 self.enterPath(ref)
745
746         def inBouquet(self):
747                 return self.isBasePathEqual(self.bouquet_root)
748
749         def atBegin(self):
750                 return self.servicelist.atBegin()
751
752         def atEnd(self):
753                 return self.servicelist.atEnd()
754
755         def nextBouquet(self):
756                 self.changeBouquet(+1)
757
758         def prevBouquet(self):
759                 self.changeBouquet(-1)
760
761         def showFavourites(self):
762                 if not self.pathChangedDisabled:
763                         if not self.preEnterPath(self.bouquet_rootstr):
764                                 if self.isBasePathEqual(self.bouquet_root):
765                                         self.pathUp()
766                                 else:
767                                         currentRoot = self.getRoot()
768                                         if currentRoot is None or currentRoot != self.bouquet_root:
769                                                 self.clearPath()
770                                                 self.enterPath(self.bouquet_root)
771
772         def keyNumberGlobal(self, number):
773                 char = self.numericalTextInput.getKey(number)
774                 self.servicelist.moveToChar(char)
775
776         def getRoot(self):
777                 return self.servicelist.getRoot()
778
779         def getCurrentSelection(self):
780                 return self.servicelist.getCurrent()
781
782         def setCurrentSelection(self, service):
783                 servicepath = service.getPath()
784                 pos = servicepath.find(" FROM BOUQUET")
785                 if pos != -1:
786                         if self.mode == MODE_TV:
787                                 servicepath = '(type == 1)' + servicepath[pos:]
788                         else:
789                                 servicepath = '(type == 2)' + servicepath[pos:]
790                         service.setPath(servicepath)
791                 self.servicelist.setCurrent(service)
792
793         def getBouquetList(self):
794                 serviceCount=0
795                 bouquets = [ ]
796                 serviceHandler = eServiceCenter.getInstance()
797                 list = serviceHandler.list(self.bouquet_root)
798                 if not list is None:
799                         while True:
800                                 s = list.getNext()
801                                 if not s.valid():
802                                         break
803                                 if ((s.flags & eServiceReference.flagDirectory) == eServiceReference.flagDirectory):
804                                         info = serviceHandler.info(s)
805                                         if not info is None:
806                                                 bouquets.append((info.getName(s), s))
807                                 else:
808                                         serviceCount += 1
809                         if len(bouquets) == 0 and serviceCount > 0:
810                                 info = serviceHandler.info(self.bouquet_root)
811                                 if not info is None:
812                                         bouquets.append((info.getName(self.bouquet_root), self.bouquet_root))
813                         return bouquets
814                 return None
815
816         def keyNumber0(self, num):
817                 if len(self.servicePath) > 1:
818                         self.keyGoUp()
819                 else:
820                         self.keyNumberGlobal(num)
821
822         def keyGoUp(self):
823                 if len(self.servicePath) > 1:
824                         if self.isBasePathEqual(self.bouquet_root):
825                                 self.showFavourites()
826                         else:
827                                 ref = eServiceReference('%s FROM SATELLITES ORDER BY satellitePosition'%(self.service_types))
828                                 if self.isBasePathEqual(ref):
829                                         self.showSatellites()
830                                 else:
831                                         ref = eServiceReference('%s FROM PROVIDERS ORDER BY name'%(self.service_types))
832                                         if self.isBasePathEqual(ref):
833                                                 self.showProviders()
834                                         else:
835                                                 self.showAllServices()
836
837 HISTORYSIZE = 20
838
839 class ChannelSelection(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG):
840         def __init__(self, session):
841                 ChannelSelectionBase.__init__(self,session)
842                 ChannelSelectionEdit.__init__(self)
843                 ChannelSelectionEPG.__init__(self)
844
845                 #config for lastservice
846                 config.tv = ConfigSubsection();
847                 config.tv.lastservice = configElement("config.tv.lastservice", configText, "", 0);
848                 config.tv.lastroot = configElement("config.tv.lastroot", configText, "", 0);
849
850                 self["actions"] = ActionMap(["OkCancelActions"],
851                         {
852                                 "cancel": self.cancel,
853                                 "ok": self.channelSelected,
854                         })
855                 self.onShown.append(self.__onShown)
856
857                 self.lastChannelRootTimer = eTimer()
858                 self.lastChannelRootTimer.timeout.get().append(self.__onCreate)
859                 self.lastChannelRootTimer.start(100,True)
860
861                 self.history = [ ]
862                 self.history_pos = 0
863
864         def __onCreate(self):
865                 self.setTvMode()
866                 self.restoreRoot()
867                 lastservice=eServiceReference(config.tv.lastservice.value)
868                 if lastservice.valid():
869                         self.setCurrentSelection(lastservice)
870                         self.zap()
871
872         def __onShown(self):
873                 self.recallBouquetMode()
874                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
875                 if ref is not None and ref.valid() and ref.getPath() == "":
876                         self.servicelist.setPlayableIgnoreService(ref)
877                 else:
878                         self.servicelist.setPlayableIgnoreService(eServiceReference())
879
880         def channelSelected(self):
881                 ref = self.getCurrentSelection()
882                 if self.movemode:
883                         self.toggleMoveMarked()
884                 elif (ref.flags & 7) == 7:
885                         self.enterPath(ref)
886                 elif self.bouquet_mark_edit:
887                         self.doMark()
888                 else:
889                         self.zap()
890                         self.close(ref)
891
892         #called from infoBar and channelSelected
893         def zap(self):
894                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
895                 nref = self.getCurrentSelection()
896                 if ref is None or ref != nref:
897                         self.session.nav.playService(nref)
898                         self.saveRoot()
899                         self.saveChannel()
900                         self.addToHistory(nref)
901
902         def addToHistory(self, ref):
903                 if self.servicePath is not None:
904                         tmp=self.servicePath[:]
905                         tmp.append(ref)
906                         try:
907                                 del self.history[self.history_pos+1:]
908                         except:
909                                 pass
910                         self.history.append(tmp)
911                         hlen = len(self.history)
912                         if hlen > HISTORYSIZE:
913                                 del self.history[0]
914                                 hlen -= 1
915                         self.history_pos = hlen-1
916
917         def historyBack(self):
918                 hlen = len(self.history)
919                 if hlen > 1 and self.history_pos > 0:
920                         self.history_pos -= 1
921                         self.setHistoryPath()
922
923         def historyNext(self):
924                 hlen = len(self.history)
925                 if hlen > 1 and self.history_pos < (hlen-1):
926                         self.history_pos += 1
927                         self.setHistoryPath()
928
929         def setHistoryPath(self):
930                 path = self.history[self.history_pos][:]
931                 ref = path.pop()
932                 del self.servicePath[:]
933                 self.servicePath += path
934                 self.saveRoot()
935                 plen = len(path)
936                 root = path[plen-1]
937                 if self.getRoot() != root:
938                         self.setRoot(root)
939                 self.session.nav.playService(ref)
940                 self.setCurrentSelection(ref)
941                 self.saveChannel()
942
943         def saveRoot(self):
944                 path = ''
945                 for i in self.servicePathTV:
946                         path += i.toString()
947                         path += ';'
948                 if len(path) and path != config.tv.lastroot.value:
949                         config.tv.lastroot.value = path
950                         config.tv.lastroot.save()
951
952         def restoreRoot(self):
953                 self.clearPath()
954                 re = compile('.+?;')
955                 tmp = re.findall(config.tv.lastroot.value)
956                 cnt = 0
957                 for i in tmp:
958                         self.servicePathTV.append(eServiceReference(i[:len(i)-1]))
959                         cnt += 1
960                 if cnt:
961                         path = self.servicePathTV.pop()
962                         self.enterPath(path)
963                 else:
964                         self.showFavourites()
965                         self.saveRoot()
966
967         def preEnterPath(self, refstr):
968                 if len(self.servicePathTV) and self.servicePathTV[0] != eServiceReference(refstr):
969                         pathstr = config.tv.lastroot.value
970                         if pathstr is not None and pathstr.find(refstr) == 0:
971                                 self.restoreRoot()
972                                 lastservice=eServiceReference(config.tv.lastservice.value)
973                                 if lastservice.valid():
974                                         self.setCurrentSelection(lastservice)
975                                 return True
976                 return False
977
978         def saveChannel(self):
979                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
980                 if ref is not None:
981                         refstr = ref.toString()
982                 else:
983                         refstr = ""
984                 if refstr != config.tv.lastservice.value:
985                         config.tv.lastservice.value = refstr
986                         config.tv.lastservice.save()
987
988         def recallPrevService(self):
989                 hlen = len(self.history)
990                 if hlen > 1:
991                         if self.history_pos == hlen-1:
992                                 tmp = self.history[self.history_pos]
993                                 self.history[self.history_pos] = self.history[self.history_pos-1]
994                                 self.history[self.history_pos-1] = tmp
995                         else:
996                                 tmp = self.history[self.history_pos+1]
997                                 self.history[self.history_pos+1] = self.history[self.history_pos]
998                                 self.history[self.history_pos] = tmp
999                         self.setHistoryPath()
1000
1001         def cancel(self):
1002                 self.close(None)
1003                 self.restoreRoot()
1004                 lastservice=eServiceReference(config.tv.lastservice.value)
1005                 if lastservice.valid() and self.getCurrentSelection() != lastservice:
1006                         self.setCurrentSelection(lastservice)
1007
1008 from Screens.InfoBarGenerics import InfoBarEvent, InfoBarServiceName, InfoBarInstantRecord
1009
1010 class RadioInfoBar(Screen, InfoBarEvent, InfoBarServiceName, InfoBarInstantRecord):
1011         def __init__(self, session):
1012                 Screen.__init__(self, session)
1013                 InfoBarEvent.__init__(self)
1014                 InfoBarServiceName.__init__(self)
1015                 InfoBarInstantRecord.__init__(self)
1016                 self["Clock"] = Clock()
1017
1018 class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelSelectionEPG):
1019         def __init__(self, session):
1020                 ChannelSelectionBase.__init__(self, session)
1021                 ChannelSelectionEdit.__init__(self)
1022                 ChannelSelectionEPG.__init__(self)
1023
1024                 config.radio = ConfigSubsection();
1025                 config.radio.lastservice = configElement("config.radio.lastservice", configText, "", 0);
1026                 config.radio.lastroot = configElement("config.radio.lastroot", configText, "", 0);
1027                 self.onLayoutFinish.append(self.onCreate)
1028
1029                 self.info = session.instantiateDialog(RadioInfoBar)
1030
1031                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1032                         {
1033                                 "keyTV": self.closeRadio,
1034                                 "keyRadio": self.closeRadio,
1035                                 "cancel": self.closeRadio,
1036                                 "ok": self.channelSelected,
1037                         })
1038
1039         def saveRoot(self):
1040                 path = ''
1041                 for i in self.servicePathRadio:
1042                         path += i.toString()
1043                         path += ';'
1044                 if len(path) and path != config.radio.lastroot.value:
1045                         config.radio.lastroot.value = path
1046                         config.radio.lastroot.save()
1047
1048         def restoreRoot(self):
1049                 self.clearPath()
1050                 re = compile('.+?;')
1051                 tmp = re.findall(config.radio.lastroot.value)
1052                 cnt = 0
1053                 for i in tmp:
1054                         self.servicePathRadio.append(eServiceReference(i[:len(i)-1]))
1055                         cnt += 1
1056                 if cnt:
1057                         path = self.servicePathRadio.pop()
1058                         self.enterPath(path)
1059                 else:
1060                         self.showFavourites()
1061                         self.saveRoot()
1062
1063         def preEnterPath(self, refstr):
1064                 if len(self.servicePathRadio) and self.servicePathRadio[0] != eServiceReference(refstr):
1065                         pathstr = config.radio.lastroot.value
1066                         if pathstr is not None and pathstr.find(refstr) == 0:
1067                                 self.restoreRoot()
1068                                 lastservice=eServiceReference(config.radio.lastservice.value)
1069                                 if lastservice.valid():
1070                                         self.setCurrentSelection(lastservice)
1071                                 return True
1072                 return False
1073
1074         def onCreate(self):
1075                 self.setRadioMode()
1076                 self.restoreRoot()
1077                 lastservice=eServiceReference(config.radio.lastservice.value)
1078                 if lastservice.valid():
1079                         self.servicelist.setCurrent(lastservice)
1080                         self.session.nav.playService(lastservice)
1081                         self.servicelist.setPlayableIgnoreService(lastservice)
1082                 self.info.show()
1083
1084         def channelSelected(self): # just return selected service
1085                 ref = self.getCurrentSelection()
1086                 if self.movemode:
1087                         self.toggleMoveMarked()
1088                 elif (ref.flags & 7) == 7:
1089                         self.enterPath(ref)
1090                 elif self.bouquet_mark_edit:
1091                         self.doMark()
1092                 else:
1093                         playingref = self.session.nav.getCurrentlyPlayingServiceReference()
1094                         if playingref is None or playingref != ref:
1095                                 self.session.nav.playService(ref)
1096                                 self.servicelist.setPlayableIgnoreService(ref)
1097                                 config.radio.lastservice.value = ref.toString()
1098                                 config.radio.lastservice.save()
1099                         self.saveRoot()
1100
1101         def closeRadio(self):
1102                 self.info.hide()
1103                 #set previous tv service
1104                 lastservice=eServiceReference(config.tv.lastservice.value)
1105                 self.session.nav.playService(lastservice)
1106                 self.close(None)
1107
1108 class SimpleChannelSelection(ChannelSelectionBase):
1109         def __init__(self, session, title):
1110                 ChannelSelectionBase.__init__(self, session)
1111                 self.title = title
1112                 self.onShown.append(self.__onExecCallback)
1113
1114                 self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"],
1115                         {
1116                                 "cancel": self.close,
1117                                 "ok": self.channelSelected,
1118                                 "keyRadio": self.setModeRadio,
1119                                 "keyTV": self.setModeTv,
1120                         })
1121
1122         def __onExecCallback(self):
1123                 self.setTitle(self.title)
1124                 self.setModeTv()
1125
1126         def channelSelected(self): # just return selected service
1127                 ref = self.getCurrentSelection()
1128                 if (ref.flags & 7) == 7:
1129                         self.enterPath(ref)
1130                 else:
1131                         ref = self.getCurrentSelection()
1132                         self.close(ref)
1133
1134         def setModeTv(self):
1135                 self.setTvMode()
1136                 self.showFavourites()
1137
1138         def setModeRadio(self):
1139                 self.setRadioMode()
1140                 self.showFavourites()