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