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