fix radio player
[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
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["NumberActions"] = NumberActionMap( [ "NumberActions"],
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         def openBouquetEPG(self, bouquet):
363                 ptr=eEPGCache.getInstance()
364                 services = [ ]
365                 servicelist = eServiceCenter.getInstance().list(bouquet)
366                 if not servicelist is None:
367                         while True:
368                                 service = servicelist.getNext()
369                                 if not service.valid(): #check if end of list
370                                         break
371                                 if service.flags: #ignore non playable services
372                                         continue
373                                 services.append(ServiceReference(service))
374                 if len(services):
375                         self.session.openWithCallback(self.bouquetEPGCallback, EPGSelection, services)
376
377         def openSingleEPGSelector(self, ref):
378                 ptr=eEPGCache.getInstance()
379                 if ptr.startTimeQuery(ref) != -1:
380                         self.session.open(EPGSelection, ref)
381                 else: # try to show now/next
382                         print 'no epg for service', ref.toString()
383                         try:
384                                 self.epglist = [ ]
385                                 service = self.session.nav.getCurrentService()
386                                 info = service.info()
387                                 ptr=info.getEvent(0)
388                                 if ptr:
389                                         self.epglist.append(ptr)
390                                 ptr=info.getEvent(1)
391                                 if ptr:
392                                         self.epglist.append(ptr)
393                                 if len(self.epglist) > 0:
394                                         self.session.open(EventView, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
395                         except:
396                                 pass
397
398         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
399                 if len(self.epglist) > 1:
400                         tmp = self.epglist[0]
401                         self.epglist[0]=self.epglist[1]
402                         self.epglist[1]=tmp
403                         setEvent(self.epglist[0])
404
405 from math import log
406
407 class InfoBarTuner:
408         """provides a snr/agc/ber display"""
409         def __init__(self):
410                 self["snr"] = Label()
411                 self["agc"] = Label()
412                 self["ber"] = Label()
413                 self["snr_percent"] = Label()
414                 self["agc_percent"] = Label()
415                 self["ber_count"] = Label()
416                 self["snr_progress"] = ProgressBar()
417                 self["agc_progress"] = ProgressBar()
418                 self["ber_progress"] = ProgressBar()
419                 self.timer = eTimer()
420                 self.timer.timeout.get().append(self.updateTunerInfo)
421                 self.timer.start(1000)
422
423         def calc(self,val):
424                 if not val:
425                         return 0
426                 if val < 2500:
427                         return (long)(log(val)/log(2))
428                 return val*100/65535
429
430         def updateTunerInfo(self):
431                 if self.instance.isVisible():
432                         service = self.session.nav.getCurrentService()
433                         snr=0
434                         agc=0
435                         ber=0
436                         if service is not None:
437                                 feinfo = service.frontendStatusInfo()
438                                 if feinfo is not None:
439                                         ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
440                                         snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
441                                         agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
442                         self["snr_percent"].setText("%d%%"%(snr))
443                         self["agc_percent"].setText("%d%%"%(agc))
444                         self["ber_count"].setText("%d"%(ber))
445                         self["snr_progress"].setValue(snr)
446                         self["agc_progress"].setValue(agc)
447                         self["ber_progress"].setValue(self.calc(ber))
448
449 class InfoBarEvent:
450         """provides a current/next event info display"""
451         def __init__(self):
452                 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
453                 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
454                                 
455                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
456                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
457
458                 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Duration)
459                 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
460
461                 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
462
463 class InfoBarServiceName:
464         def __init__(self):
465                 self["ServiceName"] = ServiceName(self.session.nav)
466
467 class InfoBarSeek:
468         """handles actions like seeking, pause"""
469         
470         # ispause, isff, issm, skip
471         SEEK_STATE_PLAY = (0, 0, 0, 0)
472         SEEK_STATE_PAUSE = (1, 0, 0, 0)
473         SEEK_STATE_FF_2X = (0, 2, 0, 0)
474         SEEK_STATE_FF_4X = (0, 4, 0, 0)
475         SEEK_STATE_FF_8X = (0, 8, 0, 0)
476         SEEK_STATE_FF_32X = (0, 4, 0, 32)
477         SEEK_STATE_FF_64X = (0, 4, 0, 64)
478         SEEK_STATE_FF_128X = (0, 4, 0, 128)
479         
480         SEEK_STATE_BACK_4X = (0, 0, 0, -4)
481         SEEK_STATE_BACK_32X = (0, 0, 0, -32)
482         SEEK_STATE_BACK_64X = (0, 0, 0, -64)
483         SEEK_STATE_BACK_128X = (0, 0, 0, -128)
484         
485         SEEK_STATE_SM_HALF = (0, 0, 2, 0)
486         SEEK_STATE_SM_QUARTER = (0, 0, 4, 0)
487         SEEK_STATE_SM_EIGHTH = (0, 0, 8, 0)
488         
489         def __init__(self):
490                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
491                         {
492                                 pNavigation.evSeekableStatusChanged: self.__seekableStatusChanged,
493                                 pNavigation.evNewService: self.__serviceStarted
494                         })
495                 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions", 
496                         {
497                                 "pauseService": (self.pauseService, "pause"),
498                                 "unPauseService": (self.unPauseService, "continue"),
499                                 
500                                 "seekFwd": (self.seekFwd, "skip forward"),
501                                 "seekFwdUp": (self.seekFwdUp, "skip forward"),
502                                 "seekBack": (self.seekBack, "skip backward"),
503                                 "seekBackUp": (self.seekBackUp, "skip backward"),
504                         }, prio=-1)
505                         # give them a little more priority to win over color buttons
506
507                 self.seekstate = self.SEEK_STATE_PLAY
508                 self.seekTimer = eTimer()
509                 self.seekTimer.timeout.get().append(self.seekTimerFired)
510                 self.skipinterval = 500 # 500ms skip interval
511                 self.onClose.append(self.delSeekTimer)
512                 
513                 self.fwdtimer = False
514                 self.fwdKeyTimer = eTimer()
515                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
516
517                 self.rwdtimer = False
518                 self.rwdKeyTimer = eTimer()
519                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
520         
521         def up(self):
522                 pass
523         
524         def down(self):
525                 pass
526         
527         def delSeekTimer(self):
528                 del self.seekTimer
529                 del self.fwdKeyTimer
530                 del self.rwdKeyTimer
531         
532         def seekTimerFired(self):
533                 self.seekbase += self.skipmode * self.skipinterval
534                 
535                 # check if we bounced against the beginning of the file
536                 if self.seekbase < 0:
537                         self.seekbase = 0;
538                         self.setSeekState(self.SEEK_STATE_PLAY)
539                         
540                 self.doSeek(self.seekbase)
541
542         def isSeekable(self):
543                 service = self.session.nav.getCurrentService()
544                 if service is None:
545                         return False
546                 if service.seek() is None:
547                         return False
548                 else:
549                         return True
550
551         def __seekableStatusChanged(self):
552                 print "seekable status changed!"
553                 if not self.isSeekable():
554                         self["SeekActions"].setEnabled(False)
555                         print "not seekable, return to play"
556                         self.setSeekState(self.SEEK_STATE_PLAY)
557                 else:
558                         self["SeekActions"].setEnabled(True)
559                         print "seekable"
560
561         def __serviceStarted(self):
562                 self.seekstate = self.SEEK_STATE_PLAY
563
564         def setSeekState(self, state):
565                 service = self.session.nav.getCurrentService()
566                 self.seekTimer.stop()
567                 
568                 if service is None:
569                         return False
570                 
571                 if service.seek() is None:
572                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
573                                 state = self.SEEK_STATE_PLAY
574                 
575                 pauseable = service.pause()
576
577                 if pauseable is None:
578                         print "not pauseable."
579                         state = self.SEEK_STATE_PLAY
580                 
581                 oldstate = self.seekstate
582                 self.seekstate = state
583                 
584                 for i in range(4):
585                         if oldstate[i] != self.seekstate[i]:
586                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion, self.setSkipMode)[i](self.seekstate[i])
587
588                 return True
589                 
590         def setSkipMode(self, skipmode):
591                 print "setskipmode", skipmode
592                 self.skipmode = skipmode
593                 if skipmode == 0:
594                         self.seekTimer.stop()
595                 else:
596                         self.seekTimer.start(500)
597                 
598                 service = self.session.nav.getCurrentService()
599                 if service is None:
600                         return
601                 
602                 seekable = service.seek()
603                 if seekable is None:
604                         return
605
606                 if skipmode:
607                         seekable.setTrickmode(1)
608                 else:
609                         seekable.setTrickmode(0)
610                 
611                 self.seekbase = seekable.getPlayPosition()[1] / 90
612         
613         def pauseService(self):
614                 if self.seekstate == self.SEEK_STATE_PAUSE:
615                         print "pause, but in fact unpause"
616                         self.unPauseService()
617                 else:
618                         if self.seekstate == self.SEEK_STATE_PLAY:
619                                 print "yes, playing."
620                         else:
621                                 print "no", self.seekstate
622                         print "pause"
623                         self.setSeekState(self.SEEK_STATE_PAUSE);
624                 
625         def unPauseService(self):
626                 print "unpause"
627                 self.setSeekState(self.SEEK_STATE_PLAY);
628         
629         def doSeek(self, seektime):
630                 print "doseek", seektime
631                 service = self.session.nav.getCurrentService()
632                 if service is None:
633                         return
634                 
635                 seekable = service.seek()
636                 if seekable is None:
637                         return
638                 seekable.seekTo(90 * seektime)
639
640         def seekFwd(self):
641                 print "start fwd timer"
642                 self.fwdtimer = True
643                 self.fwdKeyTimer.start(500)
644
645         def seekBack(self):
646                 print "start rewind timer"
647                 self.rwdtimer = True
648                 self.rwdKeyTimer.start(500)
649
650         def seekFwdUp(self):
651                 print "seekFwdUp"
652                 if self.fwdtimer:
653                         self.fwdKeyTimer.stop()
654                         self.fwdtimer = False
655                         lookup = {
656                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
657                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
658                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
659                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
660                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
661                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
662                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
663                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
664                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_PLAY,
665                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_4X,
666                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
667                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
668                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
669                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
670                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
671                                 }
672                         self.setSeekState(lookup[self.seekstate]);
673         
674         def seekBackUp(self):
675                 print "seekBackUp"
676                 if self.rwdtimer:
677                         self.rwdKeyTimer.stop()
678                         self.rwdtimer = False
679                 
680                         lookup = {
681                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_4X,
682                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
683                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
684                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
685                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
686                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
687                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
688                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
689                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_BACK_32X,
690                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
691                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
692                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
693                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
694                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
695                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
696                                 }
697                         self.setSeekState(lookup[self.seekstate]);
698                 
699         def fwdTimerFire(self):
700                 print "Display seek fwd"
701                 self.fwdKeyTimer.stop()
702                 self.fwdtimer = False
703                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
704                 
705         def fwdSeekTo(self, minutes):
706                 print "Seek", minutes, "minutes forward"
707                 if minutes != 0:
708                         service = self.session.nav.getCurrentService()
709                         if service is None:
710                                 return
711                         seekable = service.seek()
712                         if seekable is None:
713                                 return
714                         seekable.seekRelative(1, minutes * 60 * 90000)
715         
716         def rwdTimerFire(self):
717                 print "rwdTimerFire"
718                 self.rwdKeyTimer.stop()
719                 self.rwdtimer = False
720                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
721         
722         def rwdSeekTo(self, minutes):
723                 print "rwdSeekTo"
724                 self.fwdSeekTo(0 - minutes)
725
726 class InfoBarShowMovies:
727
728         # i don't really like this class. 
729         # it calls a not further specified "movie list" on up/down/movieList,
730         # so this is not more than an action map
731         def __init__(self):
732                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
733                         {
734                                 "movieList": (self.showMovies, "movie list"),
735                                 "up": (self.showMovies, "movie list"),
736                                 "down": (self.showMovies, "movie list")
737                         })
738
739 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
740
741 # Hrmf.
742 #
743 # Timeshift works the following way:
744 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
745 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
746 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
747 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
748 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
749 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
750 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
751 #
752
753 # in other words:
754 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
755 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
756 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
757 # - the user can now PVR around
758 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
759 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
760 # after!
761 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
762 # - if the user rewinds, or press pause, timeshift will be activated again
763
764 # note that a timeshift can be enabled ("recording") and
765 # activated (currently time-shifting).
766
767 class InfoBarTimeshift:
768         def __init__(self):
769                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
770                         {
771                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
772                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
773                         }, prio=1)
774                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
775                         {
776                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
777                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
778                         })
779
780                 self.timeshift_enabled = 0
781                 self.timeshift_state = 0
782                 self.ts_pause_timer = eTimer()
783                 self.ts_pause_timer.timeout.get().append(self.pauseService)
784
785                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
786                         {
787                                 pNavigation.evSeekableStatusChanged: self.__seekableStatusChanged
788                         })
789         
790         def getTimeshift(self):
791                 service = self.session.nav.getCurrentService()
792                 return service.timeshift()
793
794         def startTimeshift(self):
795                 # TODO: check for harddisk! (or do this in the interface? would make
796                 # more sense... for example radio could be timeshifted in memory,
797                 # and the decision can't be made here)
798                 print "enable timeshift"
799                 ts = self.getTimeshift()
800                 if ts is None:
801                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
802                         print "no ts interface"
803                         return
804                 
805                 if self.timeshift_enabled:
806                         print "hu, timeshift already enabled?"
807                 else:
808                         if not ts.startTimeshift():
809                                 self.timeshift_enabled = 1
810                                 
811                                 # PAUSE.
812                                 self.setSeekState(self.SEEK_STATE_PAUSE)
813                                 
814                                 # enable the "TimeshiftEnableActions", which will override
815                                 # the startTimeshift actions
816                                 self.__seekableStatusChanged()
817                         else:
818                                 print "timeshift failed"
819
820         # nyi
821         def stopTimeshift(self):
822                 print "disable timeshift"
823                 ts = self.getTimeshift()
824                 if ts is None:
825                         return
826                 ts.stopTimeshift()
827                 self.timeshift_enabled = 0
828
829                 # disable actions
830                 self.__seekableStatusChanged()
831         
832         # activates timeshift, and seeks to (almost) the end
833         def activateTimeshiftEnd(self):
834                 ts = self.getTimeshift()
835                 
836                 if ts is None:
837                         return
838                 
839                 if ts.isTimeshiftActive():
840                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
841                         self.pauseService()
842                 else:
843                         self.setSeekState(self.SEEK_STATE_PLAY)
844                         ts.activateTimeshift()
845         
846         # same as activateTimeshiftEnd, but pauses afterwards.
847         def activateTimeshiftEndAndPause(self):
848                 state = self.seekstate
849                 self.activateTimeshiftEnd()
850                 
851                 # well, this is "andPause", but it could be pressed from pause,
852                 # when pausing on the (fake-)"live" picture, so an un-pause
853                 # is perfectly ok.
854                 
855                 print "now, pauseService"
856                 if state == self.SEEK_STATE_PLAY:
857                         print "is PLAYING, start pause timer"
858                         self.ts_pause_timer.start(200, 1)
859                 else:
860                         print "unpause"
861                         self.unPauseService()
862         
863         def __seekableStatusChanged(self):
864                 enabled = False
865                 
866                 print "self.isSeekable", self.isSeekable()
867                 print "self.timeshift_enabled", self.timeshift_enabled
868                 
869                 # when this service is not seekable, but timeshift
870                 # is enabled, this means we can activate
871                 # the timeshift
872                 if not self.isSeekable() and self.timeshift_enabled:
873                         enabled = True
874
875                 print "timeshift activate:", enabled
876                 self["TimeshiftActivateActions"].setEnabled(enabled)
877
878 from RecordTimer import parseEvent
879
880 class InfoBarInstantRecord:
881         """Instant Record - handles the instantRecord action in order to 
882         start/stop instant records"""
883         def __init__(self):
884                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
885                         {
886                                 "instantRecord": (self.instantRecord, "Instant Record..."),
887                         })
888                 self.recording = None
889                 
890                 self["BlinkingPoint"] = BlinkingPixmapConditional()
891                 self.onShown.append(self["BlinkingPoint"].hideWidget)
892                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
893                 
894         def stopCurrentRecording(self): 
895                 self.session.nav.RecordTimer.removeEntry(self.recording)
896                 self.recording = None
897                         
898         def startInstantRecording(self):
899                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
900                 
901                 # try to get event info
902                 event = None
903                 try:
904                         service = self.session.nav.getCurrentService()
905                         info = service.info()
906                         ev = info.getEvent(0)
907                         event = ev
908                 except:
909                         pass
910                 
911                 if event is not None:
912                         data = parseEvent(event)
913                         begin = data[0]
914                         if begin < time.time():
915                                 begin = time.time()
916                         
917                         end = data[1]
918                         if end < begin:
919                                 end = begin
920                         
921                         end += 3600 * 10
922                         
923                         data = (begin, end, data[2], data[3], data[4])
924                 else:
925                         data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
926                 
927                 # fix me, description. 
928                 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
929                 self.recording.dontSave = True
930                 
931                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
932                 
933         def isInstantRecordRunning(self):
934                 if self.recording != None:
935                         if self.recording.isRunning():
936                                 return True
937                 return False
938
939         def recordQuestionCallback(self, answer):
940                 if answer == False:
941                         return
942                 
943                 if self.isInstantRecordRunning():
944                         self.stopCurrentRecording()
945                 else:
946                         self.startInstantRecording()
947
948         def instantRecord(self):
949                 try:
950                         stat = os.stat(resolveFilename(SCOPE_HDD))
951                 except:
952                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
953                         return
954         
955                 if self.isInstantRecordRunning():
956                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
957                 else:
958                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
959
960 from Screens.AudioSelection import AudioSelection
961
962 class InfoBarAudioSelection:
963         def __init__(self):
964                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
965                         {
966                                 "audioSelection": (self.audioSelection, "Audio Options..."),
967                         })
968
969         def audioSelection(self):
970                 service = self.session.nav.getCurrentService()
971                 audio = service.audioTracks()
972                 n = audio.getNumberOfTracks()
973                 if n > 0:
974                         self.session.open(AudioSelection, audio)
975
976 from Screens.SubserviceSelection import SubserviceSelection
977
978 class InfoBarSubserviceSelection:
979         def __init__(self):
980                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
981                         {
982                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
983                         })
984
985         def subserviceSelection(self):
986                 service = self.session.nav.getCurrentService()
987                 subservices = service.subServices()
988                 n = subservices.getNumberOfSubservices()
989                 if n > 0:
990                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
991
992         def subserviceSelected(self, service):
993                 if not service is None:
994                         self.session.nav.playService(service)
995
996 class InfoBarAdditionalInfo:
997         def __init__(self):
998                 self["DolbyActive"] = Pixmap()
999                 self["CryptActive"] = Pixmap()
1000                 self["FormatActive"] = Pixmap()
1001                 
1002                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1003                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1004                 self.onShown.append(self["ButtonRed"].update)
1005                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1006                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1007                 self.onShown.append(self["ButtonRedText"].update)
1008
1009                 self["ButtonGreen"] = Pixmap()
1010                 self["ButtonGreenText"] = Label(_("Subservices"))
1011
1012                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1013                 self["ButtonYellow"].setConnect(lambda: False)
1014
1015                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1016                 self["ButtonBlue"].setConnect(lambda: False)
1017
1018                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1019
1020         def hideSubServiceIndication(self):
1021                 self["ButtonGreen"].hideWidget()
1022                 self["ButtonGreenText"].hide()
1023
1024         def showSubServiceIndication(self):
1025                 self["ButtonGreen"].showWidget()
1026                 self["ButtonGreenText"].show()
1027
1028         def checkFormat(self, service):
1029                 info = service.info()
1030                 if info is not None:
1031                         aspect = info.getInfo(iServiceInformation.sAspect)
1032                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1033                                 self["FormatActive"].showWidget()
1034                         else:
1035                                 self["FormatActive"].hideWidget()
1036
1037         def checkSubservices(self, service):
1038                 if service.subServices().getNumberOfSubservices() > 0:
1039                         self.showSubServiceIndication()
1040                 else:
1041                         self.hideSubServiceIndication()
1042
1043         def checkDolby(self, service):
1044                 # FIXME
1045                 dolby = False
1046                 audio = service.audioTracks()
1047                 if audio is not None:
1048                         n = audio.getNumberOfTracks()
1049                         for x in range(n):
1050                                 i = audio.getTrackInfo(x)
1051                                 description = i.getDescription();
1052                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1053                                         dolby = True
1054                                         break
1055                 if dolby:
1056                         self["DolbyActive"].showWidget()
1057                 else:
1058                         self["DolbyActive"].hideWidget()
1059
1060         def checkCrypted(self, service):
1061                 info = service.info()
1062                 if info is not None:
1063                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1064                                 self["CryptActive"].showWidget()
1065                         else:
1066                                 self["CryptActive"].hideWidget()
1067
1068         def gotServiceEvent(self, ev):
1069                 service = self.session.nav.getCurrentService()
1070                 if ev == pNavigation.evUpdatedEventInfo:
1071                         self.checkSubservices(service)
1072                         self.checkFormat(service)
1073                 elif ev == pNavigation.evUpdatedInfo:
1074                         self.checkCrypted(service)
1075                         self.checkDolby(service)
1076                 elif ev == pNavigation.evStopService:
1077                         self.hideSubServiceIndication()
1078                         self["CryptActive"].hideWidget()
1079                         self["DolbyActive"].hideWidget()
1080                         self["FormatActive"].hideWidget()
1081
1082 class InfoBarNotifications:
1083         def __init__(self):
1084                 self.onExecBegin.append(self.checkNotifications)
1085                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1086         
1087         def checkNotificationsIfExecing(self):
1088                 if self.execing:
1089                         self.checkNotifications()
1090
1091         def checkNotifications(self):
1092                 if len(Notifications.notifications):
1093                         n = Notifications.notifications[0]
1094                         Notifications.notifications = Notifications.notifications[1:]
1095                         print "open",n
1096                         cb = n[0]
1097                         if cb is not None:
1098                                 self.session.openWithCallback(cb, *n[1:])
1099                         else:
1100                                 self.session.open(*n[1:])
1101
1102 class InfoBarServiceNotifications:
1103         def __init__(self):
1104                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1105                         {
1106                                 pNavigation.evEnd: self.serviceHasEnded
1107                         })
1108
1109         def serviceHasEnded(self):
1110                 print "service end!"
1111
1112                 try:
1113                         self.setSeekState(self.SEEK_STATE_PLAY)
1114                 except:
1115                         pass