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