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