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