add support for multiple instant recordings
[vuplus_dvbapp] / 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.Clock import Clock
7 from Components.EventInfo import EventInfo, EventInfoProgress
8 from Components.Harddisk import harddiskmanager
9 from Components.Input import Input
10 from Components.Label import *
11 from Components.Pixmap import Pixmap, PixmapConditional
12 from Components.PluginComponent import plugins
13 from Components.ProgressBar import *
14 from Components.ServiceEventTracker import ServiceEventTracker
15 from Components.ServiceName import ServiceName
16 from Components.config import config, configElement, ConfigSubsection, configSequence, configElementBoolean
17 from Components.config import configfile, configsequencearg
18 from Components.TimerList import TimerEntryComponent
19
20 from EpgSelection import EPGSelection
21 from Plugins.Plugin import PluginDescriptor
22
23 from Screen import Screen
24 from Screens.ChoiceBox import ChoiceBox
25 from Screens.Dish import Dish
26 from Screens.EventView import EventViewEPGSelect, EventViewSimple
27 from Screens.InputBox import InputBox
28 from Screens.MessageBox import MessageBox
29 from Screens.MinuteInput import MinuteInput
30 from Screens.TimerSelection import TimerSelection
31 from ServiceReference import ServiceReference
32
33 from Tools import Notifications
34 from Tools.Directories import *
35
36 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
37 from enigma import *
38
39 import time
40 import os
41 import bisect
42
43 from Components.config import config, currentConfigSelectionElement
44
45 # hack alert!
46 from Menu import MainMenu, mdom
47
48 class InfoBarDish:
49         def __init__(self):
50                 self.dishDialog = self.session.instantiateDialog(Dish)
51                 self.onLayoutFinish.append(self.dishDialog.show)
52
53 class InfoBarShowHide:
54         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
55         fancy animations. """
56         STATE_HIDDEN = 0
57         STATE_HIDING = 1
58         STATE_SHOWING = 2
59         STATE_SHOWN = 3
60         
61         def __init__(self):
62                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
63                         {
64                                 "toggleShow": self.toggleShow,
65                                 "hide": self.hide,
66                         })
67
68                 self.__state = self.STATE_SHOWN
69                 self.__locked = 0
70                 
71                 self.onExecBegin.append(self.show)
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 __onShow(self):
81                 self.__state = self.STATE_SHOWN
82                 self.startHideTimer()
83         
84         def startHideTimer(self):
85                 if self.__state == self.STATE_SHOWN and not self.__locked:
86                         self.hideTimer.start(5000, True)
87
88         def __onHide(self):
89                 self.__state = self.STATE_HIDDEN
90
91         def doShow(self):
92                 self.show()
93                 self.startHideTimer()
94
95         def doTimerHide(self):
96                 self.hideTimer.stop()
97                 if self.__state == self.STATE_SHOWN:
98                         self.hide()
99
100         def toggleShow(self):
101                 if self.__state == self.STATE_SHOWN:
102                         self.hide()
103                         self.hideTimer.stop()
104                 elif self.__state == self.STATE_HIDDEN:
105                         self.show()
106
107         def lockShow(self):
108                 self.__locked = self.__locked + 1
109                 if self.execing:
110                         self.show()
111                         self.hideTimer.stop()
112         
113         def unlockShow(self):
114                 self.__locked = self.__locked - 1
115                 if self.execing:
116                         self.startHideTimer()
117
118 #       def startShow(self):
119 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
120 #               self.__state = self.STATE_SHOWN
121 #       
122 #       def startHide(self):
123 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
124 #               self.__state = self.STATE_HIDDEN
125
126 class NumberZap(Screen):
127         def quit(self):
128                 self.Timer.stop()
129                 self.close(0)
130
131         def keyOK(self):
132                 self.Timer.stop()
133                 self.close(int(self["number"].getText()))
134
135         def keyNumberGlobal(self, number):
136                 self.Timer.start(3000, True)            #reset timer
137                 self.field = self.field + str(number)
138                 self["number"].setText(self.field)
139                 if len(self.field) >= 4:
140                         self.keyOK()
141
142         def __init__(self, session, number):
143                 Screen.__init__(self, session)
144                 self.field = str(number)
145
146                 self["channel"] = Label(_("Channel:"))
147
148                 self["number"] = Label(self.field)
149
150                 self["actions"] = NumberActionMap( [ "SetupActions" ], 
151                         {
152                                 "cancel": self.quit,
153                                 "ok": self.keyOK,
154                                 "1": self.keyNumberGlobal,
155                                 "2": self.keyNumberGlobal,
156                                 "3": self.keyNumberGlobal,
157                                 "4": self.keyNumberGlobal,
158                                 "5": self.keyNumberGlobal,
159                                 "6": self.keyNumberGlobal,
160                                 "7": self.keyNumberGlobal,
161                                 "8": self.keyNumberGlobal,
162                                 "9": self.keyNumberGlobal,
163                                 "0": self.keyNumberGlobal
164                         })
165
166                 self.Timer = eTimer()
167                 self.Timer.timeout.get().append(self.keyOK)
168                 self.Timer.start(3000, True)
169
170 class InfoBarNumberZap:
171         """ Handles an initial number for NumberZapping """
172         def __init__(self):
173                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
174                         {
175                                 "1": self.keyNumberGlobal,
176                                 "2": self.keyNumberGlobal,
177                                 "3": self.keyNumberGlobal,
178                                 "4": self.keyNumberGlobal,
179                                 "5": self.keyNumberGlobal,
180                                 "6": self.keyNumberGlobal,
181                                 "7": self.keyNumberGlobal,
182                                 "8": self.keyNumberGlobal,
183                                 "9": self.keyNumberGlobal,
184                                 "0": self.keyNumberGlobal,
185                         })
186
187         def keyNumberGlobal(self, number):
188 #               print "You pressed number " + str(number)
189                 if number == 0:
190                         self.servicelist.recallPrevService()
191                         self.doShow()
192                 else:
193                         self.session.openWithCallback(self.numberEntered, NumberZap, number)
194
195         def numberEntered(self, retval):
196 #               print self.servicelist
197                 if retval > 0:
198                         self.zapToNumber(retval)
199
200         def searchNumberHelper(self, serviceHandler, num, bouquet):
201                 servicelist = serviceHandler.list(bouquet)
202                 if not servicelist is None:
203                         while num:
204                                 serviceIterator = servicelist.getNext()
205                                 if not serviceIterator.valid(): #check end of list
206                                         break
207                                 if serviceIterator.flags: #assume normal dvb service have no flags set
208                                         continue
209                                 num -= 1;
210                         if not num: #found service with searched number ?
211                                 return serviceIterator, 0
212                 return None, num
213
214         def zapToNumber(self, number):
215                 bouquet = self.servicelist.bouquet_root
216                 service = None
217                 serviceHandler = eServiceCenter.getInstance()
218                 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
219                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
220                 else:
221                         bouquetlist = serviceHandler.list(bouquet)
222                         if not bouquetlist is None:
223                                 while number:
224                                         bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
225                                         if not bouquet.valid(): #check end of list
226                                                 break
227                                         if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
228                                                 continue
229                                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
230                 if not service is None:
231                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
232                                 self.servicelist.clearPath()
233                                 if self.servicelist.bouquet_root != bouquet:
234                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
235                                 self.servicelist.enterPath(bouquet)
236                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
237                         self.servicelist.zap()
238
239 config.misc.initialchannelselection = configElementBoolean("config.misc.initialchannelselection", 1);
240
241 class InfoBarChannelSelection:
242         """ ChannelSelection - handles the channelSelection dialog and the initial 
243         channelChange actions which open the channelSelection dialog """
244         def __init__(self):
245                 #instantiate forever
246                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
247                 
248                 if config.misc.initialchannelselection.value == 1:
249                         self.onShown.append(self.firstRun)
250
251                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
252                         {
253                                 "switchChannelUp": self.switchChannelUp,
254                                 "switchChannelDown": self.switchChannelDown,
255                                 "zapUp": (self.zapUp, _("previous channel")),
256                                 "zapDown": (self.zapDown, _("next channel")),
257                                 "historyBack": (self.historyBack, _("previous channel in history")),
258                                 "historyNext": (self.historyNext, _("next channel in history"))
259                         })
260
261         def firstRun(self):
262                 self.onShown.remove(self.firstRun)
263                 config.misc.initialchannelselection.value = 0
264                 config.misc.initialchannelselection.save()
265                 self.switchChannelDown()
266                 
267         def historyBack(self):
268                 self.servicelist.historyBack()
269
270         def historyNext(self):
271                 self.servicelist.historyNext()
272
273         def switchChannelUp(self):
274                 self.servicelist.moveUp()
275                 self.session.execDialog(self.servicelist)
276
277         def switchChannelDown(self):
278                 self.servicelist.moveDown()
279                 self.session.execDialog(self.servicelist)
280
281         def zapUp(self):
282                 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
283                         if self.servicelist.inBouquet() and self.servicelist.atBegin():
284                                 self.servicelist.prevBouquet()
285                 self.servicelist.moveUp()
286                 self.servicelist.zap()
287                 self.doShow()
288
289         def zapDown(self):
290                 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
291                         self.servicelist.nextBouquet()
292                 else:
293                         self.servicelist.moveDown()
294                 self.servicelist.zap()
295                 self.doShow()
296
297 class InfoBarMenu:
298         """ Handles a menu action, to open the (main) menu """
299         def __init__(self):
300                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions", 
301                         {
302                                 "mainMenu": (self.mainMenu, "Enter main menu..."),
303                         })
304
305         def mainMenu(self):
306                 print "loading mainmenu XML..."
307                 menu = mdom.childNodes[0]
308                 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
309                 self.session.open(MainMenu, menu, menu.childNodes)
310
311 class InfoBarSimpleEventView:
312         """ Opens the Eventview for now/next """
313         def __init__(self):
314                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
315                         {
316                                 "showEventInfo": (self.openEventView, _("show event details")),
317                         })
318
319         def openEventView(self):
320                 self.epglist = [ ]
321                 service = self.session.nav.getCurrentService()
322                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
323                 info = service.info()
324                 ptr=info.getEvent(0)
325                 if ptr:
326                         self.epglist.append(ptr)
327                 ptr=info.getEvent(1)
328                 if ptr:
329                         self.epglist.append(ptr)
330                 if len(self.epglist) > 0:
331                         self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
332
333         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
334                 if len(self.epglist) > 1:
335                         tmp = self.epglist[0]
336                         self.epglist[0]=self.epglist[1]
337                         self.epglist[1]=tmp
338                         setEvent(self.epglist[0])
339
340 class InfoBarEPG:
341         """ EPG - Opens an EPG list when the showEPGList action fires """
342         def __init__(self):
343                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
344                         {
345                                 "showEventInfo": (self.openEventView, _("show EPG...")),
346                         })
347
348         def zapToService(self, service):
349                 if not service is None:
350                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
351                                 self.servicelist.clearPath()
352                                 if self.servicelist.bouquet_root != self.epg_bouquet:
353                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
354                                 self.servicelist.enterPath(self.epg_bouquet)
355                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
356                         self.servicelist.zap()
357
358         def openBouquetEPG(self, bouquet, withCallback=True):
359                 ptr=eEPGCache.getInstance()
360                 services = [ ]
361                 servicelist = eServiceCenter.getInstance().list(bouquet)
362                 if not servicelist is None:
363                         while True:
364                                 service = servicelist.getNext()
365                                 if not service.valid(): #check if end of list
366                                         break
367                                 if service.flags: #ignore non playable services
368                                         continue
369                                 services.append(ServiceReference(service))
370                 if len(services):
371                         self.epg_bouquet = bouquet
372                         if withCallback:
373                                 self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
374                         else:
375                                 self.session.open(EPGSelection, services, self.zapToService)
376
377         def closed(self, ret):
378                 if ret:
379                         self.close(ret)
380
381         def openMultiServiceEPG(self, withCallback=True):
382                 bouquets = self.servicelist.getBouquetList()
383                 if bouquets is None:
384                         cnt = 0
385                 else:
386                         cnt = len(bouquets)
387                 if cnt > 1: # show bouquet list
388                         if withCallback:
389                                 self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
390                         else:
391                                 self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
392                 elif cnt == 1: 
393                         self.openBouquetEPG(bouquets[0][1], withCallback)
394
395         def openSingleServiceEPG(self):
396                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
397                 ptr=eEPGCache.getInstance()
398                 self.session.openWithCallback(self.closed, EPGSelection, ref)
399
400         def openEventView(self):
401                 self.epglist = [ ]
402                 service = self.session.nav.getCurrentService()
403                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
404                 info = service.info()
405                 ptr=info.getEvent(0)
406                 if ptr:
407                         self.epglist.append(ptr)
408                 ptr=info.getEvent(1)
409                 if ptr:
410                         self.epglist.append(ptr)
411                 if len(self.epglist) == 0:
412                         epg = eEPGCache.getInstance()
413                         ptr = epg.lookupEventTime(ref, -1)
414                         if ptr:
415                                 self.epglist.append(ptr)
416                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
417                                 if ptr:
418                                         self.epglist.append(ptr)
419                 if len(self.epglist) > 0:
420                         self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
421                 else:
422                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
423                         self.openMultiServiceEPG(False)
424
425         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
426                 if len(self.epglist) > 1:
427                         tmp = self.epglist[0]
428                         self.epglist[0]=self.epglist[1]
429                         self.epglist[1]=tmp
430                         setEvent(self.epglist[0])
431
432 from math import log
433
434 class InfoBarTuner:
435         """provides a snr/agc/ber display"""
436         def __init__(self):
437                 self["snr"] = Label()
438                 self["agc"] = Label()
439                 self["ber"] = Label()
440                 self["snr_percent"] = Label()
441                 self["agc_percent"] = Label()
442                 self["ber_count"] = Label()
443                 self["snr_progress"] = ProgressBar()
444                 self["agc_progress"] = ProgressBar()
445                 self["ber_progress"] = ProgressBar()
446                 self.timer = eTimer()
447                 self.timer.timeout.get().append(self.updateTunerInfo)
448                 self.timer.start(1000)
449
450         def calc(self,val):
451                 if not val:
452                         return 0
453                 if val < 2500:
454                         return (long)(log(val)/log(2))
455                 return val*100/65535
456
457         def updateTunerInfo(self):
458                 if self.instance.isVisible():
459                         service = self.session.nav.getCurrentService()
460                         snr=0
461                         agc=0
462                         ber=0
463                         if service is not None:
464                                 feinfo = service.frontendStatusInfo()
465                                 if feinfo is not None:
466                                         ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
467                                         snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
468                                         agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
469                         self["snr_percent"].setText("%d%%"%(snr))
470                         self["agc_percent"].setText("%d%%"%(agc))
471                         self["ber_count"].setText("%d"%(ber))
472                         self["snr_progress"].setValue(snr)
473                         self["agc_progress"].setValue(agc)
474                         self["ber_progress"].setValue(self.calc(ber))
475
476 class InfoBarEvent:
477         """provides a current/next event info display"""
478         def __init__(self):
479                 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
480                 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
481                                 
482                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
483                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
484
485                 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
486                 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
487
488                 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
489
490 class InfoBarServiceName:
491         def __init__(self):
492                 self["ServiceName"] = ServiceName(self.session.nav)
493
494 class InfoBarSeek:
495         """handles actions like seeking, pause"""
496         
497         # ispause, isff, issm
498         SEEK_STATE_PLAY = (0, 0, 0, ">")
499         SEEK_STATE_PAUSE = (1, 0, 0, "||")
500         SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
501         SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
502         SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
503         SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
504         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
505         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
506         
507         SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
508         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
509         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
510         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
511         
512         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
513         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
514         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
515         
516         def __init__(self):
517                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
518                         {
519                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
520                                 iPlayableService.evStart: self.__serviceStarted,
521                                 
522                                 iPlayableService.evEOF: self.__evEOF,
523                                 iPlayableService.evSOF: self.__evSOF,
524                         })
525
526                 class InfoBarSeekActionMap(HelpableActionMap):
527                         def __init__(self, screen, *args, **kwargs):
528                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
529                                 self.screen = screen
530                                 
531                         def action(self, contexts, action):
532                                 if action[:5] == "seek:":
533                                         time = int(action[5:])
534                                         self.screen.seekRelative(time * 90000)
535                                         return 1
536                                 else:
537                                         return HelpableActionMap.action(self, contexts, action)
538
539                 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
540                         {
541                                 "pauseService": (self.pauseService, "pause"),
542                                 "unPauseService": (self.unPauseService, "continue"),
543                                 
544                                 "seekFwd": (self.seekFwd, "skip forward"),
545                                 "seekFwdDown": self.seekFwdDown,
546                                 "seekFwdUp": self.seekFwdUp,
547                                 "seekBack": (self.seekBack, "skip backward"),
548                                 "seekBackDown": self.seekBackDown,
549                                 "seekBackUp": self.seekBackUp,
550                         }, prio=-1)
551                         # give them a little more priority to win over color buttons
552
553                 self.seekstate = self.SEEK_STATE_PLAY
554                 self.onClose.append(self.delTimer)
555                 
556                 self.fwdtimer = False
557                 self.fwdKeyTimer = eTimer()
558                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
559
560                 self.rwdtimer = False
561                 self.rwdKeyTimer = eTimer()
562                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
563                 
564                 self.onPlayStateChanged = [ ]
565                 
566                 self.lockedBecauseOfSkipping = False
567         
568         def up(self):
569                 pass
570         
571         def down(self):
572                 pass
573         
574         def delTimer(self):
575                 del self.fwdKeyTimer
576                 del self.rwdKeyTimer
577         
578         def getSeek(self):
579                 service = self.session.nav.getCurrentService()
580                 if service is None:
581                         return False
582
583                 seek = service.seek()
584
585                 if seek is None or not seek.isCurrentlySeekable():
586                         return None
587                 
588                 return seek
589         
590         def isSeekable(self):
591                 if self.getSeek() is None:
592                         return False
593                 return True
594
595         def __seekableStatusChanged(self):
596                 print "seekable status changed!"
597                 if not self.isSeekable():
598                         self["SeekActions"].setEnabled(False)
599                         print "not seekable, return to play"
600                         self.setSeekState(self.SEEK_STATE_PLAY)
601                 else:
602                         self["SeekActions"].setEnabled(True)
603                         print "seekable"
604
605         def __serviceStarted(self):
606                 self.seekstate = self.SEEK_STATE_PLAY
607
608         def setSeekState(self, state):
609                 service = self.session.nav.getCurrentService()
610                 
611                 if service is None:
612                         return False
613                 
614                 if not self.isSeekable():
615                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
616                                 state = self.SEEK_STATE_PLAY
617                 
618                 pauseable = service.pause()
619
620                 if pauseable is None:
621                         print "not pauseable."
622                         state = self.SEEK_STATE_PLAY
623                 
624                 oldstate = self.seekstate
625                 self.seekstate = state
626                 
627                 for i in range(3):
628                         if oldstate[i] != self.seekstate[i]:
629                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
630
631                 for c in self.onPlayStateChanged:
632                         c(self.seekstate)
633                 
634                 self.checkSkipShowHideLock()
635
636                 return True
637
638         def pauseService(self):
639                 if self.seekstate == self.SEEK_STATE_PAUSE:
640                         print "pause, but in fact unpause"
641                         self.unPauseService()
642                 else:
643                         if self.seekstate == self.SEEK_STATE_PLAY:
644                                 print "yes, playing."
645                         else:
646                                 print "no", self.seekstate
647                         print "pause"
648                         self.setSeekState(self.SEEK_STATE_PAUSE);
649                 
650         def unPauseService(self):
651                 print "unpause"
652                 self.setSeekState(self.SEEK_STATE_PLAY);
653         
654         def doSeek(self, seektime):
655                 print "doseek", seektime
656                 service = self.session.nav.getCurrentService()
657                 if service is None:
658                         return
659                 
660                 seekable = self.getSeek()
661                 if seekable is None:
662                         return
663                 
664                 seekable.seekTo(90 * seektime)
665
666         def seekFwdDown(self):
667                 print "start fwd timer"
668                 self.fwdtimer = True
669                 self.fwdKeyTimer.start(1000)
670
671         def seekBackDown(self):
672                 print "start rewind timer"
673                 self.rwdtimer = True
674                 self.rwdKeyTimer.start(1000)
675
676         def seekFwdUp(self):
677                 print "seekFwdUp"
678                 if self.fwdtimer:
679                         self.fwdKeyTimer.stop()
680                         self.fwdtimer = False
681                         self.seekFwd()
682
683         def seekFwd(self):
684                 lookup = {
685                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
686                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
687                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
688                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
689                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
690                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
691                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
692                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
693                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
694                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
695                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
696                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
697                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
698                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
699                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
700                         }
701                 self.setSeekState(lookup[self.seekstate])
702         
703         def seekBackUp(self):
704                 print "seekBackUp"
705                 if self.rwdtimer:
706                         self.rwdKeyTimer.stop()
707                         self.rwdtimer = False
708                         self.seekBack()
709                 
710         def seekBack(self):
711                 lookup = {
712                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
713                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
714                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
715                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
716                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
717                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
718                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
719                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
720                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
721                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
722                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
723                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
724                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
725                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
726                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
727                         }
728                 self.setSeekState(lookup[self.seekstate])
729                 
730                 if self.seekstate == self.SEEK_STATE_PAUSE:
731                         seekable = self.getSeek()
732                         if seekable is not None:
733                                 seekable.seekRelative(-1, 3)
734
735         def fwdTimerFire(self):
736                 print "Display seek fwd"
737                 self.fwdKeyTimer.stop()
738                 self.fwdtimer = False
739                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
740                 
741         def fwdSeekTo(self, minutes):
742                 print "Seek", minutes, "minutes forward"
743                 if minutes != 0:
744                         seekable = self.getSeek()
745                         if seekable is not None:
746                                 seekable.seekRelative(1, minutes * 60 * 90000)
747         
748         def rwdTimerFire(self):
749                 print "rwdTimerFire"
750                 self.rwdKeyTimer.stop()
751                 self.rwdtimer = False
752                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
753         
754         def rwdSeekTo(self, minutes):
755                 print "rwdSeekTo"
756                 self.fwdSeekTo(0 - minutes)
757         
758         def checkSkipShowHideLock(self):
759                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
760                 
761                 if self.lockedBecauseOfSkipping and not wantlock:
762                         self.unlockShow()
763                         self.lockedBecauseOfSkipping = False
764                 
765                 if wantlock and not self.lockedBecauseOfSkipping:
766                         self.lockShow()
767                         self.lockedBecauseOfSkipping = True
768
769         def __evEOF(self):
770                 if self.seekstate != self.SEEK_STATE_PLAY:
771                         self.setSeekState(self.SEEK_STATE_PAUSE)
772                         # HACK
773                         #self.getSeek().seekRelative(1, -90000)
774                         self.setSeekState(self.SEEK_STATE_PLAY)
775                 else:
776                         self.setSeekState(self.SEEK_STATE_PAUSE)
777         
778         def __evSOF(self):
779                 self.setSeekState(self.SEEK_STATE_PLAY)
780                 self.doSeek(0)
781
782         def seekRelative(self, diff):
783                 seekable = self.getSeek()
784                 if seekable is not None:
785                         seekable.seekRelative(1, diff)
786
787 from Screens.PVRState import PVRState, TimeshiftState
788
789 class InfoBarPVRState:
790         def __init__(self, screen=PVRState):
791                 self.onPlayStateChanged.append(self.__playStateChanged)
792                 self.pvrStateDialog = self.session.instantiateDialog(screen)
793                 self.onShow.append(self.__mayShow)
794                 self.onHide.append(self.pvrStateDialog.hide)
795         
796         def __mayShow(self):
797                 if self.seekstate != self.SEEK_STATE_PLAY and self.execing:
798                         self.pvrStateDialog.show()
799
800         def __playStateChanged(self, state):
801                 playstateString = state[3]
802                 self.pvrStateDialog["state"].setText(playstateString)
803                 self.__mayShow()
804
805 class InfoBarTimeshiftState(InfoBarPVRState):
806         def __init__(self):
807                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
808
809
810 class InfoBarShowMovies:
811
812         # i don't really like this class. 
813         # it calls a not further specified "movie list" on up/down/movieList,
814         # so this is not more than an action map
815         def __init__(self):
816                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
817                         {
818                                 "movieList": (self.showMovies, "movie list"),
819                                 "up": (self.showMovies, "movie list"),
820                                 "down": (self.showMovies, "movie list")
821                         })
822
823 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
824
825 # Hrmf.
826 #
827 # Timeshift works the following way:
828 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
829 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
830 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
831 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
832 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
833 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
834 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
835 #
836
837 # in other words:
838 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
839 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
840 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
841 # - the user can now PVR around
842 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
843 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
844 # after!
845 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
846 # - if the user rewinds, or press pause, timeshift will be activated again
847
848 # note that a timeshift can be enabled ("recording") and
849 # activated (currently time-shifting).
850
851 class InfoBarTimeshift:
852         def __init__(self):
853                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
854                         {
855                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
856                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
857                         }, prio=1)
858                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
859                         {
860                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
861                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
862                         }, prio=-1) # priority over record
863
864                 self.timeshift_enabled = 0
865                 self.timeshift_state = 0
866                 self.ts_pause_timer = eTimer()
867                 self.ts_pause_timer.timeout.get().append(self.pauseService)
868
869                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
870                         {
871                                 iPlayableService.evStart: self.__serviceStarted,
872                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
873                         })
874         
875         def getTimeshift(self):
876                 service = self.session.nav.getCurrentService()
877                 return service.timeshift()
878
879         def startTimeshift(self):
880                 print "enable timeshift"
881                 ts = self.getTimeshift()
882                 if ts is None:
883                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
884                         print "no ts interface"
885                         return
886                 
887                 if self.timeshift_enabled:
888                         print "hu, timeshift already enabled?"
889                 else:
890                         if not ts.startTimeshift():
891                                 import time
892                                 self.timeshift_enabled = 1
893                                 self.pvrStateDialog["timeshift"].setRelative(time.time())
894                                 
895                                 # PAUSE.
896                                 self.setSeekState(self.SEEK_STATE_PAUSE)
897                                 
898                                 # enable the "TimeshiftEnableActions", which will override
899                                 # the startTimeshift actions
900                                 self.__seekableStatusChanged()
901                         else:
902                                 print "timeshift failed"
903
904         def stopTimeshift(self):
905                 if not self.timeshift_enabled:
906                         return
907                 print "disable timeshift"
908                 ts = self.getTimeshift()
909                 if ts is None:
910                         return
911                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
912
913         def stopTimeshiftConfirmed(self, confirmed):
914                 if not confirmed:
915                         return
916
917                 ts = self.getTimeshift()
918                 if ts is None:
919                         return
920
921                 ts.stopTimeshift()
922                 self.timeshift_enabled = 0
923
924                 # disable actions
925                 self.__seekableStatusChanged()
926         
927         # activates timeshift, and seeks to (almost) the end
928         def activateTimeshiftEnd(self):
929                 ts = self.getTimeshift()
930                 
931                 if ts is None:
932                         return
933                 
934                 if ts.isTimeshiftActive():
935                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
936                         self.pauseService()
937                 else:
938                         self.setSeekState(self.SEEK_STATE_PLAY)
939                         ts.activateTimeshift()
940                         self.seekRelative(0)
941         
942         # same as activateTimeshiftEnd, but pauses afterwards.
943         def activateTimeshiftEndAndPause(self):
944                 state = self.seekstate
945                 self.activateTimeshiftEnd()
946                 
947                 # well, this is "andPause", but it could be pressed from pause,
948                 # when pausing on the (fake-)"live" picture, so an un-pause
949                 # is perfectly ok.
950                 
951                 print "now, pauseService"
952                 if state == self.SEEK_STATE_PLAY:
953                         print "is PLAYING, start pause timer"
954                         self.ts_pause_timer.start(200, 1)
955                 else:
956                         print "unpause"
957                         self.unPauseService()
958         
959         def __seekableStatusChanged(self):
960                 enabled = False
961                 
962                 print "self.isSeekable", self.isSeekable()
963                 print "self.timeshift_enabled", self.timeshift_enabled
964                 
965                 # when this service is not seekable, but timeshift
966                 # is enabled, this means we can activate
967                 # the timeshift
968                 if not self.isSeekable() and self.timeshift_enabled:
969                         enabled = True
970
971                 print "timeshift activate:", enabled
972                 self["TimeshiftActivateActions"].setEnabled(enabled)
973
974         def __serviceStarted(self):
975                 self.timeshift_enabled = False
976                 self.__seekableStatusChanged()
977
978 from RecordTimer import parseEvent
979
980 class InfoBarInstantRecord:
981         """Instant Record - handles the instantRecord action in order to 
982         start/stop instant records"""
983         def __init__(self):
984                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
985                         {
986                                 "instantRecord": (self.instantRecord, "Instant Record..."),
987                         })
988                 self.recording = []
989                 self["BlinkingPoint"] = BlinkingPixmapConditional()
990                 self["BlinkingPoint"].hide()
991                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
992
993         def stopCurrentRecording(self, entry = -1):     
994                 if entry is not None and entry != -1:
995                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
996                         self.recording.remove(self.recording[entry])
997                         
998         def startInstantRecording(self, limitEvent = False):
999                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1000                 
1001                 # try to get event info
1002                 event = None
1003                 try:
1004                         service = self.session.nav.getCurrentService()
1005                         epg = eEPGCache.getInstance()
1006                         event = epg.lookupEventTime(serviceref, -1, 0)
1007                         if event is None:
1008                                 info = service.info()
1009                                 ev = info.getEvent(0)
1010                                 event = ev
1011                 except:
1012                         pass
1013
1014                 begin = time.time()
1015                 end = time.time() + 3600 * 10
1016                 name = "instant record"
1017                 description = ""
1018                 eventid = None
1019                 
1020                 if event is not None:
1021                         curEvent = parseEvent(event)
1022                         name = curEvent[2]
1023                         description = curEvent[3]
1024                         eventid = curEvent[4]
1025                         if limitEvent:
1026                                 end = curEvent[1]
1027                 else:
1028                         if limitEvent:
1029                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1030                                 
1031                 data = (begin, end, name, description, eventid)
1032                 
1033                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1034                 recording.dontSave = True
1035                 self.recording.append(recording)
1036                 
1037                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1038                 
1039         def isInstantRecordRunning(self):
1040                 print "self.recording:", self.recording
1041                 if len(self.recording) > 0:
1042                         for x in self.recording:
1043                                 if x.isRunning():
1044                                         return True
1045                 return False
1046
1047         def recordQuestionCallback(self, answer):
1048                 if answer is None or answer[1] == "no":
1049                         return
1050                 list = []
1051                 for x in self.recording:
1052                         if x.dontSave:
1053                                 list.append(TimerEntryComponent(x, False))              
1054
1055                 if answer[1] == "changeduration":
1056                         if len(self.recording) == 1:
1057                                 self.changeDuration(0)
1058                         else:
1059                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1060                 elif answer[1] == "stop":
1061                         if len(self.recording) == 1:
1062                                 self.stopCurrentRecording(0)
1063                         else:
1064                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1065                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1066                         limitEvent = False
1067                         if answer[1] == "event":
1068                                 limitEvent = True
1069                         if answer[1] == "manualduration":
1070                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1071                         self.startInstantRecording(limitEvent = limitEvent)
1072
1073         def changeDuration(self, entry):
1074                 if entry is not None:
1075                         self.selectedEntry = entry
1076                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1077
1078         def inputCallback(self, value):
1079                 if value is not None:
1080                         print "stopping recording after", int(value), "minutes."
1081                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1082                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1083
1084         def instantRecord(self):
1085                 try:
1086                         stat = os.stat(resolveFilename(SCOPE_HDD))
1087                 except:
1088                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1089                         return
1090         
1091                 if self.isInstantRecordRunning():
1092                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("A recording is currently running.\nWhat do you want to do?"), list=[(_("stop recording"), "stop"), (_("change recording (duration)"), "changeduration"), (_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"), (_("do nothing"), "no")])
1093                 else:
1094                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Start recording?"), list=[(_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"),(_("don't record"), "no")])
1095
1096 from Screens.AudioSelection import AudioSelection
1097
1098 class InfoBarAudioSelection:
1099         def __init__(self):
1100                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1101                         {
1102                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1103                         })
1104
1105         def audioSelection(self):
1106                 service = self.session.nav.getCurrentService()
1107                 audio = service.audioTracks()
1108                 n = audio.getNumberOfTracks()
1109                 if n > 0:
1110                         self.session.open(AudioSelection, audio)
1111
1112 from Screens.SubserviceSelection import SubserviceSelection
1113
1114 class InfoBarSubserviceSelection:
1115         def __init__(self):
1116                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1117                         {
1118                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1119                         })
1120
1121         def subserviceSelection(self):
1122                 service = self.session.nav.getCurrentService()
1123                 subservices = service.subServices()
1124                 n = subservices.getNumberOfSubservices()
1125                 if n > 0:
1126                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1127
1128         def subserviceSelected(self, service):
1129                 if not service is None:
1130                         self.session.nav.playService(service)
1131
1132 class InfoBarAdditionalInfo:
1133         def __init__(self):
1134                 self["DolbyActive"] = Pixmap()
1135                 self["CryptActive"] = Pixmap()
1136                 self["FormatActive"] = Pixmap()
1137                 
1138                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1139                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1140                 self.onLayoutFinish.append(self["ButtonRed"].update)
1141                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1142                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1143                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1144
1145                 self["ButtonGreen"] = Pixmap()
1146                 self["ButtonGreenText"] = Label(_("Subservices"))
1147
1148                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1149                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1150                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1151                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1152                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1153                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1154
1155                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1156                 self["ButtonBlue"].setConnect(lambda: False)
1157                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1158                 self["ButtonBlueText"].setConnect(lambda: False)
1159                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1160                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1161
1162                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1163
1164         def hideSubServiceIndication(self):
1165                 self["ButtonGreen"].hide()
1166                 self["ButtonGreenText"].hide()
1167
1168         def showSubServiceIndication(self):
1169                 self["ButtonGreen"].show()
1170                 self["ButtonGreenText"].show()
1171
1172         def checkFormat(self, service):
1173                 info = service.info()
1174                 if info is not None:
1175                         aspect = info.getInfo(iServiceInformation.sAspect)
1176                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1177                                 self["FormatActive"].show()
1178                         else:
1179                                 self["FormatActive"].hide()
1180
1181         def checkSubservices(self, service):
1182                 if service.subServices().getNumberOfSubservices() > 0:
1183                         self.showSubServiceIndication()
1184                 else:
1185                         self.hideSubServiceIndication()
1186
1187         def checkDolby(self, service):
1188                 # FIXME
1189                 dolby = False
1190                 audio = service.audioTracks()
1191                 if audio is not None:
1192                         n = audio.getNumberOfTracks()
1193                         for x in range(n):
1194                                 i = audio.getTrackInfo(x)
1195                                 description = i.getDescription();
1196                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1197                                         dolby = True
1198                                         break
1199                 if dolby:
1200                         self["DolbyActive"].show()
1201                 else:
1202                         self["DolbyActive"].hide()
1203
1204         def checkCrypted(self, service):
1205                 info = service.info()
1206                 if info is not None:
1207                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1208                                 self["CryptActive"].show()
1209                         else:
1210                                 self["CryptActive"].hide()
1211
1212         def gotServiceEvent(self, ev):
1213                 service = self.session.nav.getCurrentService()
1214                 if ev == iPlayableService.evUpdatedEventInfo:
1215                         self.checkSubservices(service)
1216                         self.checkFormat(service)
1217                 elif ev == iPlayableService.evUpdatedInfo:
1218                         self.checkCrypted(service)
1219                         self.checkDolby(service)
1220                 elif ev == iPlayableService.evEnd:
1221                         self.hideSubServiceIndication()
1222                         self["CryptActive"].hide()
1223                         self["DolbyActive"].hide()
1224                         self["FormatActive"].hide()
1225
1226 class InfoBarNotifications:
1227         def __init__(self):
1228                 self.onExecBegin.append(self.checkNotifications)
1229                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1230         
1231         def checkNotificationsIfExecing(self):
1232                 if self.execing:
1233                         self.checkNotifications()
1234
1235         def checkNotifications(self):
1236                 if len(Notifications.notifications):
1237                         n = Notifications.notifications[0]
1238                         Notifications.notifications = Notifications.notifications[1:]
1239                         print "open",n
1240                         cb = n[0]
1241                         if cb is not None:
1242                                 self.session.openWithCallback(cb, *n[1:])
1243                         else:
1244                                 self.session.open(*n[1:])
1245
1246 class InfoBarServiceNotifications:
1247         def __init__(self):
1248                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1249                         {
1250                                 iPlayableService.evEnd: self.serviceHasEnded
1251                         })
1252
1253         def serviceHasEnded(self):
1254                 print "service end!"
1255
1256                 try:
1257                         self.setSeekState(self.SEEK_STATE_PLAY)
1258                 except:
1259                         pass
1260
1261 class InfoBarCueSheetSupport:
1262         CUT_TYPE_IN = 0
1263         CUT_TYPE_OUT = 1
1264         CUT_TYPE_MARK = 2
1265         
1266         def __init__(self):
1267                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1268                         {
1269                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1270                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1271                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1272                         }, prio=1) 
1273                 
1274                 self.cut_list = [ ]
1275                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1276                         {
1277                                 iPlayableService.evStart: self.__serviceStarted,
1278                         })
1279
1280         def __serviceStarted(self):
1281                 print "new service started! trying to download cuts!"
1282                 self.downloadCuesheet()
1283
1284         def __getSeekable(self):
1285                 service = self.session.nav.getCurrentService()
1286                 if service is None:
1287                         return None
1288                 return service.seek()
1289
1290         def cueGetCurrentPosition(self):
1291                 seek = self.__getSeekable()
1292                 if seek is None:
1293                         return None
1294                 r = seek.getPlayPosition()
1295                 if r[0]:
1296                         return None
1297                 return long(r[1])
1298
1299         def jumpPreviousNextMark(self, cmp, alternative=None):
1300                 current_pos = self.cueGetCurrentPosition()
1301                 if current_pos is None:
1302                         return
1303                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1304                 if mark is not None:
1305                         pts = mark[0]
1306                 elif alternative is not None:
1307                         pts = alternative
1308                 else:
1309                         return
1310
1311                 seekable = self.__getSeekable()
1312                 if seekable is not None:
1313                         seekable.seekTo(pts)
1314
1315         def jumpPreviousMark(self):
1316                 # we add 2 seconds, so if the play position is <2s after
1317                 # the mark, the mark before will be used
1318                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1319
1320         def jumpNextMark(self):
1321                 self.jumpPreviousNextMark(lambda x: x)
1322
1323         def getNearestCutPoint(self, pts, cmp=abs):
1324                 # can be optimized
1325                 nearest = None
1326                 for cp in self.cut_list:
1327                         diff = cmp(cp[0] - pts)
1328                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1329                                 nearest = cp
1330                 return nearest
1331
1332         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1333                 current_pos = self.cueGetCurrentPosition()
1334                 if current_pos is None:
1335                         print "not seekable"
1336                         return
1337                 
1338                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1339                 
1340                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1341                         if onlyreturn:
1342                                 return nearest_cutpoint
1343                         if not onlyadd:
1344                                 self.removeMark(nearest_cutpoint)
1345                 elif not onlyremove and not onlyreturn:
1346                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1347                 
1348                 if onlyreturn:
1349                         return None
1350
1351         def addMark(self, point):
1352                 bisect.insort(self.cut_list, point)
1353                 self.uploadCuesheet()
1354
1355         def removeMark(self, point):
1356                 self.cut_list.remove(point)
1357                 self.uploadCuesheet()
1358
1359         def __getCuesheet(self):
1360                 service = self.session.nav.getCurrentService()
1361                 if service is None:
1362                         return None
1363                 return service.cueSheet()
1364
1365         def uploadCuesheet(self):
1366                 cue = self.__getCuesheet()
1367
1368                 if cue is None:
1369                         print "upload failed, no cuesheet interface"
1370                         return
1371                 cue.setCutList(self.cut_list)
1372
1373         def downloadCuesheet(self):
1374                 cue = self.__getCuesheet()
1375
1376                 if cue is None:
1377                         print "upload failed, no cuesheet interface"
1378                         return
1379                 self.cut_list = cue.getCutList()
1380
1381 class InfoBarSummary(Screen):
1382         skin = """
1383         <screen position="0,0" size="132,64">
1384                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1385                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1386         </screen>"""
1387
1388         def __init__(self, session, parent):
1389                 Screen.__init__(self, session)
1390                 self["CurrentService"] = ServiceName(self.session.nav)
1391                 self["Clock"] = Clock()
1392
1393 class InfoBarSummarySupport:
1394         def __init__(self):
1395                 pass
1396         
1397         def createSummary(self):
1398                 return InfoBarSummary
1399
1400 class InfoBarTeletextPlugin:
1401         def __init__(self):
1402                 self.teletext_plugin = None
1403                 
1404                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1405                         self.teletext_plugin = p
1406                 
1407                 if self.teletext_plugin is not None:
1408                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1409                                 {
1410                                         "startTeletext": (self.startTeletext, "View teletext...")
1411                                 })
1412                 else:
1413                         print "no teletext plugin found!"
1414
1415         def startTeletext(self):
1416                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())