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