499d97232f3a3d5d738a81f9852f98ace2bb7761
[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.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                 self.__locked = 0
63                 
64                 self.onExecBegin.append(self.show)
65                 
66                 self.hideTimer = eTimer()
67                 self.hideTimer.timeout.get().append(self.doTimerHide)
68                 self.hideTimer.start(5000, True)
69                 
70                 self.onShow.append(self.__onShow)
71                 self.onHide.append(self.__onHide)
72
73         def __onShow(self):
74                 self.__state = self.STATE_SHOWN
75                 self.startHideTimer()
76         
77         def startHideTimer(self):
78                 if self.__state == self.STATE_SHOWN and not self.__locked:
79                         self.hideTimer.start(5000, True)
80
81         def __onHide(self):
82                 self.__state = self.STATE_HIDDEN
83
84         def doShow(self):
85                 self.show()
86                 self.startHideTimer()
87
88         def doTimerHide(self):
89                 self.hideTimer.stop()
90                 if self.__state == self.STATE_SHOWN:
91                         self.hide()
92
93         def toggleShow(self):
94                 if self.__state == self.STATE_SHOWN:
95                         self.hide()
96                         self.hideTimer.stop()
97                 elif self.__state == self.STATE_HIDDEN:
98                         self.show()
99
100         def lockShow(self):
101                 self.__locked = self.__locked + 1
102                 if self.execing:
103                         self.show()
104                         self.hideTimer.stop()
105         
106         def unlockShow(self):
107                 self.__locked = self.__locked - 1
108                 if self.execing:
109                         self.startHideTimer()
110
111 #       def startShow(self):
112 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
113 #               self.__state = self.STATE_SHOWN
114 #       
115 #       def startHide(self):
116 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
117 #               self.__state = self.STATE_HIDDEN
118
119 class NumberZap(Screen):
120         def quit(self):
121                 self.Timer.stop()
122                 self.close(0)
123
124         def keyOK(self):
125                 self.Timer.stop()
126                 self.close(int(self["number"].getText()))
127
128         def keyNumberGlobal(self, number):
129                 self.Timer.start(3000, True)            #reset timer
130                 self.field = self.field + str(number)
131                 self["number"].setText(self.field)
132                 if len(self.field) >= 4:
133                         self.keyOK()
134
135         def __init__(self, session, number):
136                 Screen.__init__(self, session)
137                 self.field = str(number)
138
139                 self["channel"] = Label(_("Channel:"))
140
141                 self["number"] = Label(self.field)
142
143                 self["actions"] = NumberActionMap( [ "SetupActions" ], 
144                         {
145                                 "cancel": self.quit,
146                                 "ok": self.keyOK,
147                                 "1": self.keyNumberGlobal,
148                                 "2": self.keyNumberGlobal,
149                                 "3": self.keyNumberGlobal,
150                                 "4": self.keyNumberGlobal,
151                                 "5": self.keyNumberGlobal,
152                                 "6": self.keyNumberGlobal,
153                                 "7": self.keyNumberGlobal,
154                                 "8": self.keyNumberGlobal,
155                                 "9": self.keyNumberGlobal,
156                                 "0": self.keyNumberGlobal
157                         })
158
159                 self.Timer = eTimer()
160                 self.Timer.timeout.get().append(self.keyOK)
161                 self.Timer.start(3000, True)
162
163 class InfoBarPowerKey:
164         """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
165         
166         def __init__(self):
167                 self.powerKeyTimer = eTimer()
168                 self.powerKeyTimer.timeout.get().append(self.powertimer)
169                 self["PowerKeyActions"] = HelpableActionMap(self, "PowerKeyActions",
170                         {
171                                 "powerdown": self.powerdown,
172                                 "powerup": self.powerup,
173                                 "discreteStandby": (self.standby, "Go standby"),
174                                 "discretePowerOff": (self.quit, "Go to deep standby"),
175                         })
176
177         def powertimer(self):   
178                 print "PowerOff - Now!"
179                 self.quit()
180         
181         def powerdown(self):
182                 self.standbyblocked = 0
183                 self.powerKeyTimer.start(3000, True)
184
185         def powerup(self):
186                 self.powerKeyTimer.stop()
187                 if self.standbyblocked == 0:
188                         self.standbyblocked = 1
189                         self.standby()
190
191         def standby(self):
192                 self.session.open(Standby, self)
193
194         def quit(self):
195                 # halt
196                 quitMainloop(1)
197
198 class InfoBarNumberZap:
199         """ Handles an initial number for NumberZapping """
200         def __init__(self):
201                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
202                         {
203                                 "1": self.keyNumberGlobal,
204                                 "2": self.keyNumberGlobal,
205                                 "3": self.keyNumberGlobal,
206                                 "4": self.keyNumberGlobal,
207                                 "5": self.keyNumberGlobal,
208                                 "6": self.keyNumberGlobal,
209                                 "7": self.keyNumberGlobal,
210                                 "8": self.keyNumberGlobal,
211                                 "9": self.keyNumberGlobal,
212                                 "0": self.keyNumberGlobal,
213                         })
214
215         def keyNumberGlobal(self, number):
216 #               print "You pressed number " + str(number)
217                 if number == 0:
218                         self.servicelist.recallPrevService()
219                         self.doShow()
220                 else:
221                         self.session.openWithCallback(self.numberEntered, NumberZap, number)
222
223         def numberEntered(self, retval):
224 #               print self.servicelist
225                 if retval > 0:
226                         self.zapToNumber(retval)
227
228         def searchNumberHelper(self, serviceHandler, num, bouquet):
229                 servicelist = serviceHandler.list(bouquet)
230                 if not servicelist is None:
231                         while num:
232                                 serviceIterator = servicelist.getNext()
233                                 if not serviceIterator.valid(): #check end of list
234                                         break
235                                 if serviceIterator.flags: #assume normal dvb service have no flags set
236                                         continue
237                                 num -= 1;
238                         if not num: #found service with searched number ?
239                                 return serviceIterator, 0
240                 return None, num
241
242         def zapToNumber(self, number):
243                 bouquet = self.servicelist.bouquet_root
244                 service = None
245                 serviceHandler = eServiceCenter.getInstance()
246                 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
247                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
248                 else:
249                         bouquetlist = serviceHandler.list(bouquet)
250                         if not bouquetlist is None:
251                                 while number:
252                                         bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
253                                         if not bouquet.valid(): #check end of list
254                                                 break
255                                         if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
256                                                 continue
257                                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
258                 if not service is None:
259                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
260                                 self.servicelist.clearPath()
261                                 if self.servicelist.bouquet_root != bouquet:
262                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
263                                 self.servicelist.enterPath(bouquet)
264                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
265                         self.servicelist.zap()
266
267 class InfoBarChannelSelection:
268         """ ChannelSelection - handles the channelSelection dialog and the initial 
269         channelChange actions which open the channelSelection dialog """
270         def __init__(self):
271                 #instantiate forever
272                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
273
274                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
275                         {
276                                 "switchChannelUp": self.switchChannelUp,
277                                 "switchChannelDown": self.switchChannelDown,
278                                 "zapUp": (self.zapUp, _("next channel")),
279                                 "zapDown": (self.zapDown, _("previous channel")),
280                         })
281
282         def switchChannelUp(self):
283                 self.servicelist.moveUp()
284                 self.session.execDialog(self.servicelist)
285
286         def switchChannelDown(self):
287                 self.servicelist.moveDown()
288                 self.session.execDialog(self.servicelist)
289
290         def zapUp(self):
291                 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
292                         if self.servicelist.inBouquet() and self.servicelist.atBegin():
293                                 self.servicelist.prevBouquet()
294                 self.servicelist.moveUp()
295                 self.servicelist.zap()
296                 self.doShow()
297
298         def zapDown(self):
299                 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
300                         self.servicelist.nextBouquet()
301                 else:
302                         self.servicelist.moveDown()
303                 self.servicelist.zap()
304                 self.doShow()
305
306 class InfoBarMenu:
307         """ Handles a menu action, to open the (main) menu """
308         def __init__(self):
309                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions", 
310                         {
311                                 "mainMenu": (self.mainMenu, "Enter main menu..."),
312                         })
313
314         def mainMenu(self):
315                 print "loading mainmenu XML..."
316                 menu = mdom.childNodes[0]
317                 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
318                 self.session.open(MainMenu, menu, menu.childNodes)
319
320 class InfoBarEPG:
321         """ EPG - Opens an EPG list when the showEPGList action fires """
322         def __init__(self):
323                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
324                         {
325                                 "showEventInfo": (self.openEventView, _("show EPG...")),
326                         })
327
328         def zapToService(self, service):
329                 if not service is None:
330                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
331                                 self.servicelist.clearPath()
332                                 if self.servicelist.bouquet_root != self.epg_bouquet:
333                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
334                                 self.servicelist.enterPath(self.epg_bouquet)
335                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
336                         self.servicelist.zap()
337
338         def openBouquetEPG(self, bouquet):
339                 ptr=eEPGCache.getInstance()
340                 services = [ ]
341                 servicelist = eServiceCenter.getInstance().list(bouquet)
342                 if not servicelist is None:
343                         while True:
344                                 service = servicelist.getNext()
345                                 if not service.valid(): #check if end of list
346                                         break
347                                 if service.flags: #ignore non playable services
348                                         continue
349                                 services.append(ServiceReference(service))
350                 if len(services):
351                         self.epg_bouquet = bouquet
352                         self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
353
354         def closed(self, ret):
355                 if ret:
356                         self.close(ret)
357
358         def openMultiServiceEPG(self):
359                 bouquets = self.servicelist.getBouquetList()
360                 if bouquets is None:
361                         cnt = 0
362                 else:
363                         cnt = len(bouquets)
364                 if cnt > 1: # show bouquet list
365                         self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
366                 elif cnt == 1: 
367                         self.openBouquetEPG(bouquets[0][1])
368
369         def openSingleServiceEPG(self):
370                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
371                 ptr=eEPGCache.getInstance()
372                 self.session.openWithCallback(self.closed, EPGSelection, ref)
373
374         def openEventView(self):
375                 self.epglist = [ ]
376                 service = self.session.nav.getCurrentService()
377                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
378                 info = service.info()
379                 ptr=info.getEvent(0)
380                 if ptr:
381                         self.epglist.append(ptr)
382                 ptr=info.getEvent(1)
383                 if ptr:
384                         self.epglist.append(ptr)
385                 if len(self.epglist) == 0:
386                         epg = eEPGCache.getInstance()
387                         ptr = epg.lookupEventTime(ref, -1)
388                         if ptr:
389                                 self.epglist.append(ptr)
390                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
391                                 if ptr:
392                                         self.epglist.append(ptr)
393                 if len(self.epglist) > 0:
394                         self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
395                 else:
396                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
397                         self.openMultiServiceEPG()
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
472         SEEK_STATE_PLAY = (0, 0, 0, ">")
473         SEEK_STATE_PAUSE = (1, 0, 0, "||")
474         SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
475         SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
476         SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
477         SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
478         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
479         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
480         
481         SEEK_STATE_BACK_4X = (0, -4, 0, "<< 4x")
482         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
483         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
484         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
485         
486         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
487         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
488         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
489         
490         def __init__(self):
491                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
492                         {
493                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
494                                 iPlayableService.evStart: self.__serviceStarted,
495                                 
496                                 iPlayableService.evEOF: self.__evEOF,
497                                 iPlayableService.evSOF: self.__evSOF,
498                         })
499                 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions", 
500                         {
501                                 "pauseService": (self.pauseService, "pause"),
502                                 "unPauseService": (self.unPauseService, "continue"),
503                                 
504                                 "seekFwd": (self.seekFwd, "skip forward"),
505                                 "seekFwdUp": (self.seekFwdUp, "skip forward"),
506                                 "seekBack": (self.seekBack, "skip backward"),
507                                 "seekBackUp": (self.seekBackUp, "skip backward"),
508                         }, prio=-1)
509                         # give them a little more priority to win over color buttons
510
511                 self.seekstate = self.SEEK_STATE_PLAY
512                 self.onClose.append(self.delTimer)
513                 
514                 self.fwdtimer = False
515                 self.fwdKeyTimer = eTimer()
516                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
517
518                 self.rwdtimer = False
519                 self.rwdKeyTimer = eTimer()
520                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
521                 
522                 self.onPlayStateChanged = [ ]
523                 
524                 self.lockedBecauseOfSkipping = False
525         
526         def up(self):
527                 pass
528         
529         def down(self):
530                 pass
531         
532         def delTimer(self):
533                 del self.fwdKeyTimer
534                 del self.rwdKeyTimer
535         
536         def getSeek(self):
537                 service = self.session.nav.getCurrentService()
538                 if service is None:
539                         return False
540
541                 seek = service.seek()
542
543                 if seek is None or not seek.isCurrentlySeekable():
544                         return None
545                 
546                 return seek
547         
548         def isSeekable(self):
549                 if self.getSeek() is None:
550                         return False
551                 return True
552
553         def __seekableStatusChanged(self):
554                 print "seekable status changed!"
555                 if not self.isSeekable():
556                         self["SeekActions"].setEnabled(False)
557                         print "not seekable, return to play"
558                         self.setSeekState(self.SEEK_STATE_PLAY)
559                 else:
560                         self["SeekActions"].setEnabled(True)
561                         print "seekable"
562
563         def __serviceStarted(self):
564                 self.seekstate = self.SEEK_STATE_PLAY
565
566         def setSeekState(self, state):
567                 service = self.session.nav.getCurrentService()
568                 
569                 if service is None:
570                         return False
571                 
572                 if not self.isSeekable():
573                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
574                                 state = self.SEEK_STATE_PLAY
575                 
576                 pauseable = service.pause()
577
578                 if pauseable is None:
579                         print "not pauseable."
580                         state = self.SEEK_STATE_PLAY
581                 
582                 oldstate = self.seekstate
583                 self.seekstate = state
584                 
585                 for i in range(3):
586                         if oldstate[i] != self.seekstate[i]:
587                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
588
589                 for c in self.onPlayStateChanged:
590                         c(self.seekstate)
591                 
592                 self.checkSkipShowHideLock()
593
594                 return True
595                 
596         def pauseService(self):
597                 if self.seekstate == self.SEEK_STATE_PAUSE:
598                         print "pause, but in fact unpause"
599                         self.unPauseService()
600                 else:
601                         if self.seekstate == self.SEEK_STATE_PLAY:
602                                 print "yes, playing."
603                         else:
604                                 print "no", self.seekstate
605                         print "pause"
606                         self.setSeekState(self.SEEK_STATE_PAUSE);
607                 
608         def unPauseService(self):
609                 print "unpause"
610                 self.setSeekState(self.SEEK_STATE_PLAY);
611         
612         def doSeek(self, seektime):
613                 print "doseek", seektime
614                 service = self.session.nav.getCurrentService()
615                 if service is None:
616                         return
617                 
618                 seekable = self.getSeek()
619                 if seekable is None:
620                         return
621                 
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                         seekable = self.getSeek()
693                         if seekable is not None:
694                                 seekable.seekRelative(1, minutes * 60 * 90000)
695         
696         def rwdTimerFire(self):
697                 print "rwdTimerFire"
698                 self.rwdKeyTimer.stop()
699                 self.rwdtimer = False
700                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
701         
702         def rwdSeekTo(self, minutes):
703                 print "rwdSeekTo"
704                 self.fwdSeekTo(0 - minutes)
705         
706         def checkSkipShowHideLock(self):
707                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
708                 
709                 if self.lockedBecauseOfSkipping and not wantlock:
710                         self.unlockShow()
711                         self.lockedBecauseOfSkipping = False
712                 
713                 if wantlock and not self.lockedBecauseOfSkipping:
714                         self.lockShow()
715                         self.lockedBecauseOfSkipping = True
716
717         def __evEOF(self):
718                 self.setSeekState(self.SEEK_STATE_PAUSE)
719         
720         def __evSOF(self):
721                 self.setSeekState(self.SEEK_STATE_PLAY)
722                 self.doSeek(0)
723
724         def seekRelative(self, diff):
725                 seekable = self.getSeek()
726                 if seekable is not None:
727                         seekable.seekRelative(0, diff)
728
729 from Screens.PVRState import PVRState
730
731 class InfoBarPVRState:
732         def __init__(self):
733                 self.onPlayStateChanged.append(self.__playStateChanged)
734                 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
735                 self.onShow.append(self.__mayShow)
736                 self.onHide.append(self.pvrStateDialog.hide)
737         
738         def __mayShow(self):
739                 if self.seekstate != self.SEEK_STATE_PLAY:
740                         self.pvrStateDialog.show()
741
742         def __playStateChanged(self, state):
743                 playstateString = state[3]
744                 self.pvrStateDialog["state"].setText(playstateString)
745                 self.__mayShow()
746
747 class InfoBarShowMovies:
748
749         # i don't really like this class. 
750         # it calls a not further specified "movie list" on up/down/movieList,
751         # so this is not more than an action map
752         def __init__(self):
753                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
754                         {
755                                 "movieList": (self.showMovies, "movie list"),
756                                 "up": (self.showMovies, "movie list"),
757                                 "down": (self.showMovies, "movie list")
758                         })
759
760 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
761
762 # Hrmf.
763 #
764 # Timeshift works the following way:
765 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
766 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
767 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
768 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
769 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
770 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
771 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
772 #
773
774 # in other words:
775 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
776 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
777 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
778 # - the user can now PVR around
779 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
780 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
781 # after!
782 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
783 # - if the user rewinds, or press pause, timeshift will be activated again
784
785 # note that a timeshift can be enabled ("recording") and
786 # activated (currently time-shifting).
787
788 class InfoBarTimeshift:
789         def __init__(self):
790                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
791                         {
792                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
793                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
794                         }, prio=1)
795                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
796                         {
797                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
798                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
799                         }, prio=-1) # priority over record
800
801                 self.timeshift_enabled = 0
802                 self.timeshift_state = 0
803                 self.ts_pause_timer = eTimer()
804                 self.ts_pause_timer.timeout.get().append(self.pauseService)
805
806                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
807                         {
808                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
809                         })
810         
811         def getTimeshift(self):
812                 service = self.session.nav.getCurrentService()
813                 return service.timeshift()
814
815         def startTimeshift(self):
816                 print "enable timeshift"
817                 ts = self.getTimeshift()
818                 if ts is None:
819                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
820                         print "no ts interface"
821                         return
822                 
823                 if self.timeshift_enabled:
824                         print "hu, timeshift already enabled?"
825                 else:
826                         if not ts.startTimeshift():
827                                 self.timeshift_enabled = 1
828                                 
829                                 # PAUSE.
830                                 self.setSeekState(self.SEEK_STATE_PAUSE)
831                                 
832                                 # enable the "TimeshiftEnableActions", which will override
833                                 # the startTimeshift actions
834                                 self.__seekableStatusChanged()
835                         else:
836                                 print "timeshift failed"
837
838         def stopTimeshift(self):
839                 print "disable timeshift"
840                 ts = self.getTimeshift()
841                 if ts is None:
842                         return
843                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
844
845         def stopTimeshiftConfirmed(self, confirmed):
846                 if not confirmed:
847                         return
848
849                 ts = self.getTimeshift()
850                 if ts is None:
851                         return
852
853                 ts.stopTimeshift()
854                 self.timeshift_enabled = 0
855
856                 # disable actions
857                 self.__seekableStatusChanged()
858         
859         # activates timeshift, and seeks to (almost) the end
860         def activateTimeshiftEnd(self):
861                 ts = self.getTimeshift()
862                 
863                 if ts is None:
864                         return
865                 
866                 if ts.isTimeshiftActive():
867                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
868                         self.pauseService()
869                 else:
870                         self.setSeekState(self.SEEK_STATE_PLAY)
871                         ts.activateTimeshift()
872                         self.seekRelative(0)
873         
874         # same as activateTimeshiftEnd, but pauses afterwards.
875         def activateTimeshiftEndAndPause(self):
876                 state = self.seekstate
877                 self.activateTimeshiftEnd()
878                 
879                 # well, this is "andPause", but it could be pressed from pause,
880                 # when pausing on the (fake-)"live" picture, so an un-pause
881                 # is perfectly ok.
882                 
883                 print "now, pauseService"
884                 if state == self.SEEK_STATE_PLAY:
885                         print "is PLAYING, start pause timer"
886                         self.ts_pause_timer.start(200, 1)
887                 else:
888                         print "unpause"
889                         self.unPauseService()
890         
891         def __seekableStatusChanged(self):
892                 enabled = False
893                 
894                 print "self.isSeekable", self.isSeekable()
895                 print "self.timeshift_enabled", self.timeshift_enabled
896                 
897                 # when this service is not seekable, but timeshift
898                 # is enabled, this means we can activate
899                 # the timeshift
900                 if not self.isSeekable() and self.timeshift_enabled:
901                         enabled = True
902
903                 print "timeshift activate:", enabled
904                 self["TimeshiftActivateActions"].setEnabled(enabled)
905
906 from RecordTimer import parseEvent
907
908 class InfoBarInstantRecord:
909         """Instant Record - handles the instantRecord action in order to 
910         start/stop instant records"""
911         def __init__(self):
912                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
913                         {
914                                 "instantRecord": (self.instantRecord, "Instant Record..."),
915                         })
916                 self.recording = None
917                 
918                 self["BlinkingPoint"] = BlinkingPixmapConditional()
919                 self.onShown.append(self["BlinkingPoint"].hideWidget)
920                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
921                 
922         def stopCurrentRecording(self): 
923                 self.session.nav.RecordTimer.removeEntry(self.recording)
924                 self.recording = None
925                         
926         def startInstantRecording(self):
927                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
928                 
929                 # try to get event info
930                 event = None
931                 try:
932                         service = self.session.nav.getCurrentService()
933                         info = service.info()
934                         ev = info.getEvent(0)
935                         event = ev
936                 except:
937                         pass
938                 
939                 if event is not None:
940                         data = parseEvent(event)
941                         begin = time.time()
942                         end = begin + 3600 * 10
943                         
944                         data = (begin, end, data[2], data[3], data[4])
945                 else:
946                         data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
947                 
948                 # fix me, description. 
949                 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
950                 self.recording.dontSave = True
951                 
952                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
953                 
954         def isInstantRecordRunning(self):
955                 if self.recording != None:
956                         if self.recording.isRunning():
957                                 return True
958                 return False
959
960         def recordQuestionCallback(self, answer):
961                 if answer == False:
962                         return
963                 
964                 if self.isInstantRecordRunning():
965                         self.stopCurrentRecording()
966                 else:
967                         self.startInstantRecording()
968
969         def instantRecord(self):
970                 try:
971                         stat = os.stat(resolveFilename(SCOPE_HDD))
972                 except:
973                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
974                         return
975         
976                 if self.isInstantRecordRunning():
977                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
978                 else:
979                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
980
981 from Screens.AudioSelection import AudioSelection
982
983 class InfoBarAudioSelection:
984         def __init__(self):
985                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
986                         {
987                                 "audioSelection": (self.audioSelection, "Audio Options..."),
988                         })
989
990         def audioSelection(self):
991                 service = self.session.nav.getCurrentService()
992                 audio = service.audioTracks()
993                 n = audio.getNumberOfTracks()
994                 if n > 0:
995                         self.session.open(AudioSelection, audio)
996
997 from Screens.SubserviceSelection import SubserviceSelection
998
999 class InfoBarSubserviceSelection:
1000         def __init__(self):
1001                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1002                         {
1003                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1004                         })
1005
1006         def subserviceSelection(self):
1007                 service = self.session.nav.getCurrentService()
1008                 subservices = service.subServices()
1009                 n = subservices.getNumberOfSubservices()
1010                 if n > 0:
1011                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1012
1013         def subserviceSelected(self, service):
1014                 if not service is None:
1015                         self.session.nav.playService(service)
1016
1017 class InfoBarAdditionalInfo:
1018         def __init__(self):
1019                 self["DolbyActive"] = Pixmap()
1020                 self["CryptActive"] = Pixmap()
1021                 self["FormatActive"] = Pixmap()
1022                 
1023                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1024                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1025                 self.onShown.append(self["ButtonRed"].update)
1026                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1027                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1028                 self.onShown.append(self["ButtonRedText"].update)
1029
1030                 self["ButtonGreen"] = Pixmap()
1031                 self["ButtonGreenText"] = Label(_("Subservices"))
1032
1033                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1034                 self["ButtonYellow"].setConnect(lambda: False)
1035
1036                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1037                 self["ButtonBlue"].setConnect(lambda: False)
1038
1039                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1040
1041         def hideSubServiceIndication(self):
1042                 self["ButtonGreen"].hideWidget()
1043                 self["ButtonGreenText"].hide()
1044
1045         def showSubServiceIndication(self):
1046                 self["ButtonGreen"].showWidget()
1047                 self["ButtonGreenText"].show()
1048
1049         def checkFormat(self, service):
1050                 info = service.info()
1051                 if info is not None:
1052                         aspect = info.getInfo(iServiceInformation.sAspect)
1053                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1054                                 self["FormatActive"].showWidget()
1055                         else:
1056                                 self["FormatActive"].hideWidget()
1057
1058         def checkSubservices(self, service):
1059                 if service.subServices().getNumberOfSubservices() > 0:
1060                         self.showSubServiceIndication()
1061                 else:
1062                         self.hideSubServiceIndication()
1063
1064         def checkDolby(self, service):
1065                 # FIXME
1066                 dolby = False
1067                 audio = service.audioTracks()
1068                 if audio is not None:
1069                         n = audio.getNumberOfTracks()
1070                         for x in range(n):
1071                                 i = audio.getTrackInfo(x)
1072                                 description = i.getDescription();
1073                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1074                                         dolby = True
1075                                         break
1076                 if dolby:
1077                         self["DolbyActive"].showWidget()
1078                 else:
1079                         self["DolbyActive"].hideWidget()
1080
1081         def checkCrypted(self, service):
1082                 info = service.info()
1083                 if info is not None:
1084                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1085                                 self["CryptActive"].showWidget()
1086                         else:
1087                                 self["CryptActive"].hideWidget()
1088
1089         def gotServiceEvent(self, ev):
1090                 service = self.session.nav.getCurrentService()
1091                 if ev == iPlayableService.evUpdatedEventInfo:
1092                         self.checkSubservices(service)
1093                         self.checkFormat(service)
1094                 elif ev == iPlayableService.evUpdatedInfo:
1095                         self.checkCrypted(service)
1096                         self.checkDolby(service)
1097                 elif ev == iPlayableService.evEnd:
1098                         self.hideSubServiceIndication()
1099                         self["CryptActive"].hideWidget()
1100                         self["DolbyActive"].hideWidget()
1101                         self["FormatActive"].hideWidget()
1102
1103 class InfoBarNotifications:
1104         def __init__(self):
1105                 self.onExecBegin.append(self.checkNotifications)
1106                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1107         
1108         def checkNotificationsIfExecing(self):
1109                 if self.execing:
1110                         self.checkNotifications()
1111
1112         def checkNotifications(self):
1113                 if len(Notifications.notifications):
1114                         n = Notifications.notifications[0]
1115                         Notifications.notifications = Notifications.notifications[1:]
1116                         print "open",n
1117                         cb = n[0]
1118                         if cb is not None:
1119                                 self.session.openWithCallback(cb, *n[1:])
1120                         else:
1121                                 self.session.open(*n[1:])
1122
1123 class InfoBarServiceNotifications:
1124         def __init__(self):
1125                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1126                         {
1127                                 iPlayableService.evEnd: self.serviceHasEnded
1128                         })
1129
1130         def serviceHasEnded(self):
1131                 print "service end!"
1132
1133                 try:
1134                         self.setSeekState(self.SEEK_STATE_PLAY)
1135                 except:
1136                         pass