InfoBarGenerics.py: small cleanup
[vuplus_dvbapp] / lib / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector
2
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.Harddisk import harddiskmanager
6 from Components.Input import Input
7 from Components.Label import Label
8 from Components.PluginComponent import plugins
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.Sources.Boolean import Boolean
11 from Components.config import config, ConfigBoolean, ConfigClock
12 from Components.SystemInfo import SystemInfo
13 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
14 from EpgSelection import EPGSelection
15 from Plugins.Plugin import PluginDescriptor
16
17 from Screen import Screen
18 from Screens.ChoiceBox import ChoiceBox
19 from Screens.Dish import Dish
20 from Screens.EventView import EventViewEPGSelect, EventViewSimple
21 from Screens.InputBox import InputBox
22 from Screens.MessageBox import MessageBox
23 from Screens.MinuteInput import MinuteInput
24 from Screens.TimerSelection import TimerSelection
25 from Screens.PictureInPicture import PictureInPicture
26 from Screens.SubtitleDisplay import SubtitleDisplay
27 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
28 from Screens.TimeDateInput import TimeDateInput
29 from Screens.UnhandledKey import UnhandledKey
30 from ServiceReference import ServiceReference
31
32 from Tools import Notifications
33 from Tools.Directories import fileExists
34
35 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
36         iPlayableService, eServiceReference, eEPGCache, eActionMap
37
38 from time import time, localtime, strftime
39 from os import stat as os_stat
40 from bisect import insort
41
42 from RecordTimer import RecordTimerEntry, RecordTimer
43
44 # hack alert!
45 from Menu import MainMenu, mdom
46
47 class InfoBarDish:
48         def __init__(self):
49                 self.dishDialog = self.session.instantiateDialog(Dish)
50
51 class InfoBarUnhandledKey:
52         def __init__(self):
53                 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
54                 self.hideTimer = eTimer()
55                 self.hideTimer.callback.append(self.unhandledKeyDialog.hide)
56                 self.checkUnusedTimer = eTimer()
57                 self.checkUnusedTimer.callback.append(self.checkUnused)
58                 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
59                 eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
60                 eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
61                 self.key = -1;
62                 self.flags = 0;
63                 self.uflags = 0;
64
65         #this function is called on every keypress!
66         def actionA(self, key, flag):
67                 if flag != 4:
68                         if self.key != key:
69                                 self.key = key
70                                 self.flags = self.uflags = 0
71                         self.flags |= (1<<flag)
72                         if flag == 1: # break
73                                 self.checkUnusedTimer.start(0, True)
74                 return 0
75
76         #this function is only called when no other action has handled this key
77         def actionB(self, key, flag):
78                 if flag != 4:
79                         self.uflags |= (1<<flag)
80
81         def checkUnused(self):
82                 if self.flags == self.uflags:
83                         self.unhandledKeyDialog.show()
84                         self.hideTimer.start(2000, True)
85
86 class InfoBarShowHide:
87         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
88         fancy animations. """
89         STATE_HIDDEN = 0
90         STATE_HIDING = 1
91         STATE_SHOWING = 2
92         STATE_SHOWN = 3
93
94         def __init__(self):
95                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
96                         {
97                                 "toggleShow": self.toggleShow,
98                                 "hide": self.hide,
99                         }, 1) # lower prio to make it possible to override ok and cancel..
100
101                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
102                         {
103                                 iPlayableService.evStart: self.serviceStarted,
104                         })
105
106                 self.__state = self.STATE_SHOWN
107                 self.__locked = 0
108
109                 self.hideTimer = eTimer()
110                 self.hideTimer.callback.append(self.doTimerHide)
111                 self.hideTimer.start(5000, True)
112
113                 self.onShow.append(self.__onShow)
114                 self.onHide.append(self.__onHide)
115
116         def serviceStarted(self):
117                 if self.execing:
118                         if config.usage.show_infobar_on_zap.value:
119                                 self.doShow()
120
121         def __onShow(self):
122                 self.__state = self.STATE_SHOWN
123                 self.startHideTimer()
124
125         def startHideTimer(self):
126                 if self.__state == self.STATE_SHOWN and not self.__locked:
127                         idx = config.usage.infobar_timeout.index
128                         if idx:
129                                 self.hideTimer.start(idx*1000, True)
130
131         def __onHide(self):
132                 self.__state = self.STATE_HIDDEN
133
134         def doShow(self):
135                 self.show()
136                 self.startHideTimer()
137
138         def doTimerHide(self):
139                 self.hideTimer.stop()
140                 if self.__state == self.STATE_SHOWN:
141                         self.hide()
142
143         def toggleShow(self):
144                 if self.__state == self.STATE_SHOWN:
145                         self.hide()
146                         self.hideTimer.stop()
147                 elif self.__state == self.STATE_HIDDEN:
148                         self.show()
149
150         def lockShow(self):
151                 self.__locked = self.__locked + 1
152                 if self.execing:
153                         self.show()
154                         self.hideTimer.stop()
155
156         def unlockShow(self):
157                 self.__locked = self.__locked - 1
158                 if self.execing:
159                         self.startHideTimer()
160
161 #       def startShow(self):
162 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
163 #               self.__state = self.STATE_SHOWN
164 #
165 #       def startHide(self):
166 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
167 #               self.__state = self.STATE_HIDDEN
168
169 class NumberZap(Screen):
170         def quit(self):
171                 self.Timer.stop()
172                 self.close(0)
173
174         def keyOK(self):
175                 self.Timer.stop()
176                 self.close(int(self["number"].getText()))
177
178         def keyNumberGlobal(self, number):
179                 self.Timer.start(3000, True)            #reset timer
180                 self.field = self.field + str(number)
181                 self["number"].setText(self.field)
182                 if len(self.field) >= 4:
183                         self.keyOK()
184
185         def __init__(self, session, number):
186                 Screen.__init__(self, session)
187                 self.field = str(number)
188
189                 self["channel"] = Label(_("Channel:"))
190
191                 self["number"] = Label(self.field)
192
193                 self["actions"] = NumberActionMap( [ "SetupActions" ],
194                         {
195                                 "cancel": self.quit,
196                                 "ok": self.keyOK,
197                                 "1": self.keyNumberGlobal,
198                                 "2": self.keyNumberGlobal,
199                                 "3": self.keyNumberGlobal,
200                                 "4": self.keyNumberGlobal,
201                                 "5": self.keyNumberGlobal,
202                                 "6": self.keyNumberGlobal,
203                                 "7": self.keyNumberGlobal,
204                                 "8": self.keyNumberGlobal,
205                                 "9": self.keyNumberGlobal,
206                                 "0": self.keyNumberGlobal
207                         })
208
209                 self.Timer = eTimer()
210                 self.Timer.callback.append(self.keyOK)
211                 self.Timer.start(3000, True)
212
213 class InfoBarNumberZap:
214         """ Handles an initial number for NumberZapping """
215         def __init__(self):
216                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
217                         {
218                                 "1": self.keyNumberGlobal,
219                                 "2": self.keyNumberGlobal,
220                                 "3": self.keyNumberGlobal,
221                                 "4": self.keyNumberGlobal,
222                                 "5": self.keyNumberGlobal,
223                                 "6": self.keyNumberGlobal,
224                                 "7": self.keyNumberGlobal,
225                                 "8": self.keyNumberGlobal,
226                                 "9": self.keyNumberGlobal,
227                                 "0": self.keyNumberGlobal,
228                         })
229
230         def keyNumberGlobal(self, number):
231 #               print "You pressed number " + str(number)
232                 if number == 0:
233                         if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
234                                 self.pipDoHandle0Action()
235                         else:
236                                 self.servicelist.recallPrevService()
237                 else:
238                         if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
239                                 self.session.openWithCallback(self.numberEntered, NumberZap, number)
240
241         def numberEntered(self, retval):
242 #               print self.servicelist
243                 if retval > 0:
244                         self.zapToNumber(retval)
245
246         def searchNumberHelper(self, serviceHandler, num, bouquet):
247                 servicelist = serviceHandler.list(bouquet)
248                 if not servicelist is None:
249                         while num:
250                                 serviceIterator = servicelist.getNext()
251                                 if not serviceIterator.valid(): #check end of list
252                                         break
253                                 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
254                                 if playable:
255                                         num -= 1;
256                         if not num: #found service with searched number ?
257                                 return serviceIterator, 0
258                 return None, num
259
260         def zapToNumber(self, number):
261                 bouquet = self.servicelist.bouquet_root
262                 service = None
263                 serviceHandler = eServiceCenter.getInstance()
264                 if not config.usage.multibouquet.value:
265                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
266                 else:
267                         bouquetlist = serviceHandler.list(bouquet)
268                         if not bouquetlist is None:
269                                 while number:
270                                         bouquet = bouquetlist.getNext()
271                                         if not bouquet.valid(): #check end of list
272                                                 break
273                                         if bouquet.flags & eServiceReference.isDirectory:
274                                                 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
275                 if not service is None:
276                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
277                                 self.servicelist.clearPath()
278                                 if self.servicelist.bouquet_root != bouquet:
279                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
280                                 self.servicelist.enterPath(bouquet)
281                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
282                         self.servicelist.zap()
283
284 config.misc.initialchannelselection = ConfigBoolean(default = True)
285
286 class InfoBarChannelSelection:
287         """ ChannelSelection - handles the channelSelection dialog and the initial
288         channelChange actions which open the channelSelection dialog """
289         def __init__(self):
290                 #instantiate forever
291                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
292
293                 if config.misc.initialchannelselection.value:
294                         self.onShown.append(self.firstRun)
295
296                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
297                         {
298                                 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
299                                 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
300                                 "zapUp": (self.zapUp, _("previous channel")),
301                                 "zapDown": (self.zapDown, _("next channel")),
302                                 "historyBack": (self.historyBack, _("previous channel in history")),
303                                 "historyNext": (self.historyNext, _("next channel in history")),
304                                 "openServiceList": (self.openServiceList, _("open servicelist")),
305                         })
306
307         def showTvChannelList(self, zap=False):
308                 self.servicelist.setModeTv()
309                 if zap:
310                         self.servicelist.zap()
311                 self.session.execDialog(self.servicelist)
312
313         def showRadioChannelList(self, zap=False):
314                 self.servicelist.setModeRadio()
315                 if zap:
316                         self.servicelist.zap()
317                 self.session.execDialog(self.servicelist)
318
319         def firstRun(self):
320                 self.onShown.remove(self.firstRun)
321                 config.misc.initialchannelselection.value = False
322                 config.misc.initialchannelselection.save()
323                 self.switchChannelDown()
324
325         def historyBack(self):
326                 self.servicelist.historyBack()
327
328         def historyNext(self):
329                 self.servicelist.historyNext()
330
331         def switchChannelUp(self):
332                 self.servicelist.moveUp()
333                 self.session.execDialog(self.servicelist)
334
335         def switchChannelDown(self):
336                 self.servicelist.moveDown()
337                 self.session.execDialog(self.servicelist)
338
339         def openServiceList(self):
340                 self.session.execDialog(self.servicelist)
341
342         def zapUp(self):
343                 if self.servicelist.inBouquet():
344                         prev = self.servicelist.getCurrentSelection()
345                         if prev:
346                                 prev = prev.toString()
347                                 while True:
348                                         if config.usage.quickzap_bouquet_change.value:
349                                                 if self.servicelist.atBegin():
350                                                         self.servicelist.prevBouquet()
351                                         self.servicelist.moveUp()
352                                         cur = self.servicelist.getCurrentSelection()
353                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
354                                                 break
355                 else:
356                         self.servicelist.moveUp()
357                 self.servicelist.zap()
358
359         def zapDown(self):
360                 if self.servicelist.inBouquet():
361                         prev = self.servicelist.getCurrentSelection()
362                         if prev:
363                                 prev = prev.toString()
364                                 while True:
365                                         if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
366                                                 self.servicelist.nextBouquet()
367                                         else:
368                                                 self.servicelist.moveDown()
369                                         cur = self.servicelist.getCurrentSelection()
370                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
371                                                 break
372                 else:
373                         self.servicelist.moveDown()
374                 self.servicelist.zap()
375
376 class InfoBarMenu:
377         """ Handles a menu action, to open the (main) menu """
378         def __init__(self):
379                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
380                         {
381                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
382                         })
383                 self.session.infobar = None
384
385         def mainMenu(self):
386                 print "loading mainmenu XML..."
387                 menu = mdom.getroot()
388                 assert menu.tag == "menu", "root element in menu must be 'menu'!"
389
390                 self.session.infobar = self
391                 # so we can access the currently active infobar from screens opened from within the mainmenu
392                 # at the moment used from the SubserviceSelection
393
394                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
395
396         def mainMenuClosed(self, *val):
397                 self.session.infobar = None
398
399 class InfoBarSimpleEventView:
400         """ Opens the Eventview for now/next """
401         def __init__(self):
402                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
403                         {
404                                 "showEventInfo": (self.openEventView, _("show event details")),
405                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
406                         })
407
408         def showEventInfoWhenNotVisible(self):
409                 if self.shown:
410                         self.openEventView()
411                 else:
412                         self.toggleShow()
413                         return 1
414
415         def openEventView(self):
416                 epglist = [ ]
417                 self.epglist = epglist
418                 service = self.session.nav.getCurrentService()
419                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
420                 info = service.info()
421                 ptr=info.getEvent(0)
422                 if ptr:
423                         epglist.append(ptr)
424                 ptr=info.getEvent(1)
425                 if ptr:
426                         epglist.append(ptr)
427                 if epglist:
428                         self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
429
430         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
431                 epglist = self.epglist
432                 if len(epglist) > 1:
433                         tmp = epglist[0]
434                         epglist[0] = epglist[1]
435                         epglist[1] = tmp
436                         setEvent(epglist[0])
437
438 class SimpleServicelist:
439         def __init__(self, services):
440                 self.services = services
441                 self.length = len(services)
442                 self.current = 0
443
444         def selectService(self, service):
445                 if not self.length:
446                         self.current = -1
447                         return False
448                 else:
449                         self.current = 0
450                         while self.services[self.current].ref != service:
451                                 self.current += 1
452                                 if self.current >= self.length:
453                                         return False
454                 return True
455
456         def nextService(self):
457                 if not self.length:
458                         return
459                 if self.current+1 < self.length:
460                         self.current += 1
461                 else:
462                         self.current = 0
463
464         def prevService(self):
465                 if not self.length:
466                         return
467                 if self.current-1 > -1:
468                         self.current -= 1
469                 else:
470                         self.current = self.length - 1
471
472         def currentService(self):
473                 if not self.length or self.current >= self.length:
474                         return None
475                 return self.services[self.current]
476
477 class InfoBarEPG:
478         """ EPG - Opens an EPG list when the showEPGList action fires """
479         def __init__(self):
480                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
481                         {
482                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
483                         })
484
485                 self.is_now_next = False
486                 self.dlg_stack = [ ]
487                 self.bouquetSel = None
488                 self.eventView = None
489                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
490                         {
491                                 "showEventInfo": (self.openEventView, _("show EPG...")),
492                                 "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
493                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
494                         })
495
496         def showEventInfoWhenNotVisible(self):
497                 if self.shown:
498                         self.openEventView()
499                 else:
500                         self.toggleShow()
501                         return 1
502
503         def zapToService(self, service):
504                 if not service is None:
505                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
506                                 self.servicelist.clearPath()
507                                 if self.servicelist.bouquet_root != self.epg_bouquet:
508                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
509                                 self.servicelist.enterPath(self.epg_bouquet)
510                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
511                         self.servicelist.zap()
512
513         def getBouquetServices(self, bouquet):
514                 services = [ ]
515                 servicelist = eServiceCenter.getInstance().list(bouquet)
516                 if not servicelist is None:
517                         while True:
518                                 service = servicelist.getNext()
519                                 if not service.valid(): #check if end of list
520                                         break
521                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
522                                         continue
523                                 services.append(ServiceReference(service))
524                 return services
525
526         def openBouquetEPG(self, bouquet, withCallback=True):
527                 services = self.getBouquetServices(bouquet)
528                 if services:
529                         self.epg_bouquet = bouquet
530                         if withCallback:
531                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
532                         else:
533                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
534
535         def changeBouquetCB(self, direction, epg):
536                 if self.bouquetSel:
537                         if direction > 0:
538                                 self.bouquetSel.down()
539                         else:
540                                 self.bouquetSel.up()
541                         bouquet = self.bouquetSel.getCurrent()
542                         services = self.getBouquetServices(bouquet)
543                         if services:
544                                 self.epg_bouquet = bouquet
545                                 epg.setServices(services)
546
547         def closed(self, ret=False):
548                 closedScreen = self.dlg_stack.pop()
549                 if self.bouquetSel and closedScreen == self.bouquetSel:
550                         self.bouquetSel = None
551                 elif self.eventView and closedScreen == self.eventView:
552                         self.eventView = None
553                 if ret:
554                         dlgs=len(self.dlg_stack)
555                         if dlgs > 0:
556                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
557
558         def openMultiServiceEPG(self, withCallback=True):
559                 bouquets = self.servicelist.getBouquetList()
560                 if bouquets is None:
561                         cnt = 0
562                 else:
563                         cnt = len(bouquets)
564                 if cnt > 1: # show bouquet list
565                         if withCallback:
566                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
567                                 self.dlg_stack.append(self.bouquetSel)
568                         else:
569                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
570                 elif cnt == 1:
571                         self.openBouquetEPG(bouquets[0][1], withCallback)
572
573         def changeServiceCB(self, direction, epg):
574                 if self.serviceSel:
575                         if direction > 0:
576                                 self.serviceSel.nextService()
577                         else:
578                                 self.serviceSel.prevService()
579                         epg.setService(self.serviceSel.currentService())
580
581         def SingleServiceEPGClosed(self, ret=False):
582                 self.serviceSel = None
583
584         def openSingleServiceEPG(self):
585                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
586                 if ref:
587                         if self.servicelist.getMutableList() is not None: # bouquet in channellist
588                                 current_path = self.servicelist.getRoot()
589                                 services = self.getBouquetServices(current_path)
590                                 self.serviceSel = SimpleServicelist(services)
591                                 if self.serviceSel.selectService(ref):
592                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
593                                 else:
594                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
595                         else:
596                                 self.session.open(EPGSelection, ref)
597
598         def showEventInfoPlugins(self):
599                 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
600
601                 if list:
602                         list.append((_("show single service EPG..."), self.openSingleServiceEPG))
603                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
604                 else:
605                         self.openSingleServiceEPG()
606
607         def runPlugin(self, plugin):
608                 plugin(session = self.session, servicelist = self.servicelist)
609                 
610         def EventInfoPluginChosen(self, answer):
611                 if answer is not None:
612                         answer[1]()
613
614         def openSimilarList(self, eventid, refstr):
615                 self.session.open(EPGSelection, refstr, None, eventid)
616
617         def getNowNext(self):
618                 epglist = [ ]
619                 service = self.session.nav.getCurrentService()
620                 info = service and service.info()
621                 ptr = info and info.getEvent(0)
622                 if ptr:
623                         epglist.append(ptr)
624                 ptr = info and info.getEvent(1)
625                 if ptr:
626                         epglist.append(ptr)
627                 self.epglist = epglist
628
629         def __evEventInfoChanged(self):
630                 if self.is_now_next and len(self.dlg_stack) == 1:
631                         self.getNowNext()
632                         assert self.eventView
633                         if self.epglist:
634                                 self.eventView.setEvent(self.epglist[0])
635
636         def openEventView(self):
637                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
638                 self.getNowNext()
639                 epglist = self.epglist
640                 if not epglist:
641                         self.is_now_next = False
642                         epg = eEPGCache.getInstance()
643                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
644                         if ptr:
645                                 epglist.append(ptr)
646                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
647                                 if ptr:
648                                         epglist.append(ptr)
649                 else:
650                         self.is_now_next = True
651                 if epglist:
652                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
653                         self.dlg_stack.append(self.eventView)
654                 else:
655                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
656                         self.openMultiServiceEPG(False)
657
658         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
659                 epglist = self.epglist
660                 if len(epglist) > 1:
661                         tmp = epglist[0]
662                         epglist[0]=epglist[1]
663                         epglist[1]=tmp
664                         setEvent(epglist[0])
665
666 class InfoBarRdsDecoder:
667         """provides RDS and Rass support/display"""
668         def __init__(self):
669                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
670                 self.rass_interactive = None
671
672                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
673                         {
674                                 iPlayableService.evEnd: self.__serviceStopped,
675                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
676                         })
677
678                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
679                 {
680                         "startRassInteractive": self.startRassInteractive
681                 },-1)
682
683                 self["RdsActions"].setEnabled(False)
684
685                 self.onLayoutFinish.append(self.rds_display.show)
686                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
687
688         def RassInteractivePossibilityChanged(self, state):
689                 self["RdsActions"].setEnabled(state)
690
691         def RassSlidePicChanged(self):
692                 if not self.rass_interactive:
693                         service = self.session.nav.getCurrentService()
694                         decoder = service and service.rdsDecoder()
695                         if decoder:
696                                 decoder.showRassSlidePicture()
697
698         def __serviceStopped(self):
699                 if self.rass_interactive is not None:
700                         rass_interactive = self.rass_interactive
701                         self.rass_interactive = None
702                         rass_interactive.close()
703
704         def startRassInteractive(self):
705                 self.rds_display.hide()
706                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
707
708         def RassInteractiveClosed(self, *val):
709                 if self.rass_interactive is not None:
710                         self.rass_interactive = None
711                         self.RassSlidePicChanged()
712                 self.rds_display.show()
713
714 class InfoBarSeek:
715         """handles actions like seeking, pause"""
716
717         SEEK_STATE_PLAY = (0, 0, 0, ">")
718         SEEK_STATE_PAUSE = (1, 0, 0, "||")
719         SEEK_STATE_EOF = (1, 0, 0, "END")
720
721         def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
722                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
723                         {
724                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
725                                 iPlayableService.evStart: self.__serviceStarted,
726
727                                 iPlayableService.evEOF: self.__evEOF,
728                                 iPlayableService.evSOF: self.__evSOF,
729                         })
730
731                 class InfoBarSeekActionMap(HelpableActionMap):
732                         def __init__(self, screen, *args, **kwargs):
733                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
734                                 self.screen = screen
735
736                         def action(self, contexts, action):
737                                 print "action:", action
738                                 if action[:5] == "seek:":
739                                         time = int(action[5:])
740                                         self.screen.doSeekRelative(time * 90000)
741                                         return 1
742                                 elif action[:8] == "seekdef:":
743                                         key = int(action[8:])
744                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
745                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
746                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
747                                         self.screen.doSeekRelative(time * 90000)
748                                         return 1                                        
749                                 else:
750                                         return HelpableActionMap.action(self, contexts, action)
751
752                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
753                         {
754                                 "playpauseService": self.playpauseService,
755                                 "pauseService": (self.pauseService, _("pause")),
756                                 "unPauseService": (self.unPauseService, _("continue")),
757
758                                 "seekFwd": (self.seekFwd, _("skip forward")),
759                                 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
760                                 "seekBack": (self.seekBack, _("skip backward")),
761                                 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
762                         }, prio=-1)
763                         # give them a little more priority to win over color buttons
764
765                 self["SeekActions"].setEnabled(False)
766
767                 self.seekstate = self.SEEK_STATE_PLAY
768                 self.lastseekstate = self.SEEK_STATE_PLAY
769
770                 self.onPlayStateChanged = [ ]
771
772                 self.lockedBecauseOfSkipping = False
773
774                 self.__seekableStatusChanged()
775
776         def makeStateForward(self, n):
777 #               minspeed = config.seek.stepwise_minspeed.value
778 #               repeat = int(config.seek.stepwise_repeat.value)
779 #               if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
780 #                       return (0, n * repeat, repeat, ">> %dx" % n)
781 #               else:
782                         return (0, n, 0, ">> %dx" % n)
783
784         def makeStateBackward(self, n):
785 #               minspeed = config.seek.stepwise_minspeed.value
786 #               repeat = int(config.seek.stepwise_repeat.value)
787 #               if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
788 #                       return (0, -n * repeat, repeat, "<< %dx" % n)
789 #               else:
790                         return (0, -n, 0, "<< %dx" % n)
791
792         def makeStateSlowMotion(self, n):
793                 return (0, 0, n, "/%d" % n)
794
795         def isStateForward(self, state):
796                 return state[1] > 1
797
798         def isStateBackward(self, state):
799                 return state[1] < 0
800
801         def isStateSlowMotion(self, state):
802                 return state[1] == 0 and state[2] > 1
803
804         def getHigher(self, n, lst):
805                 for x in lst:
806                         if x > n:
807                                 return x
808                 return False
809
810         def getLower(self, n, lst):
811                 lst = lst[:]
812                 lst.reverse()
813                 for x in lst:
814                         if x < n:
815                                 return x
816                 return False
817
818         def showAfterSeek(self):
819                 if isinstance(self, InfoBarShowHide):
820                         self.doShow()
821
822         def up(self):
823                 pass
824
825         def down(self):
826                 pass
827
828         def getSeek(self):
829                 service = self.session.nav.getCurrentService()
830                 if service is None:
831                         return None
832
833                 seek = service.seek()
834
835                 if seek is None or not seek.isCurrentlySeekable():
836                         return None
837
838                 return seek
839
840         def isSeekable(self):
841                 if self.getSeek() is None:
842                         return False
843                 return True
844
845         def __seekableStatusChanged(self):
846 #               print "seekable status changed!"
847                 if not self.isSeekable():
848                         self["SeekActions"].setEnabled(False)
849 #                       print "not seekable, return to play"
850                         self.setSeekState(self.SEEK_STATE_PLAY)
851                 else:
852                         self["SeekActions"].setEnabled(True)
853 #                       print "seekable"
854
855         def __serviceStarted(self):
856                 self.seekstate = self.SEEK_STATE_PLAY
857                 self.__seekableStatusChanged()
858
859         def setSeekState(self, state):
860                 service = self.session.nav.getCurrentService()
861
862                 if service is None:
863                         return False
864
865                 if not self.isSeekable():
866                         if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
867                                 state = self.SEEK_STATE_PLAY
868
869                 pauseable = service.pause()
870
871                 if pauseable is None:
872                         print "not pauseable."
873                         state = self.SEEK_STATE_PLAY
874
875                 self.seekstate = state
876
877                 if pauseable is not None:
878                         if self.seekstate[0]:
879                                 print "resolved to PAUSE"
880                                 pauseable.pause()
881                         elif self.seekstate[1]:
882                                 print "resolved to FAST FORWARD"
883                                 pauseable.setFastForward(self.seekstate[1])
884                         elif self.seekstate[2]:
885                                 print "resolved to SLOW MOTION"
886                                 pauseable.setSlowMotion(self.seekstate[2])
887                         else:
888                                 print "resolved to PLAY"
889                                 pauseable.unpause()
890
891                 for c in self.onPlayStateChanged:
892                         c(self.seekstate)
893
894                 self.checkSkipShowHideLock()
895
896                 return True
897
898         def playpauseService(self):
899                 if self.seekstate != self.SEEK_STATE_PLAY:
900                         self.unPauseService()
901                 else:
902                         self.pauseService()
903
904         def pauseService(self):
905                 if self.seekstate == self.SEEK_STATE_PAUSE:
906                         if config.seek.on_pause.value == "play":
907                                 self.unPauseService()
908                         elif config.seek.on_pause.value == "step":
909                                 self.doSeekRelative(1)
910                         elif config.seek.on_pause.value == "last":
911                                 self.setSeekState(self.lastseekstate)
912                                 self.lastseekstate = self.SEEK_STATE_PLAY
913                 else:
914                         if self.seekstate != self.SEEK_STATE_EOF:
915                                 self.lastseekstate = self.seekstate
916                         self.setSeekState(self.SEEK_STATE_PAUSE);
917
918         def unPauseService(self):
919                 print "unpause"
920                 if self.seekstate == self.SEEK_STATE_PLAY:
921                         return 0
922                 self.setSeekState(self.SEEK_STATE_PLAY)
923
924         def doSeek(self, pts):
925                 seekable = self.getSeek()
926                 if seekable is None:
927                         return
928                 seekable.seekTo(pts)
929
930         def doSeekRelative(self, pts):
931                 seekable = self.getSeek()
932                 if seekable is None:
933                         return
934                 prevstate = self.seekstate
935
936                 if self.seekstate == self.SEEK_STATE_EOF:
937                         if prevstate == self.SEEK_STATE_PAUSE:
938                                 self.setSeekState(self.SEEK_STATE_PAUSE)
939                         else:
940                                 self.setSeekState(self.SEEK_STATE_PLAY)
941                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
942                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
943                         self.showAfterSeek()
944
945         def seekFwd(self):
946                 if self.seekstate == self.SEEK_STATE_PLAY:
947                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
948                 elif self.seekstate == self.SEEK_STATE_PAUSE:
949                         if len(config.seek.speeds_slowmotion.value):
950                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
951                         else:
952                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
953                 elif self.seekstate == self.SEEK_STATE_EOF:
954                         pass
955                 elif self.isStateForward(self.seekstate):
956                         speed = self.seekstate[1]
957                         if self.seekstate[2]:
958                                 speed /= self.seekstate[2]
959                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
960                         self.setSeekState(self.makeStateForward(speed))
961                 elif self.isStateBackward(self.seekstate):
962                         speed = -self.seekstate[1]
963                         if self.seekstate[2]:
964                                 speed /= self.seekstate[2]
965                         speed = self.getLower(speed, config.seek.speeds_backward.value)
966                         if speed:
967                                 self.setSeekState(self.makeStateBackward(speed))
968                         else:
969                                 self.setSeekState(self.SEEK_STATE_PLAY)
970                 elif self.isStateSlowMotion(self.seekstate):
971                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
972                         self.setSeekState(self.makeStateSlowMotion(speed))
973
974         def seekBack(self):
975                 seekstate = self.seekstate
976                 if seekstate == self.SEEK_STATE_PLAY:
977                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
978                 elif seekstate == self.SEEK_STATE_EOF:
979                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
980                         self.doSeekRelative(-6)
981                 elif seekstate == self.SEEK_STATE_PAUSE:
982                         self.doSeekRelative(-1)
983                 elif self.isStateForward(seekstate):
984                         speed = seekstate[1]
985                         if seekstate[2]:
986                                 speed /= seekstate[2]
987                         speed = self.getLower(speed, config.seek.speeds_forward.value)
988                         if speed:
989                                 self.setSeekState(self.makeStateForward(speed))
990                         else:
991                                 self.setSeekState(self.SEEK_STATE_PLAY)
992                 elif self.isStateBackward(seekstate):
993                         speed = -seekstate[1]
994                         if seekstate[2]:
995                                 speed /= seekstate[2]
996                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
997                         self.setSeekState(self.makeStateBackward(speed))
998                 elif self.isStateSlowMotion(seekstate):
999                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1000                         if speed:
1001                                 self.setSeekState(self.makeStateSlowMotion(speed))
1002                         else:
1003                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1004
1005         def seekFwdManual(self):
1006                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1007
1008         def fwdSeekTo(self, minutes):
1009                 print "Seek", minutes, "minutes forward"
1010                 self.doSeekRelative(minutes * 60 * 90000)
1011
1012         def seekBackManual(self):
1013                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1014
1015         def rwdSeekTo(self, minutes):
1016                 print "rwdSeekTo"
1017                 self.doSeekRelative(-minutes * 60 * 90000)
1018
1019         def checkSkipShowHideLock(self):
1020                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1021
1022                 if config.usage.show_infobar_on_skip.value:
1023                         if self.lockedBecauseOfSkipping and not wantlock:
1024                                 self.unlockShow()
1025                                 self.lockedBecauseOfSkipping = False
1026
1027                         if wantlock and not self.lockedBecauseOfSkipping:
1028                                 self.lockShow()
1029                                 self.lockedBecauseOfSkipping = True
1030
1031         def calcRemainingTime(self):
1032                 seekable = self.getSeek()
1033                 if seekable is not None:
1034                         len = seekable.getLength()
1035                         try:
1036                                 tmp = self.cueGetEndCutPosition()
1037                                 if tmp:
1038                                         len = [False, tmp]
1039                         except:
1040                                 pass
1041                         pos = seekable.getPlayPosition()
1042                         speednom = self.seekstate[1] or 1
1043                         speedden = self.seekstate[2] or 1
1044                         if not len[0] and not pos[0]:
1045                                 if len[1] <= pos[1]:
1046                                         return 0
1047                                 time = (len[1] - pos[1])*speedden/(90*speednom)
1048                                 return time
1049                 return False
1050                 
1051         def __evEOF(self):
1052                 if self.seekstate == self.SEEK_STATE_EOF:
1053                         return
1054
1055                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1056                 seekstate = self.seekstate
1057                 if self.seekstate != self.SEEK_STATE_PAUSE:
1058                         self.setSeekState(self.SEEK_STATE_EOF)
1059
1060                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1061                         seekable = self.getSeek()
1062                         if seekable is not None:
1063                                 seekable.seekTo(-1)
1064                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1065                         self.doEofInternal(True)
1066                 else:
1067                         self.doEofInternal(False)
1068
1069         def doEofInternal(self, playing):
1070                 pass            # Defined in subclasses
1071
1072         def __evSOF(self):
1073                 self.setSeekState(self.SEEK_STATE_PLAY)
1074                 self.doSeek(0)
1075
1076 from Screens.PVRState import PVRState, TimeshiftState
1077
1078 class InfoBarPVRState:
1079         def __init__(self, screen=PVRState, force_show = False):
1080                 self.onPlayStateChanged.append(self.__playStateChanged)
1081                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1082                 self.onShow.append(self._mayShow)
1083                 self.onHide.append(self.pvrStateDialog.hide)
1084                 self.force_show = force_show
1085
1086         def _mayShow(self):
1087                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1088                         self.pvrStateDialog.show()
1089
1090         def __playStateChanged(self, state):
1091                 playstateString = state[3]
1092                 self.pvrStateDialog["state"].setText(playstateString)
1093                 
1094                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1095                 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1096                         self.pvrStateDialog.hide()
1097                 else:
1098                         self._mayShow()
1099                         
1100
1101 class InfoBarTimeshiftState(InfoBarPVRState):
1102         def __init__(self):
1103                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1104
1105         def _mayShow(self):
1106                 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1107                         self.pvrStateDialog.show()
1108
1109 class InfoBarShowMovies:
1110
1111         # i don't really like this class.
1112         # it calls a not further specified "movie list" on up/down/movieList,
1113         # so this is not more than an action map
1114         def __init__(self):
1115                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1116                         {
1117                                 "movieList": (self.showMovies, _("movie list")),
1118                                 "up": (self.showMovies, _("movie list")),
1119                                 "down": (self.showMovies, _("movie list"))
1120                         })
1121
1122 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1123
1124 # Hrmf.
1125 #
1126 # Timeshift works the following way:
1127 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1128 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1129 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1130 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1131 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1132 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1133 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1134 #
1135
1136 # in other words:
1137 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1138 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1139 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1140 # - the user can now PVR around
1141 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1142 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1143 # after!
1144 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1145 # - if the user rewinds, or press pause, timeshift will be activated again
1146
1147 # note that a timeshift can be enabled ("recording") and
1148 # activated (currently time-shifting).
1149
1150 class InfoBarTimeshift:
1151         def __init__(self):
1152                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1153                         {
1154                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1155                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1156                         }, prio=1)
1157                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1158                         {
1159                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1160                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1161                         }, prio=-1) # priority over record
1162
1163                 self.timeshift_enabled = 0
1164                 self.timeshift_state = 0
1165                 self.ts_rewind_timer = eTimer()
1166                 self.ts_rewind_timer.callback.append(self.rewindService)
1167
1168                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1169                         {
1170                                 iPlayableService.evStart: self.__serviceStarted,
1171                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1172                         })
1173
1174         def getTimeshift(self):
1175                 service = self.session.nav.getCurrentService()
1176                 return service and service.timeshift()
1177
1178         def startTimeshift(self):
1179                 print "enable timeshift"
1180                 ts = self.getTimeshift()
1181                 if ts is None:
1182                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1183                         print "no ts interface"
1184                         return 0
1185
1186                 if self.timeshift_enabled:
1187                         print "hu, timeshift already enabled?"
1188                 else:
1189                         if not ts.startTimeshift():
1190                                 self.timeshift_enabled = 1
1191
1192                                 # we remove the "relative time" for now.
1193                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1194
1195                                 # PAUSE.
1196                                 #self.setSeekState(self.SEEK_STATE_PAUSE)
1197                                 self.activateTimeshiftEnd(False)
1198
1199                                 # enable the "TimeshiftEnableActions", which will override
1200                                 # the startTimeshift actions
1201                                 self.__seekableStatusChanged()
1202                         else:
1203                                 print "timeshift failed"
1204
1205         def stopTimeshift(self):
1206                 if not self.timeshift_enabled:
1207                         return 0
1208                 print "disable timeshift"
1209                 ts = self.getTimeshift()
1210                 if ts is None:
1211                         return 0
1212                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1213
1214         def stopTimeshiftConfirmed(self, confirmed):
1215                 if not confirmed:
1216                         return
1217
1218                 ts = self.getTimeshift()
1219                 if ts is None:
1220                         return
1221
1222                 ts.stopTimeshift()
1223                 self.timeshift_enabled = 0
1224
1225                 # disable actions
1226                 self.__seekableStatusChanged()
1227
1228         # activates timeshift, and seeks to (almost) the end
1229         def activateTimeshiftEnd(self, back = True):
1230                 ts = self.getTimeshift()
1231                 print "activateTimeshiftEnd"
1232
1233                 if ts is None:
1234                         return
1235
1236                 if ts.isTimeshiftActive():
1237                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1238                         self.pauseService()
1239                 else:
1240                         print "play, ..."
1241                         ts.activateTimeshift() # activate timeshift will automatically pause
1242                         self.setSeekState(self.SEEK_STATE_PAUSE)
1243
1244                 if back:
1245                         self.ts_rewind_timer.start(200, 1)
1246
1247         def rewindService(self):
1248                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1249
1250         # same as activateTimeshiftEnd, but pauses afterwards.
1251         def activateTimeshiftEndAndPause(self):
1252                 print "activateTimeshiftEndAndPause"
1253                 #state = self.seekstate
1254                 self.activateTimeshiftEnd(False)
1255
1256         def __seekableStatusChanged(self):
1257                 enabled = False
1258
1259 #               print "self.isSeekable", self.isSeekable()
1260 #               print "self.timeshift_enabled", self.timeshift_enabled
1261
1262                 # when this service is not seekable, but timeshift
1263                 # is enabled, this means we can activate
1264                 # the timeshift
1265                 if not self.isSeekable() and self.timeshift_enabled:
1266                         enabled = True
1267
1268 #               print "timeshift activate:", enabled
1269                 self["TimeshiftActivateActions"].setEnabled(enabled)
1270
1271         def __serviceStarted(self):
1272                 self.timeshift_enabled = False
1273                 self.__seekableStatusChanged()
1274
1275 from Screens.PiPSetup import PiPSetup
1276
1277 class InfoBarExtensions:
1278         EXTENSION_SINGLE = 0
1279         EXTENSION_LIST = 1
1280
1281         def __init__(self):
1282                 self.list = []
1283
1284                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1285                         {
1286                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1287                         }, 1) # lower priority
1288
1289         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1290                 self.list.append((type, extension, key))
1291
1292         def updateExtension(self, extension, key = None):
1293                 self.extensionsList.append(extension)
1294                 if key is not None:
1295                         if self.extensionKeys.has_key(key):
1296                                 key = None
1297
1298                 if key is None:
1299                         for x in self.availableKeys:
1300                                 if not self.extensionKeys.has_key(x):
1301                                         key = x
1302                                         break
1303
1304                 if key is not None:
1305                         self.extensionKeys[key] = len(self.extensionsList) - 1
1306
1307         def updateExtensions(self):
1308                 self.extensionsList = []
1309                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1310                 self.extensionKeys = {}
1311                 for x in self.list:
1312                         if x[0] == self.EXTENSION_SINGLE:
1313                                 self.updateExtension(x[1], x[2])
1314                         else:
1315                                 for y in x[1]():
1316                                         self.updateExtension(y[0], y[1])
1317
1318
1319         def showExtensionSelection(self):
1320                 self.updateExtensions()
1321                 extensionsList = self.extensionsList[:]
1322                 keys = []
1323                 list = []
1324                 for x in self.availableKeys:
1325                         if self.extensionKeys.has_key(x):
1326                                 entry = self.extensionKeys[x]
1327                                 extension = self.extensionsList[entry]
1328                                 if extension[2]():
1329                                         name = str(extension[0]())
1330                                         list.append((extension[0](), extension))
1331                                         keys.append(x)
1332                                         extensionsList.remove(extension)
1333                                 else:
1334                                         extensionsList.remove(extension)
1335                 list.extend([(x[0](), x) for x in extensionsList])
1336
1337                 keys += [""] * len(extensionsList)
1338                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1339
1340         def extensionCallback(self, answer):
1341                 if answer is not None:
1342                         answer[1][1]()
1343
1344 from Tools.BoundFunction import boundFunction
1345
1346 # depends on InfoBarExtensions
1347
1348 class InfoBarPlugins:
1349         def __init__(self):
1350                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1351
1352         def getPluginName(self, name):
1353                 return name
1354
1355         def getPluginList(self):
1356                 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1357                 list.sort(key = lambda e: e[2]) # sort by name
1358                 return list
1359
1360         def runPlugin(self, plugin):
1361                 if isinstance(self, InfoBarChannelSelection):
1362                         plugin(session = self.session, servicelist = self.servicelist)
1363                 else:
1364                         plugin(session = self.session)
1365
1366 from Components.Task import job_manager
1367 class InfoBarJobman:
1368         def __init__(self):
1369                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1370
1371         def getJobList(self):
1372                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1373
1374         def getJobName(self, job):
1375                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1376
1377         def showJobView(self, job):
1378                 from Screens.TaskView import JobView
1379                 job_manager.in_background = False
1380                 self.session.openWithCallback(self.JobViewCB, JobView, job)
1381         
1382         def JobViewCB(self, in_background):
1383                 job_manager.in_background = in_background
1384
1385 # depends on InfoBarExtensions
1386 class InfoBarPiP:
1387         def __init__(self):
1388                 try:
1389                         self.session.pipshown
1390                 except:
1391                         self.session.pipshown = False
1392                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1393                         if (self.allowPiP):
1394                                 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1395                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1396                                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1397                         else:
1398                                 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1399                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1400
1401         def pipShown(self):
1402                 return self.session.pipshown
1403
1404         def pipHandles0Action(self):
1405                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1406
1407         def getShowHideName(self):
1408                 if self.session.pipshown:
1409                         return _("Disable Picture in Picture")
1410                 else:
1411                         return _("Activate Picture in Picture")
1412
1413         def getSwapName(self):
1414                 return _("Swap Services")
1415
1416         def getMoveName(self):
1417                 return _("Move Picture in Picture")
1418
1419         def showPiP(self):
1420                 if self.session.pipshown:
1421                         del self.session.pip
1422                         self.session.pipshown = False
1423                 else:
1424                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1425                         self.session.pip.show()
1426                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1427                         if self.session.pip.playService(newservice):
1428                                 self.session.pipshown = True
1429                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1430                         else:
1431                                 self.session.pipshown = False
1432                                 del self.session.pip
1433                         self.session.nav.playService(newservice)
1434
1435         def swapPiP(self):
1436                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1437                 if self.session.pip.servicePath:
1438                         servicepath = self.servicelist.getCurrentServicePath()
1439                         ref=servicepath[len(servicepath)-1]
1440                         pipref=self.session.pip.getCurrentService()
1441                         self.session.pip.playService(swapservice)
1442                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1443                         if pipref.toString() != ref.toString(): # is a subservice ?
1444                                 self.session.nav.stopService() # stop portal
1445                                 self.session.nav.playService(pipref) # start subservice
1446                         self.session.pip.servicePath=servicepath
1447
1448         def movePiP(self):
1449                 self.session.open(PiPSetup, pip = self.session.pip)
1450
1451         def pipDoHandle0Action(self):
1452                 use = config.usage.pip_zero_button.value
1453                 if "swap" == use:
1454                         self.swapPiP()
1455                 elif "swapstop" == use:
1456                         self.swapPiP()
1457                         self.showPiP()
1458                 elif "stop" == use:
1459                         self.showPiP()
1460
1461 from RecordTimer import parseEvent, RecordTimerEntry
1462
1463 class InfoBarInstantRecord:
1464         """Instant Record - handles the instantRecord action in order to
1465         start/stop instant records"""
1466         def __init__(self):
1467                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1468                         {
1469                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1470                         })
1471                 self.recording = []
1472
1473         def stopCurrentRecording(self, entry = -1):
1474                 if entry is not None and entry != -1:
1475                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1476                         self.recording.remove(self.recording[entry])
1477
1478         def startInstantRecording(self, limitEvent = False):
1479                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1480
1481                 # try to get event info
1482                 event = None
1483                 try:
1484                         service = self.session.nav.getCurrentService()
1485                         epg = eEPGCache.getInstance()
1486                         event = epg.lookupEventTime(serviceref, -1, 0)
1487                         if event is None:
1488                                 info = service.info()
1489                                 ev = info.getEvent(0)
1490                                 event = ev
1491                 except:
1492                         pass
1493
1494                 begin = int(time())
1495                 end = begin + 3600      # dummy
1496                 name = "instant record"
1497                 description = ""
1498                 eventid = None
1499
1500                 if event is not None:
1501                         curEvent = parseEvent(event)
1502                         name = curEvent[2]
1503                         description = curEvent[3]
1504                         eventid = curEvent[4]
1505                         if limitEvent:
1506                                 end = curEvent[1]
1507                 else:
1508                         if limitEvent:
1509                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1510
1511                 if isinstance(serviceref, eServiceReference):
1512                         serviceref = ServiceReference(serviceref)
1513
1514                 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1515                 recording.dontSave = True
1516                 
1517                 if event is None or limitEvent == False:
1518                         recording.autoincrease = True
1519                         if recording.setAutoincreaseEnd():
1520                                 self.session.nav.RecordTimer.record(recording)
1521                                 self.recording.append(recording)
1522                 else:
1523                                 simulTimerList = self.session.nav.RecordTimer.record(recording)
1524                                 if simulTimerList is not None:  # conflict with other recording
1525                                         name = simulTimerList[1].name
1526                                         name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1527                                         print "[TIMER] conflicts with", name_date
1528                                         recording.autoincrease = True   # start with max available length, then increment
1529                                         if recording.setAutoincreaseEnd():
1530                                                 self.session.nav.RecordTimer.record(recording)
1531                                                 self.recording.append(recording)
1532                                                 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1533                                         else:
1534                                                 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1535                                         recording.autoincrease = False
1536                                 else:
1537                                         self.recording.append(recording)
1538
1539         def isInstantRecordRunning(self):
1540                 print "self.recording:", self.recording
1541                 if self.recording:
1542                         for x in self.recording:
1543                                 if x.isRunning():
1544                                         return True
1545                 return False
1546
1547         def recordQuestionCallback(self, answer):
1548                 print "pre:\n", self.recording
1549
1550                 if answer is None or answer[1] == "no":
1551                         return
1552                 list = []
1553                 recording = self.recording[:]
1554                 for x in recording:
1555                         if not x in self.session.nav.RecordTimer.timer_list:
1556                                 self.recording.remove(x)
1557                         elif x.dontSave and x.isRunning():
1558                                 list.append((x, False))
1559
1560                 if answer[1] == "changeduration":
1561                         if len(self.recording) == 1:
1562                                 self.changeDuration(0)
1563                         else:
1564                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1565                 elif answer[1] == "changeendtime":
1566                         if len(self.recording) == 1:
1567                                 self.setEndtime(0)
1568                         else:
1569                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1570                 elif answer[1] == "stop":
1571                         if len(self.recording) == 1:
1572                                 self.stopCurrentRecording(0)
1573                         else:
1574                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1575                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1576                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1577                         if answer[1] == "manualduration":
1578                                 self.changeDuration(len(self.recording)-1)
1579                         elif answer[1] == "manualendtime":
1580                                 self.setEndtime(len(self.recording)-1)
1581                 print "after:\n", self.recording
1582
1583         def setEndtime(self, entry):
1584                 if entry is not None and entry >= 0:
1585                         self.selectedEntry = entry
1586                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1587                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1588                         dlg.setTitle(_("Please change recording endtime"))
1589
1590         def TimeDateInputClosed(self, ret):
1591                 if len(ret) > 1:
1592                         if ret[0]:
1593                                 localendtime = localtime(ret[1])
1594                                 print "stopping recording at", strftime("%c", localendtime)
1595                                 if self.recording[self.selectedEntry].end != ret[1]:
1596                                         self.recording[self.selectedEntry].autoincrease = False
1597                                 self.recording[self.selectedEntry].end = ret[1]
1598                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1599
1600         def changeDuration(self, entry):
1601                 if entry is not None and entry >= 0:
1602                         self.selectedEntry = entry
1603                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1604
1605         def inputCallback(self, value):
1606                 if value is not None:
1607                         print "stopping recording after", int(value), "minutes."
1608                         entry = self.recording[self.selectedEntry]
1609                         if int(value) != 0:
1610                                 entry.autoincrease = False
1611                         entry.end = int(time()) + 60 * int(value)
1612                         self.session.nav.RecordTimer.timeChanged(entry)
1613
1614         def instantRecord(self):
1615                 dir = preferredInstantRecordPath()
1616                 if not dir or not fileExists(dir, 'w'):
1617                         dir = defaultMoviePath()
1618                 try:
1619                         stat = os_stat(dir)
1620                 except:
1621                         # XXX: this message is a little odd as we might be recording to a remote device
1622                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1623                         return
1624
1625                 if self.isInstantRecordRunning():
1626                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1627                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1628                                 list=((_("stop recording"), "stop"), \
1629                                 (_("add recording (stop after current event)"), "event"), \
1630                                 (_("add recording (indefinitely)"), "indefinitely"), \
1631                                 (_("add recording (enter recording duration)"), "manualduration"), \
1632                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1633                                 (_("change recording (duration)"), "changeduration"), \
1634                                 (_("change recording (endtime)"), "changeendtime"), \
1635                                 (_("do nothing"), "no")))
1636                 else:
1637                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1638                                 title=_("Start recording?"), \
1639                                 list=((_("add recording (stop after current event)"), "event"), \
1640                                 (_("add recording (indefinitely)"), "indefinitely"), \
1641                                 (_("add recording (enter recording duration)"), "manualduration"), \
1642                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1643                                 (_("don't record"), "no")))
1644
1645 from Tools.ISO639 import LanguageCodes
1646
1647 class InfoBarAudioSelection:
1648         def __init__(self):
1649                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1650                         {
1651                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1652                         })
1653
1654         def audioSelection(self):
1655                 service = self.session.nav.getCurrentService()
1656                 self.audioTracks = audio = service and service.audioTracks()
1657                 n = audio and audio.getNumberOfTracks() or 0
1658                 tlist = []
1659                 if n > 0:
1660                         self.audioChannel = service.audioChannel()
1661
1662                         idx = 0
1663                         while idx < n:
1664                                 cnt = 0
1665                                 i = audio.getTrackInfo(idx)
1666                                 languages = i.getLanguage().split('/')
1667                                 description = i.getDescription()
1668                                 language = ""
1669
1670                                 for lang in languages:
1671                                         if cnt:
1672                                                 language += ' / '
1673                                         if LanguageCodes.has_key(lang):
1674                                                 language += LanguageCodes[lang][0]
1675                                         else:
1676                                                 language += lang
1677                                         cnt += 1
1678
1679                                 if len(description):
1680                                         description += " (" + language + ")"
1681                                 else:
1682                                         description = language
1683
1684                                 tlist.append((description, idx))
1685                                 idx += 1
1686
1687                         tlist.sort(key=lambda x: x[0])
1688
1689                         selectedAudio = self.audioTracks.getCurrentTrack()
1690
1691                         selection = 0
1692
1693                         for x in tlist:
1694                                 if x[1] != selectedAudio:
1695                                         selection += 1
1696                                 else:
1697                                         break
1698
1699                         availableKeys = []
1700                         usedKeys = []
1701
1702                         if SystemInfo["CanDownmixAC3"]:
1703                                 flist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1704                                         ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
1705                                 usedKeys.extend(["red", "green"])
1706                                 availableKeys.extend(["yellow", "blue"])
1707                                 selection += 2
1708                         else:
1709                                 flist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
1710                                 usedKeys.extend(["red"])
1711                                 availableKeys.extend(["green", "yellow", "blue"])
1712                                 selection += 1
1713
1714                         if hasattr(self, "runPlugin"):
1715                                 class PluginCaller:
1716                                         def __init__(self, fnc, *args):
1717                                                 self.fnc = fnc
1718                                                 self.args = args
1719                                         def __call__(self, *args, **kwargs):
1720                                                 self.fnc(*self.args)
1721
1722                                 Plugins = [ (p.name, PluginCaller(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_AUDIOMENU) ]
1723
1724                                 for p in Plugins:
1725                                         selection += 1
1726                                         flist.append((p[0], "CALLFUNC", p[1]))
1727                                         if availableKeys:
1728                                                 usedKeys.append(availableKeys[0])
1729                                                 del availableKeys[0]
1730                                         else:
1731                                                 usedKeys.append("")
1732
1733                         flist.append(("--", ""))
1734                         usedKeys.append("")
1735                         selection += 1
1736
1737                         keys = usedKeys + [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" ] + [""] * n
1738                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = flist + tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1739                 else:
1740                         del self.audioTracks
1741
1742         def changeAC3Downmix(self, arg):
1743                 choicelist = self.session.current_dialog["list"]
1744                 list = choicelist.list
1745                 t = list[0][1]
1746                 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1747                         _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1748                 choicelist.setList(list)
1749                 if config.av.downmix_ac3.value:
1750                         config.av.downmix_ac3.value = False
1751                 else:
1752                         config.av.downmix_ac3.value = True
1753                 config.av.downmix_ac3.save()
1754
1755         def audioSelected(self, audio):
1756                 if audio is not None:
1757                         if isinstance(audio[1], str):
1758                                 if audio[1] == "mode":
1759                                         keys = ["red", "green", "yellow"]
1760                                         selection = self.audioChannel.getCurrentChannel()
1761                                         tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1762                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1763                         else:
1764                                 del self.audioChannel
1765                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1766                                         self.audioTracks.selectTrack(audio[1])
1767                 else:
1768                         del self.audioChannel
1769                 del self.audioTracks
1770
1771         def modeSelected(self, mode):
1772                 if mode is not None:
1773                         self.audioChannel.selectChannel(mode[1])
1774                 del self.audioChannel
1775
1776 class InfoBarSubserviceSelection:
1777         def __init__(self):
1778                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1779                         {
1780                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1781                         })
1782
1783                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1784                         {
1785                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1786                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1787                         }, -1)
1788                 self["SubserviceQuickzapAction"].setEnabled(False)
1789
1790                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1791                         {
1792                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1793                         })
1794
1795                 self.bsel = None
1796
1797         def checkSubservicesAvail(self):
1798                 service = self.session.nav.getCurrentService()
1799                 subservices = service and service.subServices()
1800                 if not subservices or subservices.getNumberOfSubservices() == 0:
1801                         self["SubserviceQuickzapAction"].setEnabled(False)
1802
1803         def nextSubservice(self):
1804                 self.changeSubservice(+1)
1805
1806         def prevSubservice(self):
1807                 self.changeSubservice(-1)
1808
1809         def changeSubservice(self, direction):
1810                 service = self.session.nav.getCurrentService()
1811                 subservices = service and service.subServices()
1812                 n = subservices and subservices.getNumberOfSubservices()
1813                 if n and n > 0:
1814                         selection = -1
1815                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1816                         idx = 0
1817                         while idx < n:
1818                                 if subservices.getSubservice(idx).toString() == ref.toString():
1819                                         selection = idx
1820                                         break
1821                                 idx += 1
1822                         if selection != -1:
1823                                 selection += direction
1824                                 if selection >= n:
1825                                         selection=0
1826                                 elif selection < 0:
1827                                         selection=n-1
1828                                 newservice = subservices.getSubservice(selection)
1829                                 if newservice.valid():
1830                                         del subservices
1831                                         del service
1832                                         self.session.nav.playService(newservice, False)
1833
1834         def subserviceSelection(self):
1835                 service = self.session.nav.getCurrentService()
1836                 subservices = service and service.subServices()
1837                 self.bouquets = self.servicelist.getBouquetList()
1838                 n = subservices and subservices.getNumberOfSubservices()
1839                 selection = 0
1840                 if n and n > 0:
1841                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1842                         tlist = []
1843                         idx = 0
1844                         while idx < n:
1845                                 i = subservices.getSubservice(idx)
1846                                 if i.toString() == ref.toString():
1847                                         selection = idx
1848                                 tlist.append((i.getName(), i))
1849                                 idx += 1
1850
1851                         if self.bouquets and len(self.bouquets):
1852                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1853                                 if config.usage.multibouquet.value:
1854                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1855                                 else:
1856                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1857                                 selection += 3
1858                         else:
1859                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1860                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1861                                 selection += 2
1862
1863                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1864
1865         def subserviceSelected(self, service):
1866                 del self.bouquets
1867                 if not service is None:
1868                         if isinstance(service[1], str):
1869                                 if service[1] == "quickzap":
1870                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1871                                         self.session.open(SubservicesQuickzap, service[2])
1872                         else:
1873                                 self["SubserviceQuickzapAction"].setEnabled(True)
1874                                 self.session.nav.playService(service[1], False)
1875
1876         def addSubserviceToBouquetCallback(self, service):
1877                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1878                         self.selectedSubservice = service
1879                         if self.bouquets is None:
1880                                 cnt = 0
1881                         else:
1882                                 cnt = len(self.bouquets)
1883                         if cnt > 1: # show bouquet list
1884                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1885                         elif cnt == 1: # add to only one existing bouquet
1886                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1887                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1888
1889         def bouquetSelClosed(self, confirmed):
1890                 self.bsel = None
1891                 del self.selectedSubservice
1892                 if confirmed:
1893                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1894
1895         def addSubserviceToBouquet(self, dest):
1896                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1897                 if self.bsel:
1898                         self.bsel.close(True)
1899                 else:
1900                         del self.selectedSubservice
1901
1902 class InfoBarAdditionalInfo:
1903         def __init__(self):
1904
1905                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1906                 self["TimeshiftPossible"] = self["RecordingPossible"]
1907                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1908                 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1909                 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1910                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1911
1912 class InfoBarNotifications:
1913         def __init__(self):
1914                 self.onExecBegin.append(self.checkNotifications)
1915                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1916                 self.onClose.append(self.__removeNotification)
1917
1918         def __removeNotification(self):
1919                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1920
1921         def checkNotificationsIfExecing(self):
1922                 if self.execing:
1923                         self.checkNotifications()
1924
1925         def checkNotifications(self):
1926                 notifications = Notifications.notifications
1927                 if notifications:
1928                         n = notifications[0]
1929
1930                         del notifications[0]
1931                         cb = n[0]
1932
1933                         if n[3].has_key("onSessionOpenCallback"):
1934                                 n[3]["onSessionOpenCallback"]()
1935                                 del n[3]["onSessionOpenCallback"]
1936
1937                         if cb is not None:
1938                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1939                         else:
1940                                 dlg = self.session.open(n[1], *n[2], **n[3])
1941
1942                         # remember that this notification is currently active
1943                         d = (n[4], dlg)
1944                         Notifications.current_notifications.append(d)
1945                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1946
1947         def __notificationClosed(self, d):
1948                 Notifications.current_notifications.remove(d)
1949
1950 class InfoBarServiceNotifications:
1951         def __init__(self):
1952                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1953                         {
1954                                 iPlayableService.evEnd: self.serviceHasEnded
1955                         })
1956
1957         def serviceHasEnded(self):
1958                 print "service end!"
1959
1960                 try:
1961                         self.setSeekState(self.SEEK_STATE_PLAY)
1962                 except:
1963                         pass
1964
1965 class InfoBarCueSheetSupport:
1966         CUT_TYPE_IN = 0
1967         CUT_TYPE_OUT = 1
1968         CUT_TYPE_MARK = 2
1969         CUT_TYPE_LAST = 3
1970
1971         ENABLE_RESUME_SUPPORT = False
1972
1973         def __init__(self, actionmap = "InfobarCueSheetActions"):
1974                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1975                         {
1976                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1977                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1978                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1979                         }, prio=1)
1980
1981                 self.cut_list = [ ]
1982                 self.is_closing = False
1983                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1984                         {
1985                                 iPlayableService.evStart: self.__serviceStarted,
1986                         })
1987
1988         def __serviceStarted(self):
1989                 if self.is_closing:
1990                         return
1991                 print "new service started! trying to download cuts!"
1992                 self.downloadCuesheet()
1993
1994                 if self.ENABLE_RESUME_SUPPORT:
1995                         last = None
1996
1997                         for (pts, what) in self.cut_list:
1998                                 if what == self.CUT_TYPE_LAST:
1999                                         last = pts
2000
2001                         if last is not None:
2002                                 self.resume_point = last
2003                                 if config.usage.on_movie_start.value == "ask":
2004                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
2005                                 elif config.usage.on_movie_start.value == "resume":
2006 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2007 # TRANSLATORS: at the start of a movie, when the user has selected
2008 # TRANSLATORS: "Resume from last position" as start behavior.
2009 # TRANSLATORS: The purpose is to notify the user that the movie starts
2010 # TRANSLATORS: in the middle somewhere and not from the beginning.
2011 # TRANSLATORS: (Some translators seem to have interpreted it as a
2012 # TRANSLATORS: question or a choice, but it is a statement.)
2013                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2014
2015         def playLastCB(self, answer):
2016                 if answer == True:
2017                         self.doSeek(self.resume_point)
2018                 self.hideAfterResume()
2019
2020         def hideAfterResume(self):
2021                 if isinstance(self, InfoBarShowHide):
2022                         self.hide()
2023
2024         def __getSeekable(self):
2025                 service = self.session.nav.getCurrentService()
2026                 if service is None:
2027                         return None
2028                 return service.seek()
2029
2030         def cueGetCurrentPosition(self):
2031                 seek = self.__getSeekable()
2032                 if seek is None:
2033                         return None
2034                 r = seek.getPlayPosition()
2035                 if r[0]:
2036                         return None
2037                 return long(r[1])
2038
2039         def cueGetEndCutPosition(self):
2040                 ret = False
2041                 isin = True
2042                 for cp in self.cut_list:
2043                         if cp[1] == self.CUT_TYPE_OUT:
2044                                 if isin:
2045                                         isin = False
2046                                         ret = cp[0]
2047                         elif cp[1] == self.CUT_TYPE_IN:
2048                                 isin = True
2049                 return ret
2050                 
2051         def jumpPreviousNextMark(self, cmp, start=False):
2052                 current_pos = self.cueGetCurrentPosition()
2053                 if current_pos is None:
2054                         return False
2055                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2056                 if mark is not None:
2057                         pts = mark[0]
2058                 else:
2059                         return False
2060
2061                 self.doSeek(pts)
2062                 return True
2063
2064         def jumpPreviousMark(self):
2065                 # we add 2 seconds, so if the play position is <2s after
2066                 # the mark, the mark before will be used
2067                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2068
2069         def jumpNextMark(self):
2070                 if not self.jumpPreviousNextMark(lambda x: x):
2071                         self.doSeek(-1)
2072
2073         def getNearestCutPoint(self, pts, cmp=abs, start=False):
2074                 # can be optimized
2075                 beforecut = False
2076                 nearest = None
2077                 if start:
2078                         beforecut = True
2079                         bestdiff = cmp(0 - pts)
2080                         if bestdiff >= 0:
2081                                 nearest = [0, False]
2082                 for cp in self.cut_list:
2083                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2084                                 beforecut = False
2085                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
2086                                         diff = cmp(cp[0] - pts)
2087                                         if diff >= 0:
2088                                                 nearest = cp
2089                                                 bestdiff = diff
2090                                         else:
2091                                                 nearest = None
2092                         if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2093                                 diff = cmp(cp[0] - pts)
2094                                 if diff >= 0 and (nearest is None or bestdiff > diff):
2095                                         nearest = cp
2096                                         bestdiff = diff
2097                 return nearest
2098
2099         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2100                 current_pos = self.cueGetCurrentPosition()
2101                 if current_pos is None:
2102                         print "not seekable"
2103                         return
2104
2105                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2106
2107                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2108                         if onlyreturn:
2109                                 return nearest_cutpoint
2110                         if not onlyadd:
2111                                 self.removeMark(nearest_cutpoint)
2112                 elif not onlyremove and not onlyreturn:
2113                         self.addMark((current_pos, self.CUT_TYPE_MARK))
2114
2115                 if onlyreturn:
2116                         return None
2117
2118         def addMark(self, point):
2119                 insort(self.cut_list, point)
2120                 self.uploadCuesheet()
2121                 self.showAfterCuesheetOperation()
2122
2123         def removeMark(self, point):
2124                 self.cut_list.remove(point)
2125                 self.uploadCuesheet()
2126                 self.showAfterCuesheetOperation()
2127
2128         def showAfterCuesheetOperation(self):
2129                 if isinstance(self, InfoBarShowHide):
2130                         self.doShow()
2131
2132         def __getCuesheet(self):
2133                 service = self.session.nav.getCurrentService()
2134                 if service is None:
2135                         return None
2136                 return service.cueSheet()
2137
2138         def uploadCuesheet(self):
2139                 cue = self.__getCuesheet()
2140
2141                 if cue is None:
2142                         print "upload failed, no cuesheet interface"
2143                         return
2144                 cue.setCutList(self.cut_list)
2145
2146         def downloadCuesheet(self):
2147                 cue = self.__getCuesheet()
2148
2149                 if cue is None:
2150                         print "download failed, no cuesheet interface"
2151                         self.cut_list = [ ]
2152                 else:
2153                         self.cut_list = cue.getCutList()
2154
2155 class InfoBarSummary(Screen):
2156         skin = """
2157         <screen position="0,0" size="132,64">
2158                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2159                         <convert type="ClockToText">WithSeconds</convert>
2160                 </widget>
2161                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2162                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2163                         <convert type="ConditionalShowHide">Blink</convert>
2164                 </widget>
2165                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2166                         <convert type="ServiceName">Name</convert>
2167                 </widget>
2168                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2169                         <convert type="EventTime">Progress</convert>
2170                 </widget>
2171         </screen>"""
2172
2173 # for picon:  (path="piconlcd" will use LCD picons)
2174 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2175 #                       <convert type="ServiceName">Reference</convert>
2176 #               </widget>
2177
2178 class InfoBarSummarySupport:
2179         def __init__(self):
2180                 pass
2181
2182         def createSummary(self):
2183                 return InfoBarSummary
2184
2185 class InfoBarMoviePlayerSummary(Screen):
2186         skin = """
2187         <screen position="0,0" size="132,64">
2188                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2189                         <convert type="ClockToText">WithSeconds</convert>
2190                 </widget>
2191                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2192                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2193                         <convert type="ConditionalShowHide">Blink</convert>
2194                 </widget>
2195                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2196                         <convert type="ServiceName">Name</convert>
2197                 </widget>
2198                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2199                         <convert type="ServicePosition">Position</convert>
2200                 </widget>
2201         </screen>"""
2202
2203 class InfoBarMoviePlayerSummarySupport:
2204         def __init__(self):
2205                 pass
2206
2207         def createSummary(self):
2208                 return InfoBarMoviePlayerSummary
2209
2210 class InfoBarTeletextPlugin:
2211         def __init__(self):
2212                 self.teletext_plugin = None
2213
2214                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2215                         self.teletext_plugin = p
2216
2217                 if self.teletext_plugin is not None:
2218                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2219                                 {
2220                                         "startTeletext": (self.startTeletext, _("View teletext..."))
2221                                 })
2222                 else:
2223                         print "no teletext plugin found!"
2224
2225         def startTeletext(self):
2226                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2227
2228 class InfoBarSubtitleSupport(object):
2229         def __init__(self):
2230                 object.__init__(self)
2231                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2232                 self.__subtitles_enabled = False
2233
2234                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2235                         {
2236                                 iPlayableService.evEnd: self.__serviceStopped,
2237                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2238                         })
2239                 self.cached_subtitle_checked = False
2240                 self.__selected_subtitle = None
2241
2242         def __serviceStopped(self):
2243                 self.cached_subtitle_checked = False
2244                 if self.__subtitles_enabled:
2245                         self.subtitle_window.hide()
2246                         self.__subtitles_enabled = False
2247                         self.__selected_subtitle = None
2248
2249         def __updatedInfo(self):
2250                 if not self.cached_subtitle_checked:
2251                         self.cached_subtitle_checked = True
2252                         subtitle = self.getCurrentServiceSubtitle()
2253                         self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2254                         if self.__selected_subtitle:
2255                                 self.setSubtitlesEnable(True)
2256
2257         def getCurrentServiceSubtitle(self):
2258                 service = self.session.nav.getCurrentService()
2259                 return service and service.subtitle()
2260
2261         def setSubtitlesEnable(self, enable=True):
2262                 subtitle = self.getCurrentServiceSubtitle()
2263                 if enable:
2264                         if self.__selected_subtitle:
2265                                 if subtitle and not self.__subtitles_enabled:
2266                                         subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2267                                         self.subtitle_window.show()
2268                                         self.__subtitles_enabled = True
2269                 else:
2270                         if subtitle:
2271                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2272                         self.__selected_subtitle = False
2273                         self.__subtitles_enabled = False
2274                         self.subtitle_window.hide()
2275
2276         def setSelectedSubtitle(self, subtitle):
2277                 self.__selected_subtitle = subtitle
2278
2279         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2280         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2281
2282 class InfoBarServiceErrorPopupSupport:
2283         def __init__(self):
2284                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2285                         {
2286                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2287                                 iPlayableService.evStart: self.__serviceStarted
2288                         })
2289                 self.__serviceStarted()
2290
2291         def __serviceStarted(self):
2292                 self.last_error = None
2293                 Notifications.RemovePopup(id = "ZapError")
2294
2295         def __tuneFailed(self):
2296                 service = self.session.nav.getCurrentService()
2297                 info = service and service.info()
2298                 error = info and info.getInfo(iServiceInformation.sDVBState)
2299
2300                 if error == self.last_error:
2301                         error = None
2302                 else:
2303                         self.last_error = error
2304
2305                 error = {
2306                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2307                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2308                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2309                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2310                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2311                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2312                         eDVBServicePMTHandler.eventTuned: None,
2313                         eDVBServicePMTHandler.eventSOF: None,
2314                         eDVBServicePMTHandler.eventEOF: None,
2315                         eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2316                 }.get(error) #this returns None when the key not exist in the dict
2317
2318                 if error is not None:
2319                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2320                 else:
2321                         Notifications.RemovePopup(id = "ZapError")