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