use TemplatedMultiContent for language selection
[enigma2.git] / 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 #               el
691                 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
692                         return (0, -n * repeat, repeat, "<< %dx" % n)
693                 else:
694                         return (0, -n, 0, "<< %dx" % n)
695
696         def makeStateSlowMotion(self, n):
697                 return (0, 0, n, "/%d" % n)
698
699         def isStateForward(self, state):
700                 return state[1] > 1
701
702         def isStateBackward(self, state):
703                 return state[1] < 0
704
705         def isStateSlowMotion(self, state):
706                 return state[1] == 0 and state[2] > 1
707
708         def getHigher(self, n, lst):
709                 for x in lst:
710                         if x > n:
711                                 return x
712                 return False
713
714         def getLower(self, n, lst):
715                 lst = lst+[]
716                 lst.reverse()
717                 for x in lst:
718                         if x < n:
719                                 return x
720                 return False
721
722         def showAfterSeek(self):
723                 if isinstance(self, InfoBarShowHide):
724                         self.doShow()
725
726         def up(self):
727                 pass
728
729         def down(self):
730                 pass
731
732         def getSeek(self):
733                 service = self.session.nav.getCurrentService()
734                 if service is None:
735                         return None
736
737                 seek = service.seek()
738
739                 if seek is None or not seek.isCurrentlySeekable():
740                         return None
741
742                 return seek
743
744         def isSeekable(self):
745                 if self.getSeek() is None:
746                         return False
747                 return True
748
749         def __seekableStatusChanged(self):
750                 print "seekable status changed!"
751                 if not self.isSeekable():
752                         self["SeekActions"].setEnabled(False)
753                         print "not seekable, return to play"
754                         self.setSeekState(self.SEEK_STATE_PLAY)
755                 else:
756                         self["SeekActions"].setEnabled(True)
757                         print "seekable"
758
759         def __serviceStarted(self):
760                 self.seekstate = self.SEEK_STATE_PLAY
761                 self.__seekableStatusChanged()
762                 if self.eofState != 0:
763                         self.eofTimer.stop()
764                 self.eofState = 0
765
766         def setSeekState(self, state):
767                 service = self.session.nav.getCurrentService()
768
769                 if service is None:
770                         return False
771
772                 if not self.isSeekable():
773                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
774                                 state = self.SEEK_STATE_PLAY
775
776                 pauseable = service.pause()
777
778                 if pauseable is None:
779                         print "not pauseable."
780                         state = self.SEEK_STATE_PLAY
781
782                 oldstate = self.seekstate
783                 self.seekstate = state
784
785                 for i in range(3):
786                         if oldstate[i] != self.seekstate[i]:
787                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
788
789                 for c in self.onPlayStateChanged:
790                         c(self.seekstate)
791
792                 self.checkSkipShowHideLock()
793
794                 return True
795
796         def playpauseService(self):
797                 if self.seekstate != self.SEEK_STATE_PLAY:
798                         self.unPauseService()
799                 else:
800                         self.pauseService()
801
802         def pauseService(self):
803                 if self.seekstate == self.SEEK_STATE_PAUSE:
804                         if config.seek.on_pause.value == "play":
805                                 self.unPauseService()
806                         elif config.seek.on_pause.value == "step":
807                                 self.doSeekRelative(0)
808                         elif config.seek.on_pause.value == "last":
809                                 self.setSeekState(self.lastseekstate)
810                                 self.lastseekstate = self.SEEK_STATE_PLAY
811                 else:
812                         if self.seekstate != self.SEEK_STATE_EOF:
813                                 self.lastseekstate = self.seekstate
814                         self.setSeekState(self.SEEK_STATE_PAUSE);
815
816         def unPauseService(self):
817                 print "unpause"
818                 if self.seekstate == self.SEEK_STATE_PLAY:
819                         return 0
820                 self.setSeekState(self.SEEK_STATE_PLAY)
821
822         def doSeek(self, pts):
823                 seekable = self.getSeek()
824                 if seekable is None:
825                         return
826                 prevstate = self.seekstate
827                 if self.eofState == 1:
828                         self.eofState = 2
829                         self.inhibitEof()
830                 if self.seekstate == self.SEEK_STATE_EOF:
831                         if prevstate == self.SEEK_STATE_PAUSE:
832                                 self.setSeekState(self.SEEK_STATE_PAUSE)
833                         else:
834                                 self.setSeekState(self.SEEK_STATE_PLAY)
835                 self.eofInhibitTimer.start(200, True)
836                 seekable.seekTo(pts)
837
838         def doSeekRelative(self, pts):
839                 seekable = self.getSeek()
840                 if seekable is None:
841                         return
842                 prevstate = self.seekstate
843                 if self.eofState == 1:
844                         self.eofState = 2
845                         self.inhibitEof()
846                 if self.seekstate == self.SEEK_STATE_EOF:
847                         if prevstate == self.SEEK_STATE_PAUSE:
848                                 self.setSeekState(self.SEEK_STATE_PAUSE)
849                         else:
850                                 self.setSeekState(self.SEEK_STATE_PLAY)
851                 self.eofInhibitTimer.start(200, True)
852                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
853                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
854                         self.showAfterSeek()
855
856         def seekFwd(self):
857                 if self.seekstate == self.SEEK_STATE_PLAY:
858                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
859                 elif self.seekstate == self.SEEK_STATE_PAUSE:
860                         if len(config.seek.speeds_slowmotion.value):
861                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
862                         else:
863                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
864                 elif self.seekstate == self.SEEK_STATE_EOF:
865                         pass
866                 elif self.isStateForward(self.seekstate):
867                         speed = self.seekstate[1]
868                         if self.seekstate[2]:
869                                 speed /= self.seekstate[2]
870                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
871                         self.setSeekState(self.makeStateForward(speed))
872                 elif self.isStateBackward(self.seekstate):
873                         speed = -self.seekstate[1]
874                         if self.seekstate[2]:
875                                 speed /= self.seekstate[2]
876                         speed = self.getLower(speed, config.seek.speeds_backward.value)
877                         if speed:
878                                 self.setSeekState(self.makeStateBackward(speed))
879                         else:
880                                 self.setSeekState(self.SEEK_STATE_PLAY)
881                 elif self.isStateSlowMotion(self.seekstate):
882                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
883                         self.setSeekState(self.makeStateSlowMotion(speed))
884
885         def seekBack(self):
886                 if self.seekstate == self.SEEK_STATE_PLAY:
887                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
888                 elif self.seekstate == self.SEEK_STATE_EOF:
889                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
890                         self.doSeekRelative(-6)
891                 elif self.seekstate == self.SEEK_STATE_PAUSE:
892                         self.doSeekRelative(-3)
893                 elif self.isStateForward(self.seekstate):
894                         speed = self.seekstate[1]
895                         if self.seekstate[2]:
896                                 speed /= self.seekstate[2]
897                         speed = self.getLower(speed, config.seek.speeds_forward.value)
898                         if speed:
899                                 self.setSeekState(self.makeStateForward(speed))
900                         else:
901                                 self.setSeekState(self.SEEK_STATE_PLAY)
902                 elif self.isStateBackward(self.seekstate):
903                         speed = -self.seekstate[1]
904                         if self.seekstate[2]:
905                                 speed /= self.seekstate[2]
906                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
907                         self.setSeekState(self.makeStateBackward(speed))
908                 elif self.isStateSlowMotion(self.seekstate):
909                         speed = self.getHigher(self.seekstate[2], config.seek.speeds_slowmotion.value)
910                         if speed:
911                                 self.setSeekState(self.makeStateSlowMotion(speed))
912                         else:
913                                 self.setSeekState(self.SEEK_STATE_PAUSE)
914
915         def seekFwdManual(self):
916                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
917
918         def fwdSeekTo(self, minutes):
919                 print "Seek", minutes, "minutes forward"
920                 self.doSeekRelative(minutes * 60 * 90000)
921
922         def seekBackManual(self):
923                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
924
925         def rwdSeekTo(self, minutes):
926                 print "rwdSeekTo"
927                 self.doSeekRelative(-minutes * 60 * 90000)
928
929         def checkSkipShowHideLock(self):
930                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
931
932                 if config.usage.show_infobar_on_skip.value:
933                         if self.lockedBecauseOfSkipping and not wantlock:
934                                 self.unlockShow()
935                                 self.lockedBecauseOfSkipping = False
936
937                         if wantlock and not self.lockedBecauseOfSkipping:
938                                 self.lockShow()
939                                 self.lockedBecauseOfSkipping = True
940
941         def calcRemainingTime(self):
942                 seekable = self.getSeek()
943                 if seekable is not None:
944                         len = seekable.getLength()
945                         try:
946                                 tmp = self.cueGetEndCutPosition()
947                                 if tmp:
948                                         len = [False, tmp]
949                         except:
950                                 pass
951                         pos = seekable.getPlayPosition()
952                         speednom = self.seekstate[1] or 1
953                         speedden = self.seekstate[2] or 1
954                         if not len[0] and not pos[0]:
955                                 if len[1] <= pos[1]:
956                                         return 0
957                                 time = (len[1] - pos[1])*speedden/(90*speednom)
958                                 return time
959                 return False
960                 
961         def __evEOF(self):
962                 if self.eofState == 0 and self.seekstate != self.SEEK_STATE_EOF:
963                         self.eofState = 1
964                         time = self.calcRemainingTime()
965                         if not time:
966                                 time = 3000   # Failed to calc, use default
967                         elif time == 0:
968                                 time = 300    # Passed end, shortest wait
969                         elif time > 15000:
970                                 self.eofState = -2  # Too long, block eof
971                                 time = 15000
972                         else:
973                                 time += 1000  # Add margin
974                         self.eofTimer.start(time, True)
975
976         def inhibitEof(self):
977                 if self.eofState >= 1:
978                         self.eofState = -self.eofState
979                         self.eofTimer.stop()
980                         self.doEof()
981
982         def doEof(self):
983                 if self.seekstate == self.SEEK_STATE_EOF:
984                         return
985                 if self.eofState == -2 or self.isStateBackward(self.seekstate):
986                         self.eofState = 0
987                         return
988
989                 # if we are seeking, we try to end up ~1s before the end, and pause there.
990                 eofstate = self.eofState
991                 seekstate = self.seekstate
992                 self.eofState = 0
993                 if not self.seekstate == self.SEEK_STATE_PAUSE:
994                         self.setSeekState(self.SEEK_STATE_EOF)
995                 if eofstate == -1 or not seekstate in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
996                         seekable = self.getSeek()
997                         if seekable is not None:
998                                 seekable.seekTo(-1)
999                 if eofstate == 1 and seekstate == self.SEEK_STATE_PLAY:
1000                         self.doEofInternal(True)
1001                 else:
1002                         self.doEofInternal(False)
1003
1004         def doEofInternal(self, playing):
1005                 pass            # Defined in subclasses
1006
1007         def __evSOF(self):
1008                 self.setSeekState(self.SEEK_STATE_PLAY)
1009                 self.doSeek(0)
1010
1011 from Screens.PVRState import PVRState, TimeshiftState
1012
1013 class InfoBarPVRState:
1014         def __init__(self, screen=PVRState):
1015                 self.onPlayStateChanged.append(self.__playStateChanged)
1016                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1017                 self.onShow.append(self._mayShow)
1018                 self.onHide.append(self.pvrStateDialog.hide)
1019
1020         def _mayShow(self):
1021                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1022                         self.pvrStateDialog.show()
1023
1024         def __playStateChanged(self, state):
1025                 playstateString = state[3]
1026                 self.pvrStateDialog["state"].setText(playstateString)
1027                 self._mayShow()
1028
1029 class InfoBarTimeshiftState(InfoBarPVRState):
1030         def __init__(self):
1031                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
1032
1033         def _mayShow(self):
1034                 if self.execing and self.timeshift_enabled:
1035                         self.pvrStateDialog.show()
1036
1037 class InfoBarShowMovies:
1038
1039         # i don't really like this class.
1040         # it calls a not further specified "movie list" on up/down/movieList,
1041         # so this is not more than an action map
1042         def __init__(self):
1043                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1044                         {
1045                                 "movieList": (self.showMovies, _("movie list")),
1046                                 "up": (self.showMovies, _("movie list")),
1047                                 "down": (self.showMovies, _("movie list"))
1048                         })
1049
1050 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1051
1052 # Hrmf.
1053 #
1054 # Timeshift works the following way:
1055 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1056 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1057 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1058 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1059 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1060 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1061 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1062 #
1063
1064 # in other words:
1065 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1066 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1067 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1068 # - the user can now PVR around
1069 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1070 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1071 # after!
1072 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1073 # - if the user rewinds, or press pause, timeshift will be activated again
1074
1075 # note that a timeshift can be enabled ("recording") and
1076 # activated (currently time-shifting).
1077
1078 class InfoBarTimeshift:
1079         def __init__(self):
1080                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1081                         {
1082                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1083                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1084                         }, prio=1)
1085                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1086                         {
1087                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1088                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1089                         }, prio=-1) # priority over record
1090
1091                 self.timeshift_enabled = 0
1092                 self.timeshift_state = 0
1093                 self.ts_rewind_timer = eTimer()
1094                 self.ts_rewind_timer.callback.append(self.rewindService)
1095
1096                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1097                         {
1098                                 iPlayableService.evStart: self.__serviceStarted,
1099                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1100                         })
1101
1102         def getTimeshift(self):
1103                 service = self.session.nav.getCurrentService()
1104                 return service and service.timeshift()
1105
1106         def startTimeshift(self):
1107                 print "enable timeshift"
1108                 ts = self.getTimeshift()
1109                 if ts is None:
1110                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1111                         print "no ts interface"
1112                         return 0
1113
1114                 if self.timeshift_enabled:
1115                         print "hu, timeshift already enabled?"
1116                 else:
1117                         if not ts.startTimeshift():
1118                                 self.timeshift_enabled = 1
1119
1120                                 # we remove the "relative time" for now.
1121                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1122
1123                                 # PAUSE.
1124                                 #self.setSeekState(self.SEEK_STATE_PAUSE)
1125                                 self.activateTimeshiftEnd(False)
1126
1127                                 # enable the "TimeshiftEnableActions", which will override
1128                                 # the startTimeshift actions
1129                                 self.__seekableStatusChanged()
1130                         else:
1131                                 print "timeshift failed"
1132
1133         def stopTimeshift(self):
1134                 if not self.timeshift_enabled:
1135                         return 0
1136                 print "disable timeshift"
1137                 ts = self.getTimeshift()
1138                 if ts is None:
1139                         return 0
1140                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1141
1142         def stopTimeshiftConfirmed(self, confirmed):
1143                 if not confirmed:
1144                         return
1145
1146                 ts = self.getTimeshift()
1147                 if ts is None:
1148                         return
1149
1150                 ts.stopTimeshift()
1151                 self.timeshift_enabled = 0
1152
1153                 # disable actions
1154                 self.__seekableStatusChanged()
1155
1156         # activates timeshift, and seeks to (almost) the end
1157         def activateTimeshiftEnd(self, back = True):
1158                 ts = self.getTimeshift()
1159                 print "activateTimeshiftEnd"
1160
1161                 if ts is None:
1162                         return
1163
1164                 if ts.isTimeshiftActive():
1165                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1166                         self.pauseService()
1167                 else:
1168                         print "play, ..."
1169                         ts.activateTimeshift() # activate timeshift will automatically pause
1170                         self.setSeekState(self.SEEK_STATE_PAUSE)
1171
1172                 if back:
1173                         self.doSeek(-5) # seek some gops before end
1174                         self.ts_rewind_timer.start(200, 1)
1175                 else:
1176                         self.doSeek(-1) # seek 1 gop before end
1177
1178         def rewindService(self):
1179                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1180
1181         # same as activateTimeshiftEnd, but pauses afterwards.
1182         def activateTimeshiftEndAndPause(self):
1183                 print "activateTimeshiftEndAndPause"
1184                 #state = self.seekstate
1185                 self.activateTimeshiftEnd(False)
1186
1187         def __seekableStatusChanged(self):
1188                 enabled = False
1189
1190                 print "self.isSeekable", self.isSeekable()
1191                 print "self.timeshift_enabled", self.timeshift_enabled
1192
1193                 # when this service is not seekable, but timeshift
1194                 # is enabled, this means we can activate
1195                 # the timeshift
1196                 if not self.isSeekable() and self.timeshift_enabled:
1197                         enabled = True
1198
1199                 print "timeshift activate:", enabled
1200                 self["TimeshiftActivateActions"].setEnabled(enabled)
1201
1202         def __serviceStarted(self):
1203                 self.timeshift_enabled = False
1204                 self.__seekableStatusChanged()
1205
1206 from Screens.PiPSetup import PiPSetup
1207
1208 class InfoBarExtensions:
1209         EXTENSION_SINGLE = 0
1210         EXTENSION_LIST = 1
1211
1212         def __init__(self):
1213                 self.list = []
1214
1215                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1216                         {
1217                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1218                         })
1219
1220         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1221                 self.list.append((type, extension, key))
1222
1223         def updateExtension(self, extension, key = None):
1224                 self.extensionsList.append(extension)
1225                 if key is not None:
1226                         if self.extensionKeys.has_key(key):
1227                                 key = None
1228
1229                 if key is None:
1230                         for x in self.availableKeys:
1231                                 if not self.extensionKeys.has_key(x):
1232                                         key = x
1233                                         break
1234
1235                 if key is not None:
1236                         self.extensionKeys[key] = len(self.extensionsList) - 1
1237
1238         def updateExtensions(self):
1239                 self.extensionsList = []
1240                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1241                 self.extensionKeys = {}
1242                 for x in self.list:
1243                         if x[0] == self.EXTENSION_SINGLE:
1244                                 self.updateExtension(x[1], x[2])
1245                         else:
1246                                 for y in x[1]():
1247                                         self.updateExtension(y[0], y[1])
1248
1249
1250         def showExtensionSelection(self):
1251                 self.updateExtensions()
1252                 extensionsList = self.extensionsList[:]
1253                 keys = []
1254                 list = []
1255                 for x in self.availableKeys:
1256                         if self.extensionKeys.has_key(x):
1257                                 entry = self.extensionKeys[x]
1258                                 extension = self.extensionsList[entry]
1259                                 if extension[2]():
1260                                         name = str(extension[0]())
1261                                         list.append((extension[0](), extension))
1262                                         keys.append(x)
1263                                         extensionsList.remove(extension)
1264                                 else:
1265                                         extensionsList.remove(extension)
1266                 for x in extensionsList:
1267                         list.append((x[0](), x))
1268                 keys += [""] * len(extensionsList)
1269                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys)
1270
1271         def extensionCallback(self, answer):
1272                 if answer is not None:
1273                         answer[1][1]()
1274
1275 from Tools.BoundFunction import boundFunction
1276
1277 # depends on InfoBarExtensions
1278 from Components.PluginComponent import plugins
1279
1280 class InfoBarPlugins:
1281         def __init__(self):
1282                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1283
1284         def getPluginName(self, name):
1285                 return name
1286
1287         def getPluginList(self):
1288                 list = []
1289                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1290                         list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
1291                 return list
1292
1293         def runPlugin(self, plugin):
1294                 plugin(session = self.session, servicelist = self.servicelist)
1295
1296 # depends on InfoBarExtensions
1297 class InfoBarSleepTimer:
1298         def __init__(self):
1299                 self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, self.available), "1")
1300
1301         def available(self):
1302                 return True
1303
1304         def getSleepTimerName(self):
1305                 return _("Sleep Timer")
1306
1307         def showSleepTimerSetup(self):
1308                 self.session.open(SleepTimerEdit)
1309
1310 # depends on InfoBarExtensions
1311 class InfoBarPiP:
1312         def __init__(self):
1313                 self.session.pipshown = False
1314                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1315                         self.addExtension((self.getShowHideName, self.showPiP, self.available), "blue")
1316                         self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1317                         self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1318
1319         def available(self):
1320                 return SystemInfo.get("NumVideoDecoders", 1) > 1
1321
1322         def pipShown(self):
1323                 return self.session.pipshown
1324
1325         def pipHandles0Action(self):
1326                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1327
1328         def getShowHideName(self):
1329                 if self.session.pipshown:
1330                         return _("Disable Picture in Picture")
1331                 else:
1332                         return _("Activate Picture in Picture")
1333
1334         def getSwapName(self):
1335                 return _("Swap Services")
1336
1337         def getMoveName(self):
1338                 return _("Move Picture in Picture")
1339
1340         def showPiP(self):
1341                 if self.session.pipshown:
1342                         del self.session.pip
1343                         self.session.pipshown = False
1344                 else:
1345                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1346                         self.session.pip.show()
1347                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1348                         if self.session.pip.playService(newservice):
1349                                 self.session.pipshown = True
1350                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1351                         else:
1352                                 self.session.pipshown = False
1353                                 del self.session.pip
1354                         self.session.nav.playService(newservice)
1355
1356         def swapPiP(self):
1357                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1358                 if self.session.pip.servicePath:
1359                         servicepath = self.servicelist.getCurrentServicePath()
1360                         ref=servicepath[len(servicepath)-1]
1361                         pipref=self.session.pip.getCurrentService()
1362                         self.session.pip.playService(swapservice)
1363                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1364                         if pipref.toString() != ref.toString(): # is a subservice ?
1365                                 self.session.nav.stopService() # stop portal
1366                                 self.session.nav.playService(pipref) # start subservice
1367                         self.session.pip.servicePath=servicepath
1368
1369         def movePiP(self):
1370                 self.session.open(PiPSetup, pip = self.session.pip)
1371
1372         def pipDoHandle0Action(self):
1373                 use = config.usage.pip_zero_button.value
1374                 if "swap" == use:
1375                         self.swapPiP()
1376                 elif "swapstop" == use:
1377                         self.swapPiP()
1378                         self.showPiP()
1379                 elif "stop" == use:
1380                         self.showPiP()
1381
1382 from RecordTimer import parseEvent
1383
1384 class InfoBarInstantRecord:
1385         """Instant Record - handles the instantRecord action in order to
1386         start/stop instant records"""
1387         def __init__(self):
1388                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1389                         {
1390                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1391                         })
1392                 self.recording = []
1393 #### DEPRECATED CODE ####
1394                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1395                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1396                 self["BlinkingPoint"].deprecationInfo = (
1397                         "session.RecordState source, Pixmap renderer and "
1398                         "ConditionalShowHide/Blink Converter", "2008-02")
1399 #########################
1400
1401         def stopCurrentRecording(self, entry = -1):
1402                 if entry is not None and entry != -1:
1403                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1404                         self.recording.remove(self.recording[entry])
1405
1406         def startInstantRecording(self, limitEvent = False):
1407                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1408
1409                 # try to get event info
1410                 event = None
1411                 try:
1412                         service = self.session.nav.getCurrentService()
1413                         epg = eEPGCache.getInstance()
1414                         event = epg.lookupEventTime(serviceref, -1, 0)
1415                         if event is None:
1416                                 info = service.info()
1417                                 ev = info.getEvent(0)
1418                                 event = ev
1419                 except:
1420                         pass
1421
1422                 begin = time()
1423                 end = time() + 3600 * 10
1424                 name = "instant record"
1425                 description = ""
1426                 eventid = None
1427
1428                 if event is not None:
1429                         curEvent = parseEvent(event)
1430                         name = curEvent[2]
1431                         description = curEvent[3]
1432                         eventid = curEvent[4]
1433                         if limitEvent:
1434                                 end = curEvent[1]
1435                 else:
1436                         if limitEvent:
1437                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1438
1439                 data = (begin, end, name, description, eventid)
1440
1441                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1442                 recording.dontSave = True
1443                 self.recording.append(recording)
1444
1445 #### DEPRECATED CODE ####
1446                 self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1447 #########################
1448
1449         def isInstantRecordRunning(self):
1450                 print "self.recording:", self.recording
1451                 if len(self.recording) > 0:
1452                         for x in self.recording:
1453                                 if x.isRunning():
1454                                         return True
1455                 return False
1456
1457         def recordQuestionCallback(self, answer):
1458                 print "pre:\n", self.recording
1459
1460                 if answer is None or answer[1] == "no":
1461                         return
1462                 list = []
1463                 recording = self.recording[:]
1464                 for x in recording:
1465                         if not x in self.session.nav.RecordTimer.timer_list:
1466                                 self.recording.remove(x)
1467                         elif x.dontSave and x.isRunning():
1468                                 list.append((x, False))
1469
1470                 if answer[1] == "changeduration":
1471                         if len(self.recording) == 1:
1472                                 self.changeDuration(0)
1473                         else:
1474                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1475                 elif answer[1] == "changeendtime":
1476                         if len(self.recording) == 1:
1477                                 self.setEndtime(0)
1478                         else:
1479                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1480                 elif answer[1] == "stop":
1481                         if len(self.recording) == 1:
1482                                 self.stopCurrentRecording(0)
1483                         else:
1484                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1485                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1486                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1487                         if answer[1] == "manualduration":
1488                                 self.changeDuration(len(self.recording)-1)
1489                         elif answer[1] == "manualendtime":
1490                                 self.setEndtime(len(self.recording)-1)
1491                 print "after:\n", self.recording
1492
1493         def setEndtime(self, entry):
1494                 if entry is not None:
1495                         self.selectedEntry = entry
1496                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1497                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1498                         dlg.setTitle(_("Please change recording endtime"))
1499
1500         def TimeDateInputClosed(self, ret):
1501                 if len(ret) > 1:
1502                         if ret[0]:
1503                                 localendtime = localtime(ret[1])
1504                                 print "stopping recording at", strftime("%c", localendtime)
1505                                 self.recording[self.selectedEntry].end = ret[1]
1506                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1507
1508         def changeDuration(self, entry):
1509                 if entry is not None:
1510                         self.selectedEntry = entry
1511                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1512
1513         def inputCallback(self, value):
1514                 if value is not None:
1515                         print "stopping recording after", int(value), "minutes."
1516                         self.recording[self.selectedEntry].end = time() + 60 * int(value)
1517                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1518
1519         def instantRecord(self):
1520                 try:
1521                         stat = os_stat(resolveFilename(SCOPE_HDD))
1522                 except:
1523                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1524                         return
1525
1526                 if self.isInstantRecordRunning():
1527                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1528                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1529                                 list=[(_("stop recording"), "stop"), \
1530                                 (_("change recording (duration)"), "changeduration"), \
1531                                 (_("change recording (endtime)"), "changeendtime"), \
1532                                 (_("add recording (indefinitely)"), "indefinitely"), \
1533                                 (_("add recording (stop after current event)"), "event"), \
1534                                 (_("add recording (enter recording duration)"), "manualduration"), \
1535                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1536                                 (_("do nothing"), "no")])
1537                 else:
1538                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1539                                 title=_("Start recording?"), \
1540                                 list=[(_("add recording (indefinitely)"), "indefinitely"), \
1541                                 (_("add recording (stop after current event)"), "event"), \
1542                                 (_("add recording (enter recording duration)"), "manualduration"), \
1543                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1544                                 (_("don't record"), "no")])
1545
1546 from Tools.ISO639 import LanguageCodes
1547
1548 class InfoBarAudioSelection:
1549         def __init__(self):
1550                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1551                         {
1552                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1553                         })
1554
1555         def audioSelection(self):
1556                 service = self.session.nav.getCurrentService()
1557                 audio = service and service.audioTracks()
1558                 self.audioTracks = audio
1559                 n = audio and audio.getNumberOfTracks() or 0
1560                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1561                 tlist = []
1562                 if n > 0:
1563                         self.audioChannel = service.audioChannel()
1564
1565                         for x in range(n):
1566                                 i = audio.getTrackInfo(x)
1567                                 language = i.getLanguage()
1568                                 description = i.getDescription()
1569
1570                                 if LanguageCodes.has_key(language):
1571                                         language = LanguageCodes[language][0]
1572
1573                                 if len(description):
1574                                         description += " (" + language + ")"
1575                                 else:
1576                                         description = language
1577
1578                                 tlist.append((description, x))
1579
1580                         selectedAudio = audio.getCurrentTrack()
1581                         tlist.sort(key=lambda x: x[0])
1582
1583                         selection = 2
1584                         for x in tlist:
1585                                 if x[1] != selectedAudio:
1586                                         selection += 1
1587                                 else:
1588                                         break
1589
1590                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1591                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1592                 else:
1593                         del self.audioTracks
1594
1595         def audioSelected(self, audio):
1596                 if audio is not None:
1597                         if isinstance(audio[1], str):
1598                                 if audio[1] == "mode":
1599                                         keys = ["red", "green", "yellow"]
1600                                         selection = self.audioChannel.getCurrentChannel()
1601                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1602                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1603                         else:
1604                                 del self.audioChannel
1605                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1606                                         self.audioTracks.selectTrack(audio[1])
1607                 else:
1608                         del self.audioChannel
1609                 del self.audioTracks
1610
1611         def modeSelected(self, mode):
1612                 if mode is not None:
1613                         self.audioChannel.selectChannel(mode[1])
1614                 del self.audioChannel
1615
1616 class InfoBarSubserviceSelection:
1617         def __init__(self):
1618                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1619                         {
1620                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1621                         })
1622
1623                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1624                         {
1625                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1626                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1627                         }, -1)
1628                 self["SubserviceQuickzapAction"].setEnabled(False)
1629
1630                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1631
1632                 self.bsel = None
1633
1634         def checkSubservicesAvail(self, ev):
1635                 if ev == iPlayableService.evUpdatedEventInfo:
1636                         service = self.session.nav.getCurrentService()
1637                         subservices = service and service.subServices()
1638                         if not subservices or subservices.getNumberOfSubservices() == 0:
1639                                 self["SubserviceQuickzapAction"].setEnabled(False)
1640
1641         def nextSubservice(self):
1642                 self.changeSubservice(+1)
1643
1644         def prevSubservice(self):
1645                 self.changeSubservice(-1)
1646
1647         def changeSubservice(self, direction):
1648                 service = self.session.nav.getCurrentService()
1649                 subservices = service and service.subServices()
1650                 n = subservices and subservices.getNumberOfSubservices()
1651                 if n and n > 0:
1652                         selection = -1
1653                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1654                         for x in range(n):
1655                                 if subservices.getSubservice(x).toString() == ref.toString():
1656                                         selection = x
1657                         if selection != -1:
1658                                 selection += direction
1659                                 if selection >= n:
1660                                         selection=0
1661                                 elif selection < 0:
1662                                         selection=n-1
1663                                 newservice = subservices.getSubservice(selection)
1664                                 if newservice.valid():
1665                                         del subservices
1666                                         del service
1667                                         self.session.nav.playService(newservice)
1668
1669         def subserviceSelection(self):
1670                 service = self.session.nav.getCurrentService()
1671                 subservices = service and service.subServices()
1672                 self.bouquets = self.servicelist.getBouquetList()
1673                 n = subservices and subservices.getNumberOfSubservices()
1674                 selection = 0
1675                 if n and n > 0:
1676                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1677                         tlist = []
1678                         for x in range(n):
1679                                 i = subservices.getSubservice(x)
1680                                 if i.toString() == ref.toString():
1681                                         selection = x
1682                                 tlist.append((i.getName(), i))
1683
1684                         if self.bouquets and len(self.bouquets):
1685                                 keys = ["red", "green", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1686                                 if config.usage.multibouquet.value:
1687                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1688                                 else:
1689                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1690                                 selection += 3
1691                         else:
1692                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1693                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1694                                 selection += 2
1695
1696                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
1697
1698         def subserviceSelected(self, service):
1699                 del self.bouquets
1700                 if not service is None:
1701                         if isinstance(service[1], str):
1702                                 if service[1] == "quickzap":
1703                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1704                                         self.session.open(SubservicesQuickzap, service[2])
1705                         else:
1706                                 self["SubserviceQuickzapAction"].setEnabled(True)
1707                                 self.session.nav.playService(service[1])
1708
1709         def addSubserviceToBouquetCallback(self, service):
1710                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1711                         self.selectedSubservice = service
1712                         if self.bouquets is None:
1713                                 cnt = 0
1714                         else:
1715                                 cnt = len(self.bouquets)
1716                         if cnt > 1: # show bouquet list
1717                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1718                         elif cnt == 1: # add to only one existing bouquet
1719                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1720                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1721
1722         def bouquetSelClosed(self, confirmed):
1723                 self.bsel = None
1724                 del self.selectedSubservice
1725                 if confirmed:
1726                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1727
1728         def addSubserviceToBouquet(self, dest):
1729                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1730                 if self.bsel:
1731                         self.bsel.close(True)
1732                 else:
1733                         del self.selectedSubservice
1734
1735 class InfoBarAdditionalInfo:
1736         def __init__(self):
1737
1738                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1739                 self["TimeshiftPossible"] = self["RecordingPossible"]
1740                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1741
1742 ######### DEPRECATED CODE ##########
1743                 self["NimA"] = Pixmap()
1744                 self["NimA"].deprecationInfo = (
1745                         "session.TunerInfo source, Pixmap renderer, TunerInfo/UseMask Converter"
1746                         ", ValueBitTest(1) Converter and ConditionalShowHide Converter", "2008-02")
1747                 self["NimB"] = Pixmap()
1748                 self["NimB"].deprecationInfo = (
1749                         "session.TunerInfo source, Pixmap renderer, TunerInfo/UseMask Converter"
1750                         ", ValueBitTest(2) Converter and ConditionalShowHide Converter", "2008-02")
1751                 self["NimA_Active"] = Pixmap()
1752                 self["NimA_Active"].deprecationInfo = (
1753                         "session.FrontendInfo source, Pixmap renderer, FrontendInfo/NUMBER Converter"
1754                         ", ValueRange(1,1) Converter and ConditionalShowHide Converter", "2008-02")
1755                 self["NimB_Active"] = Pixmap()
1756                 self["NimB_Active"].deprecationInfo = (
1757                         "session.FrontendInfo source, Pixmap renderer, FrontendInfo/NUMBER Converter"
1758                         ", ValueRange(1,1) Converter and ConditionalShowHide Converter", "2008-02")
1759
1760                 res_mgr = eDVBResourceManager.getInstance()
1761                 if res_mgr:
1762                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1763
1764                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1765
1766         def tunerUseMaskChanged(self, mask):
1767                 if mask&1:
1768                         self["NimA_Active"].show()
1769                 else:
1770                         self["NimA_Active"].hide()
1771                 if mask&2:
1772                         self["NimB_Active"].show()
1773                 else:
1774                         self["NimB_Active"].hide()
1775
1776         def checkTunerState(self, service):
1777                 info = service and service.frontendInfo()
1778                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1779                 if feNumber is None:
1780                         self["NimA"].hide()
1781                         self["NimB"].hide()
1782                 elif feNumber == 0:
1783                         self["NimB"].hide()
1784                         self["NimA"].show()
1785                 elif feNumber == 1:
1786                         self["NimA"].hide()
1787                         self["NimB"].show()
1788
1789         def gotServiceEvent(self, ev):
1790                 service = self.session.nav.getCurrentService()
1791                 if ev == iPlayableService.evUpdatedInfo or ev == iPlayableService.evEnd:
1792                         self.checkTunerState(service)
1793 ####################################
1794
1795 class InfoBarNotifications:
1796         def __init__(self):
1797                 self.onExecBegin.append(self.checkNotifications)
1798                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1799                 self.onClose.append(self.__removeNotification)
1800
1801         def __removeNotification(self):
1802                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1803
1804         def checkNotificationsIfExecing(self):
1805                 if self.execing:
1806                         self.checkNotifications()
1807
1808         def checkNotifications(self):
1809                 if len(Notifications.notifications):
1810                         n = Notifications.notifications[0]
1811
1812                         Notifications.notifications = Notifications.notifications[1:]
1813                         cb = n[0]
1814
1815                         if n[3].has_key("onSessionOpenCallback"):
1816                                 n[3]["onSessionOpenCallback"]()
1817                                 del n[3]["onSessionOpenCallback"]
1818
1819                         if cb is not None:
1820                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1821                         else:
1822                                 dlg = self.session.open(n[1], *n[2], **n[3])
1823
1824                         # remember that this notification is currently active
1825                         d = (n[4], dlg)
1826                         Notifications.current_notifications.append(d)
1827                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1828
1829         def __notificationClosed(self, d):
1830                 Notifications.current_notifications.remove(d)
1831
1832 class InfoBarServiceNotifications:
1833         def __init__(self):
1834                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1835                         {
1836                                 iPlayableService.evEnd: self.serviceHasEnded
1837                         })
1838
1839         def serviceHasEnded(self):
1840                 print "service end!"
1841
1842                 try:
1843                         self.setSeekState(self.SEEK_STATE_PLAY)
1844                 except:
1845                         pass
1846
1847 class InfoBarCueSheetSupport:
1848         CUT_TYPE_IN = 0
1849         CUT_TYPE_OUT = 1
1850         CUT_TYPE_MARK = 2
1851         CUT_TYPE_LAST = 3
1852
1853         ENABLE_RESUME_SUPPORT = False
1854
1855         def __init__(self, actionmap = "InfobarCueSheetActions"):
1856                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1857                         {
1858                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1859                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1860                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1861                         }, prio=1)
1862
1863                 self.cut_list = [ ]
1864                 self.is_closing = False
1865                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1866                         {
1867                                 iPlayableService.evStart: self.__serviceStarted,
1868                         })
1869
1870         def __serviceStarted(self):
1871                 if self.is_closing:
1872                         return
1873                 print "new service started! trying to download cuts!"
1874                 self.downloadCuesheet()
1875
1876                 if self.ENABLE_RESUME_SUPPORT:
1877                         last = None
1878
1879                         for (pts, what) in self.cut_list:
1880                                 if what == self.CUT_TYPE_LAST:
1881                                         last = pts
1882
1883                         if last is not None:
1884                                 self.resume_point = last
1885                                 if config.usage.on_movie_start.value == "ask":
1886                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1887                                 elif config.usage.on_movie_start.value == "resume":
1888 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1889 # TRANSLATORS: at the start of a movie, when the user has selected
1890 # TRANSLATORS: "Resume from last position" as start behavior.
1891 # TRANSLATORS: The purpose is to notify the user that the movie starts
1892 # TRANSLATORS: in the middle somewhere and not from the beginning.
1893 # TRANSLATORS: (Some translators seem to have interpreted it as a
1894 # TRANSLATORS: question or a choice, but it is a statement.)
1895                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1896
1897         def playLastCB(self, answer):
1898                 if answer == True:
1899                         self.doSeek(self.resume_point)
1900                 self.hideAfterResume()
1901
1902         def hideAfterResume(self):
1903                 if isinstance(self, InfoBarShowHide):
1904                         self.hide()
1905
1906         def __getSeekable(self):
1907                 service = self.session.nav.getCurrentService()
1908                 if service is None:
1909                         return None
1910                 return service.seek()
1911
1912         def cueGetCurrentPosition(self):
1913                 seek = self.__getSeekable()
1914                 if seek is None:
1915                         return None
1916                 r = seek.getPlayPosition()
1917                 if r[0]:
1918                         return None
1919                 return long(r[1])
1920
1921         def cueGetEndCutPosition(self):
1922                 ret = False
1923                 isin = True
1924                 for cp in self.cut_list:
1925                         if cp[1] == self.CUT_TYPE_OUT:
1926                                 if isin:
1927                                         isin = False
1928                                         ret = cp[0]
1929                         elif cp[1] == self.CUT_TYPE_IN:
1930                                 isin = True
1931                 return ret
1932                 
1933         def jumpPreviousNextMark(self, cmp, start=False):
1934                 current_pos = self.cueGetCurrentPosition()
1935                 if current_pos is None:
1936                         return False
1937                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
1938                 if mark is not None:
1939                         pts = mark[0]
1940                 else:
1941                         return False
1942
1943                 self.doSeek(pts)
1944                 return True
1945
1946         def jumpPreviousMark(self):
1947                 # we add 2 seconds, so if the play position is <2s after
1948                 # the mark, the mark before will be used
1949                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
1950
1951         def jumpNextMark(self):
1952                 if not self.jumpPreviousNextMark(lambda x: x):
1953                         self.doSeek(-1)
1954
1955         def getNearestCutPoint(self, pts, cmp=abs, start=False):
1956                 # can be optimized
1957                 beforecut = False
1958                 nearest = None
1959                 if start:
1960                         beforecut = True
1961                         bestdiff = cmp(0 - pts)
1962                         if bestdiff >= 0:
1963                                 nearest = [0, False]
1964                 for cp in self.cut_list:
1965                         if beforecut and cp[1] in [self.CUT_TYPE_IN, self.CUT_TYPE_OUT]:
1966                                 beforecut = False
1967                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
1968                                         diff = cmp(cp[0] - pts)
1969                                         if diff >= 0:
1970                                                 nearest = cp
1971                                                 bestdiff = diff
1972                                         else:
1973                                                 nearest = None
1974                         if cp[1] in [self.CUT_TYPE_MARK, self.CUT_TYPE_LAST]:
1975                                 diff = cmp(cp[0] - pts)
1976                                 if diff >= 0 and (nearest is None or bestdiff > diff):
1977                                         nearest = cp
1978                                         bestdiff = diff
1979                 return nearest
1980
1981         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1982                 current_pos = self.cueGetCurrentPosition()
1983                 if current_pos is None:
1984                         print "not seekable"
1985                         return
1986
1987                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1988
1989                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1990                         if onlyreturn:
1991                                 return nearest_cutpoint
1992                         if not onlyadd:
1993                                 self.removeMark(nearest_cutpoint)
1994                 elif not onlyremove and not onlyreturn:
1995                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1996
1997                 if onlyreturn:
1998                         return None
1999
2000         def addMark(self, point):
2001                 insort(self.cut_list, point)
2002                 self.uploadCuesheet()
2003                 self.showAfterCuesheetOperation()
2004
2005         def removeMark(self, point):
2006                 self.cut_list.remove(point)
2007                 self.uploadCuesheet()
2008                 self.showAfterCuesheetOperation()
2009
2010         def showAfterCuesheetOperation(self):
2011                 if isinstance(self, InfoBarShowHide):
2012                         self.doShow()
2013
2014         def __getCuesheet(self):
2015                 service = self.session.nav.getCurrentService()
2016                 if service is None:
2017                         return None
2018                 return service.cueSheet()
2019
2020         def uploadCuesheet(self):
2021                 cue = self.__getCuesheet()
2022
2023                 if cue is None:
2024                         print "upload failed, no cuesheet interface"
2025                         return
2026                 cue.setCutList(self.cut_list)
2027
2028         def downloadCuesheet(self):
2029                 cue = self.__getCuesheet()
2030
2031                 if cue is None:
2032                         print "download failed, no cuesheet interface"
2033                         self.cut_list = [ ]
2034                 else:
2035                         self.cut_list = cue.getCutList()
2036
2037 class InfoBarSummary(Screen):
2038         skin = """
2039         <screen position="0,0" size="132,64">
2040                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2041                         <convert type="ClockToText">WithSeconds</convert>
2042                 </widget>
2043                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2044                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2045                         <convert type="ConditionalShowHide">Blink</convert>
2046                 </widget>
2047                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2048                         <convert type="ServiceName">Name</convert>
2049                 </widget>
2050                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2051                         <convert type="EventTime">Progress</convert>
2052                 </widget>
2053         </screen>"""
2054
2055 # for picon:  (path="piconlcd" will use LCD picons)
2056 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2057 #                       <convert type="ServiceName">Reference</convert>
2058 #               </widget>
2059
2060         def __init__(self, session, parent):
2061                 Screen.__init__(self, session, parent = parent)
2062
2063 class InfoBarSummarySupport:
2064         def __init__(self):
2065                 pass
2066
2067         def createSummary(self):
2068                 return InfoBarSummary
2069
2070 class InfoBarMoviePlayerSummary(Screen):
2071         skin = """
2072         <screen position="0,0" size="132,64">
2073                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2074                         <convert type="ClockToText">WithSeconds</convert>
2075                 </widget>
2076                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2077                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2078                         <convert type="ConditionalShowHide">Blink</convert>
2079                 </widget>
2080                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2081                         <convert type="ServiceName">Name</convert>
2082                 </widget>
2083                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2084                         <convert type="ServicePosition">Position</convert>
2085                 </widget>
2086         </screen>"""
2087
2088         def __init__(self, session, parent):
2089                 Screen.__init__(self, session)
2090
2091 class InfoBarMoviePlayerSummarySupport:
2092         def __init__(self):
2093                 pass
2094
2095         def createSummary(self):
2096                 return InfoBarMoviePlayerSummary
2097
2098 class InfoBarTeletextPlugin:
2099         def __init__(self):
2100                 self.teletext_plugin = None
2101
2102                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2103                         self.teletext_plugin = p
2104
2105                 if self.teletext_plugin is not None:
2106                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2107                                 {
2108                                         "startTeletext": (self.startTeletext, _("View teletext..."))
2109                                 })
2110                 else:
2111                         print "no teletext plugin found!"
2112
2113         def startTeletext(self):
2114                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2115
2116 class InfoBarSubtitleSupport(object):
2117         def __init__(self):
2118                 object.__init__(self)
2119                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2120                 self.__subtitles_enabled = False
2121
2122                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2123                         {
2124                                 iPlayableService.evEnd: self.__serviceStopped,
2125                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2126                         })
2127                 self.cached_subtitle_checked = False
2128                 self.__selected_subtitle = None
2129
2130         def __serviceStopped(self):
2131                 self.subtitle_window.hide()
2132                 self.__subtitles_enabled = False
2133                 self.cached_subtitle_checked = False
2134
2135         def __updatedInfo(self):
2136                 if not self.cached_subtitle_checked:
2137                         subtitle = self.getCurrentServiceSubtitle()
2138                         self.cached_subtitle_checked = True
2139                         self.__selected_subtitle = subtitle and subtitle.getCachedSubtitle()
2140                         if self.__selected_subtitle:
2141                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2142                                 self.subtitle_window.show()
2143                                 self.__subtitles_enabled = True
2144
2145         def getCurrentServiceSubtitle(self):
2146                 service = self.session.nav.getCurrentService()
2147                 return service and service.subtitle()
2148
2149         def setSubtitlesEnable(self, enable=True):
2150                 subtitle = self.getCurrentServiceSubtitle()
2151                 if enable and self.__selected_subtitle is not None:
2152                         if subtitle and not self.__subtitles_enabled:
2153                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2154                                 self.subtitle_window.show()
2155                                 self.__subtitles_enabled = True
2156                 else:
2157                         if subtitle:
2158                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2159                         self.__subtitles_enabled = False
2160                         self.subtitle_window.hide()
2161
2162         def setSelectedSubtitle(self, subtitle):
2163                 self.__selected_subtitle = subtitle
2164
2165         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2166         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2167
2168 class InfoBarServiceErrorPopupSupport:
2169         def __init__(self):
2170                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2171                         {
2172                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2173                                 iPlayableService.evStart: self.__serviceStarted
2174                         })
2175                 self.__serviceStarted()
2176
2177         def __serviceStarted(self):
2178                 self.last_error = None
2179                 Notifications.RemovePopup(id = "ZapError")
2180
2181         def __tuneFailed(self):
2182                 service = self.session.nav.getCurrentService()
2183                 info = service and service.info()
2184                 error = info and info.getInfo(iServiceInformation.sDVBState)
2185
2186                 if error == self.last_error:
2187                         error = None
2188                 else:
2189                         self.last_error = error
2190
2191                 errors = {
2192                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2193                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2194                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2195                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2196                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2197                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2198                         eDVBServicePMTHandler.eventTuned: None,
2199                         eDVBServicePMTHandler.eventSOF: None,
2200                         eDVBServicePMTHandler.eventEOF: None,
2201                         eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2202                 }
2203
2204                 error = errors.get(error) #this returns None when the key not exist in the dict
2205
2206                 if error is not None:
2207                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2208                 else:
2209                         Notifications.RemovePopup(id = "ZapError")