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