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