timeshifting: lots of interface fixups
[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["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                         })
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 setSeekState(self, state):
562                 service = self.session.nav.getCurrentService()
563                 self.seekTimer.stop()
564                 
565                 if service is None:
566                         return False
567                 
568                 if service.seek() is None:
569                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
570                                 state = self.SEEK_STATE_PLAY
571                 
572                 pauseable = service.pause()
573
574                 if pauseable is None:
575                         print "not pauseable."
576                         state = self.SEEK_STATE_PLAY
577                 
578                 oldstate = self.seekstate
579                 self.seekstate = state
580                 
581                 for i in range(4):
582                         if oldstate[i] != self.seekstate[i]:
583                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion, self.setSkipMode)[i](self.seekstate[i])
584
585                 return True
586                 
587         def setSkipMode(self, skipmode):
588                 print "setskipmode", skipmode
589                 self.skipmode = skipmode
590                 if skipmode == 0:
591                         self.seekTimer.stop()
592                 else:
593                         self.seekTimer.start(500)
594                 
595                 service = self.session.nav.getCurrentService()
596                 if service is None:
597                         return
598                 
599                 seekable = service.seek()
600                 if seekable is None:
601                         return
602
603                 if skipmode:
604                         seekable.setTrickmode(1)
605                 else:
606                         seekable.setTrickmode(0)
607                 
608                 self.seekbase = seekable.getPlayPosition()[1] / 90
609         
610         def pauseService(self):
611                 if self.seekstate == self.SEEK_STATE_PAUSE:
612                         print "pause, but in fact unpause"
613                         self.unPauseService()
614                 else:
615                         if self.seekstate == self.SEEK_STATE_PLAY:
616                                 print "yes, playing."
617                         else:
618                                 print "no", self.seekstate
619                         print "pause"
620                         self.setSeekState(self.SEEK_STATE_PAUSE);
621                 
622         def unPauseService(self):
623                 print "unpause"
624                 self.setSeekState(self.SEEK_STATE_PLAY);
625         
626         def doSeek(self, seektime):
627                 print "doseek", seektime
628                 service = self.session.nav.getCurrentService()
629                 if service is None:
630                         return
631                 
632                 seekable = service.seek()
633                 if seekable is None:
634                         return
635                 seekable.seekTo(90 * seektime)
636
637         def seekFwd(self):
638                 print "start fwd timer"
639                 self.fwdtimer = True
640                 self.fwdKeyTimer.start(500)
641
642         def seekBack(self):
643                 print "start rewind timer"
644                 self.rwdtimer = True
645                 self.rwdKeyTimer.start(500)
646
647         def seekFwdUp(self):
648                 print "seekFwdUp"
649                 if self.fwdtimer:
650                         self.fwdKeyTimer.stop()
651                         self.fwdtimer = False
652                         lookup = {
653                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
654                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
655                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
656                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
657                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
658                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
659                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
660                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
661                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_PLAY,
662                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_4X,
663                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
664                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
665                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
666                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
667                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
668                                 }
669                         self.setSeekState(lookup[self.seekstate]);
670         
671         def seekBackUp(self):
672                 print "seekBackUp"
673                 if self.rwdtimer:
674                         self.rwdKeyTimer.stop()
675                         self.rwdtimer = False
676                 
677                         lookup = {
678                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_4X,
679                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
680                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
681                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
682                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
683                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
684                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
685                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
686                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_BACK_32X,
687                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
688                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
689                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
690                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
691                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
692                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
693                                 }
694                         self.setSeekState(lookup[self.seekstate]);
695                 
696         def fwdTimerFire(self):
697                 print "Display seek fwd"
698                 self.fwdKeyTimer.stop()
699                 self.fwdtimer = False
700                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
701                 
702         def fwdSeekTo(self, minutes):
703                 print "Seek", minutes, "minutes forward"
704                 if minutes != 0:
705                         service = self.session.nav.getCurrentService()
706                         if service is None:
707                                 return
708                         seekable = service.seek()
709                         if seekable is None:
710                                 return
711                         seekable.seekRelative(1, minutes * 60 * 90000)
712         
713         def rwdTimerFire(self):
714                 print "rwdTimerFire"
715                 self.rwdKeyTimer.stop()
716                 self.rwdtimer = False
717                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
718         
719         def rwdSeekTo(self, minutes):
720                 print "rwdSeekTo"
721                 self.fwdSeekTo(0 - minutes)
722
723 class InfoBarShowMovies:
724
725         # i don't really like this class. 
726         # it calls a not further specified "movie list" on up/down/movieList,
727         # so this is not more than an action map
728         def __init__(self):
729                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
730                         {
731                                 "movieList": (self.showMovies, "movie list"),
732                                 "up": (self.showMovies, "movie list"),
733                                 "down": (self.showMovies, "movie list")
734                         })
735
736 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
737
738 # Hrmf.
739 #
740 # Timeshift works the following way:
741 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
742 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
743 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
744 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
745 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
746 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
747 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
748 #
749
750 # in other words:
751 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
752 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
753 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
754 # - the user can now PVR around
755 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
756 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
757 # after!
758 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
759 # - if the user rewinds, or press pause, timeshift will be activated again
760
761 # note that a timeshift can be enabled ("recording") and
762 # activated (currently time-shifting).
763
764 class InfoBarTimeshift:
765         def __init__(self):
766                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
767                         {
768                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
769                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
770                         }, prio=1)
771                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
772                         {
773                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
774                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
775                         })
776
777                 self.timeshift_enabled = 0
778                 self.timeshift_state = 0
779                 self.ts_pause_timer = eTimer()
780                 self.ts_pause_timer.timeout.get().append(self.pauseService)
781
782                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
783                         {
784                                 pNavigation.evSeekableStatusChanged: self.__seekableStatusChanged
785                         })
786         
787         def getTimeshift(self):
788                 service = self.session.nav.getCurrentService()
789                 return service.timeshift()
790
791         def startTimeshift(self):
792                 # TODO: check for harddisk! (or do this in the interface? would make
793                 # more sense... for example radio could be timeshifted in memory,
794                 # and the decision can't be made here)
795                 print "enable timeshift"
796                 ts = self.getTimeshift()
797                 if ts is None:
798                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
799                         print "no ts interface"
800                         return
801                 
802                 if self.timeshift_enabled:
803                         print "hu, timeshift already enabled?"
804                 else:
805                         if not ts.startTimeshift():
806                                 self.timeshift_enabled = 1
807                                 
808                                 # PAUSE.
809                                 self.setSeekState(self.SEEK_STATE_PAUSE)
810                                 
811                                 # enable the "TimeshiftEnableActions", which will override
812                                 # the startTimeshift actions
813                                 self.__seekableStatusChanged()
814                         else:
815                                 print "timeshift failed"
816
817         # nyi
818         def stopTimeshift(self):
819                 print "disable timeshift"
820                 ts = self.getTimeshift()
821                 if ts is None:
822                         return
823                 ts.stopTimeshift()
824                 self.timeshift_enabled = 0
825
826                 # disable actions
827                 self.__seekableStatusChanged()
828         
829         # activates timeshift, and seeks to (almost) the end
830         def activateTimeshiftEnd(self):
831                 ts = self.getTimeshift()
832                 
833                 if ts is None:
834                         return
835                 
836                 if ts.isTimeshiftActive():
837                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
838                         self.pauseService()
839                 else:
840                         self.setSeekState(self.SEEK_STATE_PLAY)
841                         ts.activateTimeshift()
842         
843         # same as activateTimeshiftEnd, but pauses afterwards.
844         def activateTimeshiftEndAndPause(self):
845                 state = self.seekstate
846                 self.activateTimeshiftEnd()
847                 
848                 # well, this is "andPause", but it could be pressed from pause,
849                 # when pausing on the (fake-)"live" picture, so an un-pause
850                 # is perfectly ok.
851                 
852                 print "now, pauseService"
853                 if state == self.SEEK_STATE_PLAY:
854                         print "is PLAYING, start pause timer"
855                         self.ts_pause_timer.start(200, 1)
856                 else:
857                         print "unpause"
858                         self.unPauseService()
859         
860         def __seekableStatusChanged(self):
861                 enabled = False
862                 
863                 print "self.isSeekable", self.isSeekable()
864                 print "self.timeshift_enabled", self.timeshift_enabled
865                 
866                 # when this service is not seekable, but timeshift
867                 # is enabled, this means we can activate
868                 # the timeshift
869                 if not self.isSeekable() and self.timeshift_enabled:
870                         enabled = True
871
872                 print "timeshift activate:", enabled
873                 self["TimeshiftActivateActions"].setEnabled(enabled)
874
875 from RecordTimer import parseEvent
876
877 class InfoBarInstantRecord:
878         """Instant Record - handles the instantRecord action in order to 
879         start/stop instant records"""
880         def __init__(self):
881                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
882                         {
883                                 "instantRecord": (self.instantRecord, "Instant Record..."),
884                         })
885                 self.recording = None
886                 
887                 self["BlinkingPoint"] = BlinkingPixmapConditional()
888                 self.onShown.append(self["BlinkingPoint"].hideWidget)
889                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
890                 
891         def stopCurrentRecording(self): 
892                 self.session.nav.RecordTimer.removeEntry(self.recording)
893                 self.recording = None
894                         
895         def startInstantRecording(self):
896                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
897                 
898                 # try to get event info
899                 event = None
900                 try:
901                         service = self.session.nav.getCurrentService()
902                         info = service.info()
903                         ev = info.getEvent(0)
904                         event = ev
905                 except:
906                         pass
907                 
908                 if event is not None:
909                         data = parseEvent(event)
910                         begin = data[0]
911                         if begin < time.time():
912                                 begin = time.time()
913                         
914                         end = data[1]
915                         if end < begin:
916                                 end = begin
917                         
918                         end += 3600 * 10
919                         
920                         data = (begin, end, data[2], data[3], data[4])
921                 else:
922                         data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
923                 
924                 # fix me, description. 
925                 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
926                 self.recording.dontSave = True
927                 
928                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
929                 
930         def isInstantRecordRunning(self):
931                 if self.recording != None:
932                         if self.recording.isRunning():
933                                 return True
934                 return False
935
936         def recordQuestionCallback(self, answer):
937                 if answer == False:
938                         return
939                 
940                 if self.isInstantRecordRunning():
941                         self.stopCurrentRecording()
942                 else:
943                         self.startInstantRecording()
944
945         def instantRecord(self):
946                 try:
947                         stat = os.stat(resolveFilename(SCOPE_HDD))
948                 except:
949                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
950                         return
951         
952                 if self.isInstantRecordRunning():
953                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
954                 else:
955                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
956
957 from Screens.AudioSelection import AudioSelection
958
959 class InfoBarAudioSelection:
960         def __init__(self):
961                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
962                         {
963                                 "audioSelection": (self.audioSelection, "Audio Options..."),
964                         })
965
966         def audioSelection(self):
967                 service = self.session.nav.getCurrentService()
968                 audio = service.audioTracks()
969                 n = audio.getNumberOfTracks()
970                 if n > 0:
971                         self.session.open(AudioSelection, audio)
972
973 from Screens.SubserviceSelection import SubserviceSelection
974
975 class InfoBarSubserviceSelection:
976         def __init__(self):
977                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
978                         {
979                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
980                         })
981
982         def subserviceSelection(self):
983                 service = self.session.nav.getCurrentService()
984                 subservices = service.subServices()
985                 n = subservices.getNumberOfSubservices()
986                 if n > 0:
987                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
988
989         def subserviceSelected(self, service):
990                 if not service is None:
991                         self.session.nav.playService(service)
992
993 class InfoBarAdditionalInfo:
994         def __init__(self):
995                 self["DolbyActive"] = Pixmap()
996                 self["CryptActive"] = Pixmap()
997                 self["FormatActive"] = Pixmap()
998                 
999                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1000                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1001                 self.onShown.append(self["ButtonRed"].update)
1002                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1003                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1004                 self.onShown.append(self["ButtonRedText"].update)
1005
1006                 self["ButtonGreen"] = Pixmap()
1007                 self["ButtonGreenText"] = Label(_("Subservices"))
1008
1009                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1010                 self["ButtonYellow"].setConnect(lambda: False)
1011
1012                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1013                 self["ButtonBlue"].setConnect(lambda: False)
1014
1015                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1016
1017         def hideSubServiceIndication(self):
1018                 self["ButtonGreen"].hideWidget()
1019                 self["ButtonGreenText"].hide()
1020
1021         def showSubServiceIndication(self):
1022                 self["ButtonGreen"].showWidget()
1023                 self["ButtonGreenText"].show()
1024
1025         def checkFormat(self, service):
1026                 info = service.info()
1027                 if info is not None:
1028                         aspect = info.getInfo(iServiceInformation.sAspect)
1029                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1030                                 self["FormatActive"].showWidget()
1031                         else:
1032                                 self["FormatActive"].hideWidget()
1033
1034         def checkSubservices(self, service):
1035                 if service.subServices().getNumberOfSubservices() > 0:
1036                         self.showSubServiceIndication()
1037                 else:
1038                         self.hideSubServiceIndication()
1039
1040         def checkDolby(self, service):
1041                 # FIXME
1042                 dolby = False
1043                 audio = service.audioTracks()
1044                 if audio is not None:
1045                         n = audio.getNumberOfTracks()
1046                         for x in range(n):
1047                                 i = audio.getTrackInfo(x)
1048                                 description = i.getDescription();
1049                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1050                                         dolby = True
1051                                         break
1052                 if dolby:
1053                         self["DolbyActive"].showWidget()
1054                 else:
1055                         self["DolbyActive"].hideWidget()
1056
1057         def checkCrypted(self, service):
1058                 info = service.info()
1059                 if info is not None:
1060                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1061                                 self["CryptActive"].showWidget()
1062                         else:
1063                                 self["CryptActive"].hideWidget()
1064
1065         def gotServiceEvent(self, ev):
1066                 service = self.session.nav.getCurrentService()
1067                 if ev == pNavigation.evUpdatedEventInfo:
1068                         self.checkSubservices(service)
1069                         self.checkFormat(service)
1070                 elif ev == pNavigation.evUpdatedInfo:
1071                         self.checkCrypted(service)
1072                         self.checkDolby(service)
1073                 elif ev == pNavigation.evStopService:
1074                         self.hideSubServiceIndication()
1075                         self["CryptActive"].hideWidget()
1076                         self["DolbyActive"].hideWidget()
1077                         self["FormatActive"].hideWidget()
1078
1079 class InfoBarNotifications:
1080         def __init__(self):
1081                 self.onExecBegin.append(self.checkNotifications)
1082                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1083         
1084         def checkNotificationsIfExecing(self):
1085                 if self.execing:
1086                         self.checkNotifications()
1087
1088         def checkNotifications(self):
1089                 if len(Notifications.notifications):
1090                         n = Notifications.notifications[0]
1091                         Notifications.notifications = Notifications.notifications[1:]
1092                         print "open",n
1093                         cb = n[0]
1094                         if cb is not None:
1095                                 self.session.openWithCallback(cb, *n[1:])
1096                         else:
1097                                 self.session.open(*n[1:])
1098
1099 class InfoBarServiceNotifications:
1100         def __init__(self):
1101                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1102                         {
1103                                 pNavigation.evEnd: self.serviceHasEnded
1104                         })
1105
1106         def serviceHasEnded(self):
1107                 print "service end!"
1108
1109                 try:
1110                         self.setSeekState(self.SEEK_STATE_PLAY)
1111                 except:
1112                         pass