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