remove deprecated code (deprecation date was 2008-01 and 2008-02)
[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 #### DEPRECATED CODE ####
1423                 self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1424 #########################
1425
1426         def isInstantRecordRunning(self):
1427                 print "self.recording:", self.recording
1428                 if len(self.recording) > 0:
1429                         for x in self.recording:
1430                                 if x.isRunning():
1431                                         return True
1432                 return False
1433
1434         def recordQuestionCallback(self, answer):
1435                 print "pre:\n", self.recording
1436
1437                 if answer is None or answer[1] == "no":
1438                         return
1439                 list = []
1440                 recording = self.recording[:]
1441                 for x in recording:
1442                         if not x in self.session.nav.RecordTimer.timer_list:
1443                                 self.recording.remove(x)
1444                         elif x.dontSave and x.isRunning():
1445                                 list.append((x, False))
1446
1447                 if answer[1] == "changeduration":
1448                         if len(self.recording) == 1:
1449                                 self.changeDuration(0)
1450                         else:
1451                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1452                 elif answer[1] == "changeendtime":
1453                         if len(self.recording) == 1:
1454                                 self.setEndtime(0)
1455                         else:
1456                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1457                 elif answer[1] == "stop":
1458                         if len(self.recording) == 1:
1459                                 self.stopCurrentRecording(0)
1460                         else:
1461                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1462                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1463                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1464                         if answer[1] == "manualduration":
1465                                 self.changeDuration(len(self.recording)-1)
1466                         elif answer[1] == "manualendtime":
1467                                 self.setEndtime(len(self.recording)-1)
1468                 print "after:\n", self.recording
1469
1470         def setEndtime(self, entry):
1471                 if entry is not None:
1472                         self.selectedEntry = entry
1473                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1474                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1475                         dlg.setTitle(_("Please change recording endtime"))
1476
1477         def TimeDateInputClosed(self, ret):
1478                 if len(ret) > 1:
1479                         if ret[0]:
1480                                 localendtime = localtime(ret[1])
1481                                 print "stopping recording at", strftime("%c", localendtime)
1482                                 self.recording[self.selectedEntry].end = ret[1]
1483                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1484
1485         def changeDuration(self, entry):
1486                 if entry is not None:
1487                         self.selectedEntry = entry
1488                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1489
1490         def inputCallback(self, value):
1491                 if value is not None:
1492                         print "stopping recording after", int(value), "minutes."
1493                         self.recording[self.selectedEntry].end = time() + 60 * int(value)
1494                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1495
1496         def instantRecord(self):
1497                 try:
1498                         stat = os_stat(resolveFilename(SCOPE_HDD))
1499                 except:
1500                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1501                         return
1502
1503                 if self.isInstantRecordRunning():
1504                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1505                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1506                                 list=[(_("stop recording"), "stop"), \
1507                                 (_("change recording (duration)"), "changeduration"), \
1508                                 (_("change recording (endtime)"), "changeendtime"), \
1509                                 (_("add recording (indefinitely)"), "indefinitely"), \
1510                                 (_("add recording (stop after current event)"), "event"), \
1511                                 (_("add recording (enter recording duration)"), "manualduration"), \
1512                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1513                                 (_("do nothing"), "no")])
1514                 else:
1515                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1516                                 title=_("Start recording?"), \
1517                                 list=[(_("add recording (indefinitely)"), "indefinitely"), \
1518                                 (_("add recording (stop after current event)"), "event"), \
1519                                 (_("add recording (enter recording duration)"), "manualduration"), \
1520                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1521                                 (_("don't record"), "no")])
1522
1523 from Tools.ISO639 import LanguageCodes
1524
1525 class InfoBarAudioSelection:
1526         def __init__(self):
1527                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1528                         {
1529                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1530                         })
1531
1532         def audioSelection(self):
1533                 service = self.session.nav.getCurrentService()
1534                 audio = service and service.audioTracks()
1535                 self.audioTracks = audio
1536                 n = audio and audio.getNumberOfTracks() or 0
1537                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1538                 tlist = []
1539                 if n > 0:
1540                         self.audioChannel = service.audioChannel()
1541
1542                         for x in range(n):
1543                                 i = audio.getTrackInfo(x)
1544                                 language = i.getLanguage()
1545                                 description = i.getDescription()
1546
1547                                 if LanguageCodes.has_key(language):
1548                                         language = LanguageCodes[language][0]
1549
1550                                 if len(description):
1551                                         description += " (" + language + ")"
1552                                 else:
1553                                         description = language
1554
1555                                 tlist.append((description, x))
1556
1557                         selectedAudio = audio.getCurrentTrack()
1558                         tlist.sort(key=lambda x: x[0])
1559
1560                         selection = 2
1561                         for x in tlist:
1562                                 if x[1] != selectedAudio:
1563                                         selection += 1
1564                                 else:
1565                                         break
1566
1567                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1568                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1569                 else:
1570                         del self.audioTracks
1571
1572         def audioSelected(self, audio):
1573                 if audio is not None:
1574                         if isinstance(audio[1], str):
1575                                 if audio[1] == "mode":
1576                                         keys = ["red", "green", "yellow"]
1577                                         selection = self.audioChannel.getCurrentChannel()
1578                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1579                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1580                         else:
1581                                 del self.audioChannel
1582                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1583                                         self.audioTracks.selectTrack(audio[1])
1584                 else:
1585                         del self.audioChannel
1586                 del self.audioTracks
1587
1588         def modeSelected(self, mode):
1589                 if mode is not None:
1590                         self.audioChannel.selectChannel(mode[1])
1591                 del self.audioChannel
1592
1593 class InfoBarSubserviceSelection:
1594         def __init__(self):
1595                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1596                         {
1597                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1598                         })
1599
1600                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1601                         {
1602                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1603                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1604                         }, -1)
1605                 self["SubserviceQuickzapAction"].setEnabled(False)
1606
1607                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1608
1609                 self.bsel = None
1610
1611         def checkSubservicesAvail(self, ev):
1612                 if ev == iPlayableService.evUpdatedEventInfo:
1613                         service = self.session.nav.getCurrentService()
1614                         subservices = service and service.subServices()
1615                         if not subservices or subservices.getNumberOfSubservices() == 0:
1616                                 self["SubserviceQuickzapAction"].setEnabled(False)
1617
1618         def nextSubservice(self):
1619                 self.changeSubservice(+1)
1620
1621         def prevSubservice(self):
1622                 self.changeSubservice(-1)
1623
1624         def changeSubservice(self, direction):
1625                 service = self.session.nav.getCurrentService()
1626                 subservices = service and service.subServices()
1627                 n = subservices and subservices.getNumberOfSubservices()
1628                 if n and n > 0:
1629                         selection = -1
1630                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1631                         for x in range(n):
1632                                 if subservices.getSubservice(x).toString() == ref.toString():
1633                                         selection = x
1634                         if selection != -1:
1635                                 selection += direction
1636                                 if selection >= n:
1637                                         selection=0
1638                                 elif selection < 0:
1639                                         selection=n-1
1640                                 newservice = subservices.getSubservice(selection)
1641                                 if newservice.valid():
1642                                         del subservices
1643                                         del service
1644                                         self.session.nav.playService(newservice)
1645
1646         def subserviceSelection(self):
1647                 service = self.session.nav.getCurrentService()
1648                 subservices = service and service.subServices()
1649                 self.bouquets = self.servicelist.getBouquetList()
1650                 n = subservices and subservices.getNumberOfSubservices()
1651                 selection = 0
1652                 if n and n > 0:
1653                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1654                         tlist = []
1655                         for x in range(n):
1656                                 i = subservices.getSubservice(x)
1657                                 if i.toString() == ref.toString():
1658                                         selection = x
1659                                 tlist.append((i.getName(), i))
1660
1661                         if self.bouquets and len(self.bouquets):
1662                                 keys = ["red", "green", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1663                                 if config.usage.multibouquet.value:
1664                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1665                                 else:
1666                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1667                                 selection += 3
1668                         else:
1669                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1670                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1671                                 selection += 2
1672
1673                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
1674
1675         def subserviceSelected(self, service):
1676                 del self.bouquets
1677                 if not service is None:
1678                         if isinstance(service[1], str):
1679                                 if service[1] == "quickzap":
1680                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1681                                         self.session.open(SubservicesQuickzap, service[2])
1682                         else:
1683                                 self["SubserviceQuickzapAction"].setEnabled(True)
1684                                 self.session.nav.playService(service[1])
1685
1686         def addSubserviceToBouquetCallback(self, service):
1687                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1688                         self.selectedSubservice = service
1689                         if self.bouquets is None:
1690                                 cnt = 0
1691                         else:
1692                                 cnt = len(self.bouquets)
1693                         if cnt > 1: # show bouquet list
1694                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1695                         elif cnt == 1: # add to only one existing bouquet
1696                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1697                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1698
1699         def bouquetSelClosed(self, confirmed):
1700                 self.bsel = None
1701                 del self.selectedSubservice
1702                 if confirmed:
1703                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1704
1705         def addSubserviceToBouquet(self, dest):
1706                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1707                 if self.bsel:
1708                         self.bsel.close(True)
1709                 else:
1710                         del self.selectedSubservice
1711
1712 class InfoBarAdditionalInfo:
1713         def __init__(self):
1714
1715                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1716                 self["TimeshiftPossible"] = self["RecordingPossible"]
1717                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1718
1719 class InfoBarNotifications:
1720         def __init__(self):
1721                 self.onExecBegin.append(self.checkNotifications)
1722                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1723                 self.onClose.append(self.__removeNotification)
1724
1725         def __removeNotification(self):
1726                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1727
1728         def checkNotificationsIfExecing(self):
1729                 if self.execing:
1730                         self.checkNotifications()
1731
1732         def checkNotifications(self):
1733                 if len(Notifications.notifications):
1734                         n = Notifications.notifications[0]
1735
1736                         Notifications.notifications = Notifications.notifications[1:]
1737                         cb = n[0]
1738
1739                         if n[3].has_key("onSessionOpenCallback"):
1740                                 n[3]["onSessionOpenCallback"]()
1741                                 del n[3]["onSessionOpenCallback"]
1742
1743                         if cb is not None:
1744                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1745                         else:
1746                                 dlg = self.session.open(n[1], *n[2], **n[3])
1747
1748                         # remember that this notification is currently active
1749                         d = (n[4], dlg)
1750                         Notifications.current_notifications.append(d)
1751                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1752
1753         def __notificationClosed(self, d):
1754                 Notifications.current_notifications.remove(d)
1755
1756 class InfoBarServiceNotifications:
1757         def __init__(self):
1758                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1759                         {
1760                                 iPlayableService.evEnd: self.serviceHasEnded
1761                         })
1762
1763         def serviceHasEnded(self):
1764                 print "service end!"
1765
1766                 try:
1767                         self.setSeekState(self.SEEK_STATE_PLAY)
1768                 except:
1769                         pass
1770
1771 class InfoBarCueSheetSupport:
1772         CUT_TYPE_IN = 0
1773         CUT_TYPE_OUT = 1
1774         CUT_TYPE_MARK = 2
1775         CUT_TYPE_LAST = 3
1776
1777         ENABLE_RESUME_SUPPORT = False
1778
1779         def __init__(self, actionmap = "InfobarCueSheetActions"):
1780                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1781                         {
1782                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1783                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1784                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1785                         }, prio=1)
1786
1787                 self.cut_list = [ ]
1788                 self.is_closing = False
1789                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1790                         {
1791                                 iPlayableService.evStart: self.__serviceStarted,
1792                         })
1793
1794         def __serviceStarted(self):
1795                 if self.is_closing:
1796                         return
1797                 print "new service started! trying to download cuts!"
1798                 self.downloadCuesheet()
1799
1800                 if self.ENABLE_RESUME_SUPPORT:
1801                         last = None
1802
1803                         for (pts, what) in self.cut_list:
1804                                 if what == self.CUT_TYPE_LAST:
1805                                         last = pts
1806
1807                         if last is not None:
1808                                 self.resume_point = last
1809                                 if config.usage.on_movie_start.value == "ask":
1810                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1811                                 elif config.usage.on_movie_start.value == "resume":
1812 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1813 # TRANSLATORS: at the start of a movie, when the user has selected
1814 # TRANSLATORS: "Resume from last position" as start behavior.
1815 # TRANSLATORS: The purpose is to notify the user that the movie starts
1816 # TRANSLATORS: in the middle somewhere and not from the beginning.
1817 # TRANSLATORS: (Some translators seem to have interpreted it as a
1818 # TRANSLATORS: question or a choice, but it is a statement.)
1819                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1820
1821         def playLastCB(self, answer):
1822                 if answer == True:
1823                         self.doSeek(self.resume_point)
1824                 self.hideAfterResume()
1825
1826         def hideAfterResume(self):
1827                 if isinstance(self, InfoBarShowHide):
1828                         self.hide()
1829
1830         def __getSeekable(self):
1831                 service = self.session.nav.getCurrentService()
1832                 if service is None:
1833                         return None
1834                 return service.seek()
1835
1836         def cueGetCurrentPosition(self):
1837                 seek = self.__getSeekable()
1838                 if seek is None:
1839                         return None
1840                 r = seek.getPlayPosition()
1841                 if r[0]:
1842                         return None
1843                 return long(r[1])
1844
1845         def cueGetEndCutPosition(self):
1846                 ret = False
1847                 isin = True
1848                 for cp in self.cut_list:
1849                         if cp[1] == self.CUT_TYPE_OUT:
1850                                 if isin:
1851                                         isin = False
1852                                         ret = cp[0]
1853                         elif cp[1] == self.CUT_TYPE_IN:
1854                                 isin = True
1855                 return ret
1856                 
1857         def jumpPreviousNextMark(self, cmp, start=False):
1858                 current_pos = self.cueGetCurrentPosition()
1859                 if current_pos is None:
1860                         return False
1861                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
1862                 if mark is not None:
1863                         pts = mark[0]
1864                 else:
1865                         return False
1866
1867                 self.doSeek(pts)
1868                 return True
1869
1870         def jumpPreviousMark(self):
1871                 # we add 2 seconds, so if the play position is <2s after
1872                 # the mark, the mark before will be used
1873                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
1874
1875         def jumpNextMark(self):
1876                 if not self.jumpPreviousNextMark(lambda x: x):
1877                         self.doSeek(-1)
1878
1879         def getNearestCutPoint(self, pts, cmp=abs, start=False):
1880                 # can be optimized
1881                 beforecut = False
1882                 nearest = None
1883                 if start:
1884                         beforecut = True
1885                         bestdiff = cmp(0 - pts)
1886                         if bestdiff >= 0:
1887                                 nearest = [0, False]
1888                 for cp in self.cut_list:
1889                         if beforecut and cp[1] in [self.CUT_TYPE_IN, self.CUT_TYPE_OUT]:
1890                                 beforecut = False
1891                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
1892                                         diff = cmp(cp[0] - pts)
1893                                         if diff >= 0:
1894                                                 nearest = cp
1895                                                 bestdiff = diff
1896                                         else:
1897                                                 nearest = None
1898                         if cp[1] in [self.CUT_TYPE_MARK, self.CUT_TYPE_LAST]:
1899                                 diff = cmp(cp[0] - pts)
1900                                 if diff >= 0 and (nearest is None or bestdiff > diff):
1901                                         nearest = cp
1902                                         bestdiff = diff
1903                 return nearest
1904
1905         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1906                 current_pos = self.cueGetCurrentPosition()
1907                 if current_pos is None:
1908                         print "not seekable"
1909                         return
1910
1911                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1912
1913                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1914                         if onlyreturn:
1915                                 return nearest_cutpoint
1916                         if not onlyadd:
1917                                 self.removeMark(nearest_cutpoint)
1918                 elif not onlyremove and not onlyreturn:
1919                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1920
1921                 if onlyreturn:
1922                         return None
1923
1924         def addMark(self, point):
1925                 insort(self.cut_list, point)
1926                 self.uploadCuesheet()
1927                 self.showAfterCuesheetOperation()
1928
1929         def removeMark(self, point):
1930                 self.cut_list.remove(point)
1931                 self.uploadCuesheet()
1932                 self.showAfterCuesheetOperation()
1933
1934         def showAfterCuesheetOperation(self):
1935                 if isinstance(self, InfoBarShowHide):
1936                         self.doShow()
1937
1938         def __getCuesheet(self):
1939                 service = self.session.nav.getCurrentService()
1940                 if service is None:
1941                         return None
1942                 return service.cueSheet()
1943
1944         def uploadCuesheet(self):
1945                 cue = self.__getCuesheet()
1946
1947                 if cue is None:
1948                         print "upload failed, no cuesheet interface"
1949                         return
1950                 cue.setCutList(self.cut_list)
1951
1952         def downloadCuesheet(self):
1953                 cue = self.__getCuesheet()
1954
1955                 if cue is None:
1956                         print "download failed, no cuesheet interface"
1957                         self.cut_list = [ ]
1958                 else:
1959                         self.cut_list = cue.getCutList()
1960
1961 class InfoBarSummary(Screen):
1962         skin = """
1963         <screen position="0,0" size="132,64">
1964                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
1965                         <convert type="ClockToText">WithSeconds</convert>
1966                 </widget>
1967                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
1968                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
1969                         <convert type="ConditionalShowHide">Blink</convert>
1970                 </widget>
1971                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
1972                         <convert type="ServiceName">Name</convert>
1973                 </widget>
1974                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
1975                         <convert type="EventTime">Progress</convert>
1976                 </widget>
1977         </screen>"""
1978
1979 # for picon:  (path="piconlcd" will use LCD picons)
1980 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
1981 #                       <convert type="ServiceName">Reference</convert>
1982 #               </widget>
1983
1984         def __init__(self, session, parent):
1985                 Screen.__init__(self, session, parent = parent)
1986
1987 class InfoBarSummarySupport:
1988         def __init__(self):
1989                 pass
1990
1991         def createSummary(self):
1992                 return InfoBarSummary
1993
1994 class InfoBarMoviePlayerSummary(Screen):
1995         skin = """
1996         <screen position="0,0" size="132,64">
1997                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
1998                         <convert type="ClockToText">WithSeconds</convert>
1999                 </widget>
2000                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2001                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2002                         <convert type="ConditionalShowHide">Blink</convert>
2003                 </widget>
2004                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2005                         <convert type="ServiceName">Name</convert>
2006                 </widget>
2007                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2008                         <convert type="ServicePosition">Position</convert>
2009                 </widget>
2010         </screen>"""
2011
2012         def __init__(self, session, parent):
2013                 Screen.__init__(self, session)
2014
2015 class InfoBarMoviePlayerSummarySupport:
2016         def __init__(self):
2017                 pass
2018
2019         def createSummary(self):
2020                 return InfoBarMoviePlayerSummary
2021
2022 class InfoBarTeletextPlugin:
2023         def __init__(self):
2024                 self.teletext_plugin = None
2025
2026                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2027                         self.teletext_plugin = p
2028
2029                 if self.teletext_plugin is not None:
2030                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2031                                 {
2032                                         "startTeletext": (self.startTeletext, _("View teletext..."))
2033                                 })
2034                 else:
2035                         print "no teletext plugin found!"
2036
2037         def startTeletext(self):
2038                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2039
2040 class InfoBarSubtitleSupport(object):
2041         def __init__(self):
2042                 object.__init__(self)
2043                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2044                 self.__subtitles_enabled = False
2045
2046                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2047                         {
2048                                 iPlayableService.evEnd: self.__serviceStopped,
2049                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2050                         })
2051                 self.cached_subtitle_checked = False
2052                 self.__selected_subtitle = None
2053
2054         def __serviceStopped(self):
2055                 self.subtitle_window.hide()
2056                 self.__subtitles_enabled = False
2057                 self.cached_subtitle_checked = False
2058
2059         def __updatedInfo(self):
2060                 if not self.cached_subtitle_checked:
2061                         subtitle = self.getCurrentServiceSubtitle()
2062                         self.cached_subtitle_checked = True
2063                         self.__selected_subtitle = subtitle and subtitle.getCachedSubtitle()
2064                         if self.__selected_subtitle:
2065                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2066                                 self.subtitle_window.show()
2067                                 self.__subtitles_enabled = True
2068
2069         def getCurrentServiceSubtitle(self):
2070                 service = self.session.nav.getCurrentService()
2071                 return service and service.subtitle()
2072
2073         def setSubtitlesEnable(self, enable=True):
2074                 subtitle = self.getCurrentServiceSubtitle()
2075                 if enable and self.__selected_subtitle is not None:
2076                         if subtitle and not self.__subtitles_enabled:
2077                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2078                                 self.subtitle_window.show()
2079                                 self.__subtitles_enabled = True
2080                 else:
2081                         if subtitle:
2082                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2083                         self.__subtitles_enabled = False
2084                         self.subtitle_window.hide()
2085
2086         def setSelectedSubtitle(self, subtitle):
2087                 self.__selected_subtitle = subtitle
2088
2089         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2090         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2091
2092 class InfoBarServiceErrorPopupSupport:
2093         def __init__(self):
2094                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2095                         {
2096                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2097                                 iPlayableService.evStart: self.__serviceStarted
2098                         })
2099                 self.__serviceStarted()
2100
2101         def __serviceStarted(self):
2102                 self.last_error = None
2103                 Notifications.RemovePopup(id = "ZapError")
2104
2105         def __tuneFailed(self):
2106                 service = self.session.nav.getCurrentService()
2107                 info = service and service.info()
2108                 error = info and info.getInfo(iServiceInformation.sDVBState)
2109
2110                 if error == self.last_error:
2111                         error = None
2112                 else:
2113                         self.last_error = error
2114
2115                 errors = {
2116                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2117                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2118                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2119                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2120                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2121                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2122                         eDVBServicePMTHandler.eventTuned: None,
2123                         eDVBServicePMTHandler.eventSOF: None,
2124                         eDVBServicePMTHandler.eventEOF: None,
2125                         eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2126                 }
2127
2128                 error = errors.get(error) #this returns None when the key not exist in the dict
2129
2130                 if error is not None:
2131                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2132                 else:
2133                         Notifications.RemovePopup(id = "ZapError")