reset state to PLAY when a new service starts
[vuplus_dvbapp] / 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
15 from ServiceReference import ServiceReference
16 from EpgSelection import EPGSelection
17
18 from Screens.MessageBox import MessageBox
19 from Screens.Dish import Dish
20 from Screens.Standby import Standby
21 from Screens.EventView import EventView
22 from Screens.MinuteInput import MinuteInput
23 from Components.Harddisk import harddiskmanager
24
25 from Components.ServiceEventTracker import ServiceEventTracker
26
27 from Tools import Notifications
28 from Tools.Directories import *
29
30 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
31 from enigma import *
32
33 import time
34 import os
35
36 from Components.config import config, currentConfigSelectionElement
37
38 # hack alert!
39 from Menu import MainMenu, mdom
40
41 class InfoBarDish:
42         def __init__(self):
43                 self.dishDialog = self.session.instantiateDialog(Dish)
44                 self.onShown.append(self.dishDialog.instance.show)
45
46 class InfoBarShowHide:
47         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
48         fancy animations. """
49         STATE_HIDDEN = 0
50         STATE_HIDING = 1
51         STATE_SHOWING = 2
52         STATE_SHOWN = 3
53         
54         def __init__(self):
55                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
56                         {
57                                 "toggleShow": self.toggleShow,
58                                 "hide": self.hide,
59                         })
60
61                 self.state = self.STATE_SHOWN
62                 
63                 self.onExecBegin.append(self.show)
64                 self.onClose.append(self.delHideTimer)
65                 
66                 self.hideTimer = eTimer()
67                 self.hideTimer.timeout.get().append(self.doTimerHide)
68                 self.hideTimer.start(5000, True)
69
70         def delHideTimer(self):
71                 del self.hideTimer
72
73         def hide(self): 
74                 self.instance.hide()
75                 
76         def show(self):
77                 self.state = self.STATE_SHOWN
78                 self.hideTimer.start(5000, True)
79
80         def doTimerHide(self):
81                 self.hideTimer.stop()
82                 if self.state == self.STATE_SHOWN:
83                         self.instance.hide()
84                         self.state = self.STATE_HIDDEN
85
86         def toggleShow(self):
87                 if self.state == self.STATE_SHOWN:
88                         self.instance.hide()
89                         #pls check animation support, sorry
90 #                       self.startHide()
91                         self.hideTimer.stop()
92                         self.state = self.STATE_HIDDEN
93                 elif self.state == self.STATE_HIDDEN:
94                         self.instance.show()
95                         self.show()
96                         
97         def startShow(self):
98                 self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
99                 self.state = self.STATE_SHOWN
100         
101         def startHide(self):
102                 self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
103                 self.state = self.STATE_HIDDEN
104
105 class NumberZap(Screen):
106         def quit(self):
107                 self.Timer.stop()
108                 self.close(0)
109
110         def keyOK(self):
111                 self.Timer.stop()
112                 self.close(int(self["number"].getText()))
113
114         def keyNumberGlobal(self, number):
115                 self.Timer.start(3000, True)            #reset timer
116                 self.field = self.field + str(number)
117                 self["number"].setText(self.field)
118                 if len(self.field) >= 4:
119                         self.keyOK()
120
121         def __init__(self, session, number):
122                 Screen.__init__(self, session)
123                 self.field = str(number)
124
125                 self["channel"] = Label(_("Channel:"))
126
127                 self["number"] = Label(self.field)
128
129                 self["actions"] = NumberActionMap( [ "SetupActions" ], 
130                         {
131                                 "cancel": self.quit,
132                                 "ok": self.keyOK,
133                                 "1": self.keyNumberGlobal,
134                                 "2": self.keyNumberGlobal,
135                                 "3": self.keyNumberGlobal,
136                                 "4": self.keyNumberGlobal,
137                                 "5": self.keyNumberGlobal,
138                                 "6": self.keyNumberGlobal,
139                                 "7": self.keyNumberGlobal,
140                                 "8": self.keyNumberGlobal,
141                                 "9": self.keyNumberGlobal,
142                                 "0": self.keyNumberGlobal
143                         })
144
145                 self.Timer = eTimer()
146                 self.Timer.timeout.get().append(self.keyOK)
147                 self.Timer.start(3000, True)
148
149 class InfoBarPowerKey:
150         """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
151         
152         def __init__(self):
153                 self.powerKeyTimer = eTimer()
154                 self.powerKeyTimer.timeout.get().append(self.powertimer)
155                 self["PowerKeyActions"] = HelpableActionMap(self, "PowerKeyActions",
156                         {
157                                 "powerdown": self.powerdown,
158                                 "powerup": self.powerup,
159                                 "discreteStandby": (self.standby, "Go standby"),
160                                 "discretePowerOff": (self.quit, "Go to deep standby"),
161                         })
162
163         def powertimer(self):   
164                 print "PowerOff - Now!"
165                 self.quit()
166         
167         def powerdown(self):
168                 self.standbyblocked = 0
169                 self.powerKeyTimer.start(3000, True)
170
171         def powerup(self):
172                 self.powerKeyTimer.stop()
173                 if self.standbyblocked == 0:
174                         self.standbyblocked = 1
175                         self.standby()
176
177         def standby(self):
178                 self.session.open(Standby, self)
179
180         def quit(self):
181                 # halt
182                 quitMainloop(1)
183
184 class InfoBarNumberZap:
185         """ Handles an initial number for NumberZapping """
186         def __init__(self):
187                 self["NumberZapActions"] = NumberActionMap( [ "NumberZapActions"],
188                         {
189                                 "1": self.keyNumberGlobal,
190                                 "2": self.keyNumberGlobal,
191                                 "3": self.keyNumberGlobal,
192                                 "4": self.keyNumberGlobal,
193                                 "5": self.keyNumberGlobal,
194                                 "6": self.keyNumberGlobal,
195                                 "7": self.keyNumberGlobal,
196                                 "8": self.keyNumberGlobal,
197                                 "9": self.keyNumberGlobal,
198                                 "0": self.keyNumberGlobal,
199                         })
200
201         def keyNumberGlobal(self, number):
202 #               print "You pressed number " + str(number)
203                 if number == 0:
204                         self.servicelist.recallPrevService()
205                         self.instance.show()
206                         self.show()
207                 else:
208                         self.session.openWithCallback(self.numberEntered, NumberZap, number)
209
210         def numberEntered(self, retval):
211 #               print self.servicelist
212                 if retval > 0:
213                         self.zapToNumber(retval)
214
215         def searchNumberHelper(self, serviceHandler, num, bouquet):
216                 servicelist = serviceHandler.list(bouquet)
217                 if not servicelist is None:
218                         while num:
219                                 serviceIterator = servicelist.getNext()
220                                 if not serviceIterator.valid(): #check end of list
221                                         break
222                                 if serviceIterator.flags: #assume normal dvb service have no flags set
223                                         continue
224                                 num -= 1;
225                         if not num: #found service with searched number ?
226                                 return serviceIterator, 0
227                 return None, num
228
229         def zapToNumber(self, number):
230                 bouquet = self.servicelist.bouquet_root
231                 service = None
232                 serviceHandler = eServiceCenter.getInstance()
233                 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
234                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
235                 else:
236                         bouquetlist = serviceHandler.list(bouquet)
237                         if not bouquetlist is None:
238                                 while number:
239                                         bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
240                                         if not bouquet.valid(): #check end of list
241                                                 break
242                                         if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
243                                                 continue
244                                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
245                 if not service is None:
246                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
247                                 self.servicelist.clearPath()
248                                 if self.servicelist.bouquet_root != bouquet:
249                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
250                                 self.servicelist.enterPath(bouquet)
251                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
252                         self.servicelist.zap()
253
254 class InfoBarChannelSelection:
255         """ ChannelSelection - handles the channelSelection dialog and the initial 
256         channelChange actions which open the channelSelection dialog """
257         def __init__(self):
258                 #instantiate forever
259                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
260
261                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
262                         {
263                                 "switchChannelUp": self.switchChannelUp,
264                                 "switchChannelDown": self.switchChannelDown,
265                                 "zapUp": (self.zapUp, _("next channel")),
266                                 "zapDown": (self.zapDown, _("previous channel")),
267                         })
268                         
269         def switchChannelUp(self):      
270                 self.servicelist.moveUp()
271                 self.session.execDialog(self.servicelist)
272
273         def switchChannelDown(self):    
274                 self.servicelist.moveDown()
275                 self.session.execDialog(self.servicelist)
276
277         def     zapUp(self):
278                 self.servicelist.moveUp()
279                 self.servicelist.zap()
280                 self.instance.show()
281                 self.show()
282
283         def     zapDown(self):
284                 self.servicelist.moveDown()
285                 self.servicelist.zap()
286                 self.instance.show()
287                 self.show()
288                 
289 class InfoBarMenu:
290         """ Handles a menu action, to open the (main) menu """
291         def __init__(self):
292                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions", 
293                         {
294                                 "mainMenu": (self.mainMenu, "Enter main menu..."),
295                         })
296
297         def mainMenu(self):
298                 print "loading mainmenu XML..."
299                 menu = mdom.childNodes[0]
300                 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
301                 self.session.open(MainMenu, menu, menu.childNodes)
302
303 class InfoBarEPG:
304         """ EPG - Opens an EPG list when the showEPGList action fires """
305         def __init__(self):
306                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
307                         {
308                                 "showEPGList": (self.showEPG, _("show EPG...")),
309                         })
310
311         def showEPG(self):
312                 if currentConfigSelectionElement(config.usage.epgtoggle) == "yes":
313                         self.openSingleServiceEPG()
314                 else:
315                         self.showEPGList()
316
317         def showEPGList(self):
318                 bouquets = self.servicelist.getBouquetList()
319                 if bouquets is None:
320                         cnt = 0
321                 else:
322                         cnt = len(bouquets)
323                 if cnt > 1: # show bouquet list
324                         self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
325                 elif cnt == 1: # add to only one existing bouquet
326                         self.openBouquetEPG(bouquets[0][1])
327                 else: #no bouquets so we open single epg
328                         self.openSingleEPGSelector(self.session.nav.getCurrentlyPlayingServiceReference())
329
330         def bouquetEPGCallback(self, info):
331                 if info:
332                         self.openSingleServiceEPG()
333         
334         def singleEPGCallback(self, info):
335                 if info:
336                         self.showEPGList()
337                         
338         def openEventView(self):
339                 try:
340                         self.epglist = [ ]
341                         service = self.session.nav.getCurrentService()
342                         info = service.info()
343                         ptr=info.getEvent(0)
344                         if ptr:
345                                 self.epglist.append(ptr)
346                         ptr=info.getEvent(1)
347                         if ptr:
348                                 self.epglist.append(ptr)
349                         if len(self.epglist) > 0:
350                                 self.session.open(EventView, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
351                 except:
352                         pass
353                         
354         def openSingleServiceEPG(self):
355                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
356                 ptr=eEPGCache.getInstance()
357                 if ptr.startTimeQuery(ref) != -1:
358                         self.session.openWithCallback(self.singleEPGCallback, EPGSelection, ref)
359                 else: # try to show now/next
360                         print 'no epg for service', ref.toString()
361
362         
363         def openBouquetEPG(self, bouquet):
364                 ptr=eEPGCache.getInstance()
365                 services = [ ]
366                 servicelist = eServiceCenter.getInstance().list(bouquet)
367                 if not servicelist is None:
368                         while True:
369                                 service = servicelist.getNext()
370                                 if not service.valid(): #check if end of list
371                                         break
372                                 if service.flags: #ignore non playable services
373                                         continue
374                                 services.append(ServiceReference(service))
375                 if len(services):
376                         self.session.openWithCallback(self.bouquetEPGCallback, EPGSelection, services)
377
378         def openSingleEPGSelector(self, ref):
379                 ptr=eEPGCache.getInstance()
380                 if ptr.startTimeQuery(ref) != -1:
381                         self.session.open(EPGSelection, ref)
382                 else: # try to show now/next
383                         print 'no epg for service', ref.toString()
384                         try:
385                                 self.epglist = [ ]
386                                 service = self.session.nav.getCurrentService()
387                                 info = service.info()
388                                 ptr=info.getEvent(0)
389                                 if ptr:
390                                         self.epglist.append(ptr)
391                                 ptr=info.getEvent(1)
392                                 if ptr:
393                                         self.epglist.append(ptr)
394                                 if len(self.epglist) > 0:
395                                         self.session.open(EventView, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
396                         except:
397                                 pass
398
399         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
400                 if len(self.epglist) > 1:
401                         tmp = self.epglist[0]
402                         self.epglist[0]=self.epglist[1]
403                         self.epglist[1]=tmp
404                         setEvent(self.epglist[0])
405
406 from math import log
407
408 class InfoBarTuner:
409         """provides a snr/agc/ber display"""
410         def __init__(self):
411                 self["snr"] = Label()
412                 self["agc"] = Label()
413                 self["ber"] = Label()
414                 self["snr_percent"] = Label()
415                 self["agc_percent"] = Label()
416                 self["ber_count"] = Label()
417                 self["snr_progress"] = ProgressBar()
418                 self["agc_progress"] = ProgressBar()
419                 self["ber_progress"] = ProgressBar()
420                 self.timer = eTimer()
421                 self.timer.timeout.get().append(self.updateTunerInfo)
422                 self.timer.start(1000)
423
424         def calc(self,val):
425                 if not val:
426                         return 0
427                 if val < 2500:
428                         return (long)(log(val)/log(2))
429                 return val*100/65535
430
431         def updateTunerInfo(self):
432                 if self.instance.isVisible():
433                         service = self.session.nav.getCurrentService()
434                         snr=0
435                         agc=0
436                         ber=0
437                         if service is not None:
438                                 feinfo = service.frontendStatusInfo()
439                                 if feinfo is not None:
440                                         ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
441                                         snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
442                                         agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
443                         self["snr_percent"].setText("%d%%"%(snr))
444                         self["agc_percent"].setText("%d%%"%(agc))
445                         self["ber_count"].setText("%d"%(ber))
446                         self["snr_progress"].setValue(snr)
447                         self["agc_progress"].setValue(agc)
448                         self["ber_progress"].setValue(self.calc(ber))
449
450 class InfoBarEvent:
451         """provides a current/next event info display"""
452         def __init__(self):
453                 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
454                 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
455                                 
456                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
457                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
458
459                 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Duration)
460                 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
461
462                 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
463
464 class InfoBarServiceName:
465         def __init__(self):
466                 self["ServiceName"] = ServiceName(self.session.nav)
467
468 class InfoBarSeek:
469         """handles actions like seeking, pause"""
470         
471         # ispause, isff, issm, skip
472         SEEK_STATE_PLAY = (0, 0, 0, 0)
473         SEEK_STATE_PAUSE = (1, 0, 0, 0)
474         SEEK_STATE_FF_2X = (0, 2, 0, 0)
475         SEEK_STATE_FF_4X = (0, 4, 0, 0)
476         SEEK_STATE_FF_8X = (0, 8, 0, 0)
477         SEEK_STATE_FF_32X = (0, 4, 0, 32)
478         SEEK_STATE_FF_64X = (0, 4, 0, 64)
479         SEEK_STATE_FF_128X = (0, 4, 0, 128)
480         
481         SEEK_STATE_BACK_4X = (0, 0, 0, -4)
482         SEEK_STATE_BACK_32X = (0, 0, 0, -32)
483         SEEK_STATE_BACK_64X = (0, 0, 0, -64)
484         SEEK_STATE_BACK_128X = (0, 0, 0, -128)
485         
486         SEEK_STATE_SM_HALF = (0, 0, 2, 0)
487         SEEK_STATE_SM_QUARTER = (0, 0, 4, 0)
488         SEEK_STATE_SM_EIGHTH = (0, 0, 8, 0)
489         
490         def __init__(self):
491                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
492                         {
493                                 pNavigation.evSeekableStatusChanged: self.__seekableStatusChanged,
494                                 pNavigation.evNewService: self.__serviceStarted
495                         })
496                 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions", 
497                         {
498                                 "pauseService": (self.pauseService, "pause"),
499                                 "unPauseService": (self.unPauseService, "continue"),
500                                 
501                                 "seekFwd": (self.seekFwd, "skip forward"),
502                                 "seekFwdUp": (self.seekFwdUp, "skip forward"),
503                                 "seekBack": (self.seekBack, "skip backward"),
504                                 "seekBackUp": (self.seekBackUp, "skip backward"),
505                         }, prio=-1)
506                         # give them a little more priority to win over color buttons
507
508                 self.seekstate = self.SEEK_STATE_PLAY
509                 self.seekTimer = eTimer()
510                 self.seekTimer.timeout.get().append(self.seekTimerFired)
511                 self.skipinterval = 500 # 500ms skip interval
512                 self.onClose.append(self.delSeekTimer)
513                 
514                 self.fwdtimer = False
515                 self.fwdKeyTimer = eTimer()
516                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
517
518                 self.rwdtimer = False
519                 self.rwdKeyTimer = eTimer()
520                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
521         
522         def up(self):
523                 pass
524         
525         def down(self):
526                 pass
527         
528         def delSeekTimer(self):
529                 del self.seekTimer
530                 del self.fwdKeyTimer
531                 del self.rwdKeyTimer
532         
533         def seekTimerFired(self):
534                 self.seekbase += self.skipmode * self.skipinterval
535                 
536                 # check if we bounced against the beginning of the file
537                 if self.seekbase < 0:
538                         self.seekbase = 0;
539                         self.setSeekState(self.SEEK_STATE_PLAY)
540                         
541                 self.doSeek(self.seekbase)
542
543         def isSeekable(self):
544                 service = self.session.nav.getCurrentService()
545                 if service is None:
546                         return False
547                 if service.seek() is None:
548                         return False
549                 else:
550                         return True
551
552         def __seekableStatusChanged(self):
553                 print "seekable status changed!"
554                 if not self.isSeekable():
555                         self["SeekActions"].setEnabled(False)
556                         print "not seekable, return to play"
557                         self.setSeekState(self.SEEK_STATE_PLAY)
558                 else:
559                         self["SeekActions"].setEnabled(True)
560                         print "seekable"
561
562         def __serviceStarted(self):
563                 self.seekstate = self.SEEK_STATE_PLAY
564
565         def setSeekState(self, state):
566                 service = self.session.nav.getCurrentService()
567                 self.seekTimer.stop()
568                 
569                 if service is None:
570                         return False
571                 
572                 if service.seek() is None:
573                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
574                                 state = self.SEEK_STATE_PLAY
575                 
576                 pauseable = service.pause()
577
578                 if pauseable is None:
579                         print "not pauseable."
580                         state = self.SEEK_STATE_PLAY
581                 
582                 oldstate = self.seekstate
583                 self.seekstate = state
584                 
585                 for i in range(4):
586                         if oldstate[i] != self.seekstate[i]:
587                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion, self.setSkipMode)[i](self.seekstate[i])
588
589                 return True
590                 
591         def setSkipMode(self, skipmode):
592                 print "setskipmode", skipmode
593                 self.skipmode = skipmode
594                 if skipmode == 0:
595                         self.seekTimer.stop()
596                 else:
597                         self.seekTimer.start(500)
598                 
599                 service = self.session.nav.getCurrentService()
600                 if service is None:
601                         return
602                 
603                 seekable = service.seek()
604                 if seekable is None:
605                         return
606
607                 if skipmode:
608                         seekable.setTrickmode(1)
609                 else:
610                         seekable.setTrickmode(0)
611                 
612                 self.seekbase = seekable.getPlayPosition()[1] / 90
613         
614         def pauseService(self):
615                 if self.seekstate == self.SEEK_STATE_PAUSE:
616                         print "pause, but in fact unpause"
617                         self.unPauseService()
618                 else:
619                         if self.seekstate == self.SEEK_STATE_PLAY:
620                                 print "yes, playing."
621                         else:
622                                 print "no", self.seekstate
623                         print "pause"
624                         self.setSeekState(self.SEEK_STATE_PAUSE);
625                 
626         def unPauseService(self):
627                 print "unpause"
628                 self.setSeekState(self.SEEK_STATE_PLAY);
629         
630         def doSeek(self, seektime):
631                 print "doseek", seektime
632                 service = self.session.nav.getCurrentService()
633                 if service is None:
634                         return
635                 
636                 seekable = service.seek()
637                 if seekable is None:
638                         return
639                 seekable.seekTo(90 * seektime)
640
641         def seekFwd(self):
642                 print "start fwd timer"
643                 self.fwdtimer = True
644                 self.fwdKeyTimer.start(500)
645
646         def seekBack(self):
647                 print "start rewind timer"
648                 self.rwdtimer = True
649                 self.rwdKeyTimer.start(500)
650
651         def seekFwdUp(self):
652                 print "seekFwdUp"
653                 if self.fwdtimer:
654                         self.fwdKeyTimer.stop()
655                         self.fwdtimer = False
656                         lookup = {
657                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
658                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
659                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
660                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
661                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
662                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
663                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
664                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
665                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_PLAY,
666                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_4X,
667                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
668                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
669                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
670                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
671                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
672                                 }
673                         self.setSeekState(lookup[self.seekstate]);
674         
675         def seekBackUp(self):
676                 print "seekBackUp"
677                 if self.rwdtimer:
678                         self.rwdKeyTimer.stop()
679                         self.rwdtimer = False
680                 
681                         lookup = {
682                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_4X,
683                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
684                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
685                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
686                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
687                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
688                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
689                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
690                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_BACK_32X,
691                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
692                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
693                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
694                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
695                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
696                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
697                                 }
698                         self.setSeekState(lookup[self.seekstate]);
699                 
700         def fwdTimerFire(self):
701                 print "Display seek fwd"
702                 self.fwdKeyTimer.stop()
703                 self.fwdtimer = False
704                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
705                 
706         def fwdSeekTo(self, minutes):
707                 print "Seek", minutes, "minutes forward"
708                 if minutes != 0:
709                         service = self.session.nav.getCurrentService()
710                         if service is None:
711                                 return
712                         seekable = service.seek()
713                         if seekable is None:
714                                 return
715                         seekable.seekRelative(1, minutes * 60 * 90000)
716         
717         def rwdTimerFire(self):
718                 print "rwdTimerFire"
719                 self.rwdKeyTimer.stop()
720                 self.rwdtimer = False
721                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
722         
723         def rwdSeekTo(self, minutes):
724                 print "rwdSeekTo"
725                 self.fwdSeekTo(0 - minutes)
726
727 class InfoBarShowMovies:
728
729         # i don't really like this class. 
730         # it calls a not further specified "movie list" on up/down/movieList,
731         # so this is not more than an action map
732         def __init__(self):
733                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
734                         {
735                                 "movieList": (self.showMovies, "movie list"),
736                                 "up": (self.showMovies, "movie list"),
737                                 "down": (self.showMovies, "movie list")
738                         })
739
740 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
741
742 # Hrmf.
743 #
744 # Timeshift works the following way:
745 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
746 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
747 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
748 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
749 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
750 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
751 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
752 #
753
754 # in other words:
755 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
756 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
757 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
758 # - the user can now PVR around
759 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
760 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
761 # after!
762 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
763 # - if the user rewinds, or press pause, timeshift will be activated again
764
765 # note that a timeshift can be enabled ("recording") and
766 # activated (currently time-shifting).
767
768 class InfoBarTimeshift:
769         def __init__(self):
770                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
771                         {
772                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
773                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
774                         }, prio=1)
775                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
776                         {
777                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
778                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
779                         })
780
781                 self.timeshift_enabled = 0
782                 self.timeshift_state = 0
783                 self.ts_pause_timer = eTimer()
784                 self.ts_pause_timer.timeout.get().append(self.pauseService)
785
786                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
787                         {
788                                 pNavigation.evSeekableStatusChanged: self.__seekableStatusChanged
789                         })
790         
791         def getTimeshift(self):
792                 service = self.session.nav.getCurrentService()
793                 return service.timeshift()
794
795         def startTimeshift(self):
796                 # TODO: check for harddisk! (or do this in the interface? would make
797                 # more sense... for example radio could be timeshifted in memory,
798                 # and the decision can't be made here)
799                 print "enable timeshift"
800                 ts = self.getTimeshift()
801                 if ts is None:
802                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
803                         print "no ts interface"
804                         return
805                 
806                 if self.timeshift_enabled:
807                         print "hu, timeshift already enabled?"
808                 else:
809                         if not ts.startTimeshift():
810                                 self.timeshift_enabled = 1
811                                 
812                                 # PAUSE.
813                                 self.setSeekState(self.SEEK_STATE_PAUSE)
814                                 
815                                 # enable the "TimeshiftEnableActions", which will override
816                                 # the startTimeshift actions
817                                 self.__seekableStatusChanged()
818                         else:
819                                 print "timeshift failed"
820
821         # nyi
822         def stopTimeshift(self):
823                 print "disable timeshift"
824                 ts = self.getTimeshift()
825                 if ts is None:
826                         return
827                 ts.stopTimeshift()
828                 self.timeshift_enabled = 0
829
830                 # disable actions
831                 self.__seekableStatusChanged()
832         
833         # activates timeshift, and seeks to (almost) the end
834         def activateTimeshiftEnd(self):
835                 ts = self.getTimeshift()
836                 
837                 if ts is None:
838                         return
839                 
840                 if ts.isTimeshiftActive():
841                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
842                         self.pauseService()
843                 else:
844                         self.setSeekState(self.SEEK_STATE_PLAY)
845                         ts.activateTimeshift()
846         
847         # same as activateTimeshiftEnd, but pauses afterwards.
848         def activateTimeshiftEndAndPause(self):
849                 state = self.seekstate
850                 self.activateTimeshiftEnd()
851                 
852                 # well, this is "andPause", but it could be pressed from pause,
853                 # when pausing on the (fake-)"live" picture, so an un-pause
854                 # is perfectly ok.
855                 
856                 print "now, pauseService"
857                 if state == self.SEEK_STATE_PLAY:
858                         print "is PLAYING, start pause timer"
859                         self.ts_pause_timer.start(200, 1)
860                 else:
861                         print "unpause"
862                         self.unPauseService()
863         
864         def __seekableStatusChanged(self):
865                 enabled = False
866                 
867                 print "self.isSeekable", self.isSeekable()
868                 print "self.timeshift_enabled", self.timeshift_enabled
869                 
870                 # when this service is not seekable, but timeshift
871                 # is enabled, this means we can activate
872                 # the timeshift
873                 if not self.isSeekable() and self.timeshift_enabled:
874                         enabled = True
875
876                 print "timeshift activate:", enabled
877                 self["TimeshiftActivateActions"].setEnabled(enabled)
878
879 from RecordTimer import parseEvent
880
881 class InfoBarInstantRecord:
882         """Instant Record - handles the instantRecord action in order to 
883         start/stop instant records"""
884         def __init__(self):
885                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
886                         {
887                                 "instantRecord": (self.instantRecord, "Instant Record..."),
888                         })
889                 self.recording = None
890                 
891                 self["BlinkingPoint"] = BlinkingPixmapConditional()
892                 self.onShown.append(self["BlinkingPoint"].hideWidget)
893                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
894                 
895         def stopCurrentRecording(self): 
896                 self.session.nav.RecordTimer.removeEntry(self.recording)
897                 self.recording = None
898                         
899         def startInstantRecording(self):
900                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
901                 
902                 # try to get event info
903                 event = None
904                 try:
905                         service = self.session.nav.getCurrentService()
906                         info = service.info()
907                         ev = info.getEvent(0)
908                         event = ev
909                 except:
910                         pass
911                 
912                 if event is not None:
913                         data = parseEvent(event)
914                         begin = data[0]
915                         if begin < time.time():
916                                 begin = time.time()
917                         
918                         end = data[1]
919                         if end < begin:
920                                 end = begin
921                         
922                         end += 3600 * 10
923                         
924                         data = (begin, end, data[2], data[3], data[4])
925                 else:
926                         data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
927                 
928                 # fix me, description. 
929                 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
930                 self.recording.dontSave = True
931                 
932                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
933                 
934         def isInstantRecordRunning(self):
935                 if self.recording != None:
936                         if self.recording.isRunning():
937                                 return True
938                 return False
939
940         def recordQuestionCallback(self, answer):
941                 if answer == False:
942                         return
943                 
944                 if self.isInstantRecordRunning():
945                         self.stopCurrentRecording()
946                 else:
947                         self.startInstantRecording()
948
949         def instantRecord(self):
950                 try:
951                         stat = os.stat(resolveFilename(SCOPE_HDD))
952                 except:
953                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
954                         return
955         
956                 if self.isInstantRecordRunning():
957                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
958                 else:
959                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
960
961 from Screens.AudioSelection import AudioSelection
962
963 class InfoBarAudioSelection:
964         def __init__(self):
965                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
966                         {
967                                 "audioSelection": (self.audioSelection, "Audio Options..."),
968                         })
969
970         def audioSelection(self):
971                 service = self.session.nav.getCurrentService()
972                 audio = service.audioTracks()
973                 n = audio.getNumberOfTracks()
974                 if n > 0:
975                         self.session.open(AudioSelection, audio)
976
977 from Screens.SubserviceSelection import SubserviceSelection
978
979 class InfoBarSubserviceSelection:
980         def __init__(self):
981                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
982                         {
983                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
984                         })
985
986         def subserviceSelection(self):
987                 service = self.session.nav.getCurrentService()
988                 subservices = service.subServices()
989                 n = subservices.getNumberOfSubservices()
990                 if n > 0:
991                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
992
993         def subserviceSelected(self, service):
994                 if not service is None:
995                         self.session.nav.playService(service)
996
997 class InfoBarAdditionalInfo:
998         def __init__(self):
999                 self["DolbyActive"] = Pixmap()
1000                 self["CryptActive"] = Pixmap()
1001                 self["FormatActive"] = Pixmap()
1002                 
1003                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1004                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1005                 self.onShown.append(self["ButtonRed"].update)
1006                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1007                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1008                 self.onShown.append(self["ButtonRedText"].update)
1009
1010                 self["ButtonGreen"] = Pixmap()
1011                 self["ButtonGreenText"] = Label(_("Subservices"))
1012
1013                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1014                 self["ButtonYellow"].setConnect(lambda: False)
1015
1016                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1017                 self["ButtonBlue"].setConnect(lambda: False)
1018
1019                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1020
1021         def hideSubServiceIndication(self):
1022                 self["ButtonGreen"].hideWidget()
1023                 self["ButtonGreenText"].hide()
1024
1025         def showSubServiceIndication(self):
1026                 self["ButtonGreen"].showWidget()
1027                 self["ButtonGreenText"].show()
1028
1029         def checkFormat(self, service):
1030                 info = service.info()
1031                 if info is not None:
1032                         aspect = info.getInfo(iServiceInformation.sAspect)
1033                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1034                                 self["FormatActive"].showWidget()
1035                         else:
1036                                 self["FormatActive"].hideWidget()
1037
1038         def checkSubservices(self, service):
1039                 if service.subServices().getNumberOfSubservices() > 0:
1040                         self.showSubServiceIndication()
1041                 else:
1042                         self.hideSubServiceIndication()
1043
1044         def checkDolby(self, service):
1045                 # FIXME
1046                 dolby = False
1047                 audio = service.audioTracks()
1048                 if audio is not None:
1049                         n = audio.getNumberOfTracks()
1050                         for x in range(n):
1051                                 i = audio.getTrackInfo(x)
1052                                 description = i.getDescription();
1053                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1054                                         dolby = True
1055                                         break
1056                 if dolby:
1057                         self["DolbyActive"].showWidget()
1058                 else:
1059                         self["DolbyActive"].hideWidget()
1060
1061         def checkCrypted(self, service):
1062                 info = service.info()
1063                 if info is not None:
1064                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1065                                 self["CryptActive"].showWidget()
1066                         else:
1067                                 self["CryptActive"].hideWidget()
1068
1069         def gotServiceEvent(self, ev):
1070                 service = self.session.nav.getCurrentService()
1071                 if ev == pNavigation.evUpdatedEventInfo:
1072                         self.checkSubservices(service)
1073                         self.checkFormat(service)
1074                 elif ev == pNavigation.evUpdatedInfo:
1075                         self.checkCrypted(service)
1076                         self.checkDolby(service)
1077                 elif ev == pNavigation.evStopService:
1078                         self.hideSubServiceIndication()
1079                         self["CryptActive"].hideWidget()
1080                         self["DolbyActive"].hideWidget()
1081                         self["FormatActive"].hideWidget()
1082
1083 class InfoBarNotifications:
1084         def __init__(self):
1085                 self.onExecBegin.append(self.checkNotifications)
1086                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1087         
1088         def checkNotificationsIfExecing(self):
1089                 if self.execing:
1090                         self.checkNotifications()
1091
1092         def checkNotifications(self):
1093                 if len(Notifications.notifications):
1094                         n = Notifications.notifications[0]
1095                         Notifications.notifications = Notifications.notifications[1:]
1096                         print "open",n
1097                         cb = n[0]
1098                         if cb is not None:
1099                                 self.session.openWithCallback(cb, *n[1:])
1100                         else:
1101                                 self.session.open(*n[1:])
1102
1103 class InfoBarServiceNotifications:
1104         def __init__(self):
1105                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1106                         {
1107                                 pNavigation.evEnd: self.serviceHasEnded
1108                         })
1109
1110         def serviceHasEnded(self):
1111                 print "service end!"
1112
1113                 try:
1114                         self.setSeekState(self.SEEK_STATE_PLAY)
1115                 except:
1116                         pass