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