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