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