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