ee51eb9feb2a8deaa61eb93f742a550e5d3e45a3
[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 getSeek(self):
527                 service = self.session.nav.getCurrentService()
528                 if service is None:
529                         return False
530
531                 seek = service.seek()
532
533                 if seek is None or not seek.isCurrentlySeekable():
534                         return None
535                 
536                 return seek
537         
538         def isSeekable(self):
539                 if self.getSeek() is None:
540                         return False
541                 return True
542
543         def __seekableStatusChanged(self):
544                 print "seekable status changed!"
545                 if not self.isSeekable():
546                         self["SeekActions"].setEnabled(False)
547                         print "not seekable, return to play"
548                         self.setSeekState(self.SEEK_STATE_PLAY)
549                 else:
550                         self["SeekActions"].setEnabled(True)
551                         print "seekable"
552
553         def __serviceStarted(self):
554                 self.seekstate = self.SEEK_STATE_PLAY
555
556         def setSeekState(self, state):
557                 service = self.session.nav.getCurrentService()
558                 
559                 if service is None:
560                         return False
561                 
562                 if not self.isSeekable():
563                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
564                                 state = self.SEEK_STATE_PLAY
565                 
566                 pauseable = service.pause()
567
568                 if pauseable is None:
569                         print "not pauseable."
570                         state = self.SEEK_STATE_PLAY
571                 
572                 oldstate = self.seekstate
573                 self.seekstate = state
574                 
575                 for i in range(3):
576                         if oldstate[i] != self.seekstate[i]:
577                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
578
579                 for c in self.onPlayStateChanged:
580                         c(self.seekstate)
581                 
582                 self.checkSkipShowHideLock()
583
584                 return True
585                 
586         def pauseService(self):
587                 if self.seekstate == self.SEEK_STATE_PAUSE:
588                         print "pause, but in fact unpause"
589                         self.unPauseService()
590                 else:
591                         if self.seekstate == self.SEEK_STATE_PLAY:
592                                 print "yes, playing."
593                         else:
594                                 print "no", self.seekstate
595                         print "pause"
596                         self.setSeekState(self.SEEK_STATE_PAUSE);
597                 
598         def unPauseService(self):
599                 print "unpause"
600                 self.setSeekState(self.SEEK_STATE_PLAY);
601         
602         def doSeek(self, seektime):
603                 print "doseek", seektime
604                 service = self.session.nav.getCurrentService()
605                 if service is None:
606                         return
607                 
608                 seekable = self.getSeek()
609                 if seekable is None:
610                         return
611                 
612                 seekable.seekTo(90 * seektime)
613
614         def seekFwd(self):
615                 print "start fwd timer"
616                 self.fwdtimer = True
617                 self.fwdKeyTimer.start(500)
618
619         def seekBack(self):
620                 print "start rewind timer"
621                 self.rwdtimer = True
622                 self.rwdKeyTimer.start(500)
623
624         def seekFwdUp(self):
625                 print "seekFwdUp"
626                 if self.fwdtimer:
627                         self.fwdKeyTimer.stop()
628                         self.fwdtimer = False
629                         lookup = {
630                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
631                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
632                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
633                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
634                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
635                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
636                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
637                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
638                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_PLAY,
639                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_4X,
640                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
641                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
642                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
643                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
644                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
645                                 }
646                         self.setSeekState(lookup[self.seekstate]);
647         
648         def seekBackUp(self):
649                 print "seekBackUp"
650                 if self.rwdtimer:
651                         self.rwdKeyTimer.stop()
652                         self.rwdtimer = False
653                 
654                         lookup = {
655                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_4X,
656                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
657                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
658                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
659                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
660                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
661                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
662                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
663                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_BACK_32X,
664                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
665                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
666                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
667                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
668                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
669                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
670                                 }
671                         self.setSeekState(lookup[self.seekstate]);
672                 
673         def fwdTimerFire(self):
674                 print "Display seek fwd"
675                 self.fwdKeyTimer.stop()
676                 self.fwdtimer = False
677                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
678                 
679         def fwdSeekTo(self, minutes):
680                 print "Seek", minutes, "minutes forward"
681                 if minutes != 0:
682                         seekable = self.getSeek()
683                         if seekable is not None:
684                                 seekable.seekRelative(1, minutes * 60 * 90000)
685         
686         def rwdTimerFire(self):
687                 print "rwdTimerFire"
688                 self.rwdKeyTimer.stop()
689                 self.rwdtimer = False
690                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
691         
692         def rwdSeekTo(self, minutes):
693                 print "rwdSeekTo"
694                 self.fwdSeekTo(0 - minutes)
695         
696         def checkSkipShowHideLock(self):
697                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
698                 
699                 if self.lockedBecauseOfSkipping and not wantlock:
700                         self.unlockShow()
701                         self.lockedBecauseOfSkipping = False
702                 
703                 if wantlock and not self.lockedBecauseOfSkipping:
704                         self.lockShow()
705                         self.lockedBecauseOfSkipping = True
706
707         def __evEOF(self):
708                 self.setSeekState(self.SEEK_STATE_PAUSE)
709         
710         def __evSOF(self):
711                 self.setSeekState(self.SEEK_STATE_PLAY)
712                 self.doSeek(0)
713
714         def seekRelative(self, diff):
715                 seekable = self.getSeek()
716                 if seekable is not None:
717                         seekable.seekRelative(0, diff)
718
719 from Screens.PVRState import PVRState
720
721 class InfoBarPVRState:
722         def __init__(self):
723                 self.onPlayStateChanged.append(self.__playStateChanged)
724                 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
725                 self.onShow.append(self.__mayShow)
726                 self.onHide.append(self.pvrStateDialog.hide)
727         
728         def __mayShow(self):
729                 if self.seekstate != self.SEEK_STATE_PLAY:
730                         self.pvrStateDialog.show()
731
732         def __playStateChanged(self, state):
733                 playstateString = state[3]
734                 self.pvrStateDialog["state"].setText(playstateString)
735                 self.__mayShow()
736
737 class InfoBarShowMovies:
738
739         # i don't really like this class. 
740         # it calls a not further specified "movie list" on up/down/movieList,
741         # so this is not more than an action map
742         def __init__(self):
743                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
744                         {
745                                 "movieList": (self.showMovies, "movie list"),
746                                 "up": (self.showMovies, "movie list"),
747                                 "down": (self.showMovies, "movie list")
748                         })
749
750 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
751
752 # Hrmf.
753 #
754 # Timeshift works the following way:
755 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
756 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
757 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
758 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
759 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
760 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
761 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
762 #
763
764 # in other words:
765 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
766 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
767 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
768 # - the user can now PVR around
769 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
770 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
771 # after!
772 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
773 # - if the user rewinds, or press pause, timeshift will be activated again
774
775 # note that a timeshift can be enabled ("recording") and
776 # activated (currently time-shifting).
777
778 class InfoBarTimeshift:
779         def __init__(self):
780                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
781                         {
782                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
783                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
784                         }, prio=1)
785                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
786                         {
787                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
788                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
789                         }, prio=-1) # priority over record
790
791                 self.timeshift_enabled = 0
792                 self.timeshift_state = 0
793                 self.ts_pause_timer = eTimer()
794                 self.ts_pause_timer.timeout.get().append(self.pauseService)
795
796                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
797                         {
798                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
799                         })
800         
801         def getTimeshift(self):
802                 service = self.session.nav.getCurrentService()
803                 return service.timeshift()
804
805         def startTimeshift(self):
806                 print "enable timeshift"
807                 ts = self.getTimeshift()
808                 if ts is None:
809                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
810                         print "no ts interface"
811                         return
812                 
813                 if self.timeshift_enabled:
814                         print "hu, timeshift already enabled?"
815                 else:
816                         if not ts.startTimeshift():
817                                 self.timeshift_enabled = 1
818                                 
819                                 # PAUSE.
820                                 self.setSeekState(self.SEEK_STATE_PAUSE)
821                                 
822                                 # enable the "TimeshiftEnableActions", which will override
823                                 # the startTimeshift actions
824                                 self.__seekableStatusChanged()
825                         else:
826                                 print "timeshift failed"
827
828         def stopTimeshift(self):
829                 print "disable timeshift"
830                 ts = self.getTimeshift()
831                 if ts is None:
832                         return
833                 ts.stopTimeshift()
834                 self.timeshift_enabled = 0
835
836                 # disable actions
837                 self.__seekableStatusChanged()
838         
839         # activates timeshift, and seeks to (almost) the end
840         def activateTimeshiftEnd(self):
841                 ts = self.getTimeshift()
842                 
843                 if ts is None:
844                         return
845                 
846                 if ts.isTimeshiftActive():
847                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
848                         self.pauseService()
849                 else:
850                         self.setSeekState(self.SEEK_STATE_PLAY)
851                         ts.activateTimeshift()
852                         self.seekRelative(0)
853         
854         # same as activateTimeshiftEnd, but pauses afterwards.
855         def activateTimeshiftEndAndPause(self):
856                 state = self.seekstate
857                 self.activateTimeshiftEnd()
858                 
859                 # well, this is "andPause", but it could be pressed from pause,
860                 # when pausing on the (fake-)"live" picture, so an un-pause
861                 # is perfectly ok.
862                 
863                 print "now, pauseService"
864                 if state == self.SEEK_STATE_PLAY:
865                         print "is PLAYING, start pause timer"
866                         self.ts_pause_timer.start(200, 1)
867                 else:
868                         print "unpause"
869                         self.unPauseService()
870         
871         def __seekableStatusChanged(self):
872                 enabled = False
873                 
874                 print "self.isSeekable", self.isSeekable()
875                 print "self.timeshift_enabled", self.timeshift_enabled
876                 
877                 # when this service is not seekable, but timeshift
878                 # is enabled, this means we can activate
879                 # the timeshift
880                 if not self.isSeekable() and self.timeshift_enabled:
881                         enabled = True
882
883                 print "timeshift activate:", enabled
884                 self["TimeshiftActivateActions"].setEnabled(enabled)
885
886 from RecordTimer import parseEvent
887
888 class InfoBarInstantRecord:
889         """Instant Record - handles the instantRecord action in order to 
890         start/stop instant records"""
891         def __init__(self):
892                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
893                         {
894                                 "instantRecord": (self.instantRecord, "Instant Record..."),
895                         })
896                 self.recording = None
897                 
898                 self["BlinkingPoint"] = BlinkingPixmapConditional()
899                 self.onShown.append(self["BlinkingPoint"].hideWidget)
900                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
901                 
902         def stopCurrentRecording(self): 
903                 self.session.nav.RecordTimer.removeEntry(self.recording)
904                 self.recording = None
905                         
906         def startInstantRecording(self):
907                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
908                 
909                 # try to get event info
910                 event = None
911                 try:
912                         service = self.session.nav.getCurrentService()
913                         info = service.info()
914                         ev = info.getEvent(0)
915                         event = ev
916                 except:
917                         pass
918                 
919                 if event is not None:
920                         data = parseEvent(event)
921                         begin = data[0]
922                         if begin < time.time():
923                                 begin = time.time()
924                         
925                         end = data[1]
926                         if end < begin:
927                                 end = begin
928                         
929                         end += 3600 * 10
930                         
931                         data = (begin, end, data[2], data[3], data[4])
932                 else:
933                         data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
934                 
935                 # fix me, description. 
936                 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
937                 self.recording.dontSave = True
938                 
939                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
940                 
941         def isInstantRecordRunning(self):
942                 if self.recording != None:
943                         if self.recording.isRunning():
944                                 return True
945                 return False
946
947         def recordQuestionCallback(self, answer):
948                 if answer == False:
949                         return
950                 
951                 if self.isInstantRecordRunning():
952                         self.stopCurrentRecording()
953                 else:
954                         self.startInstantRecording()
955
956         def instantRecord(self):
957                 try:
958                         stat = os.stat(resolveFilename(SCOPE_HDD))
959                 except:
960                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
961                         return
962         
963                 if self.isInstantRecordRunning():
964                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
965                 else:
966                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
967
968 from Screens.AudioSelection import AudioSelection
969
970 class InfoBarAudioSelection:
971         def __init__(self):
972                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
973                         {
974                                 "audioSelection": (self.audioSelection, "Audio Options..."),
975                         })
976
977         def audioSelection(self):
978                 service = self.session.nav.getCurrentService()
979                 audio = service.audioTracks()
980                 n = audio.getNumberOfTracks()
981                 if n > 0:
982                         self.session.open(AudioSelection, audio)
983
984 from Screens.SubserviceSelection import SubserviceSelection
985
986 class InfoBarSubserviceSelection:
987         def __init__(self):
988                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
989                         {
990                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
991                         })
992
993         def subserviceSelection(self):
994                 service = self.session.nav.getCurrentService()
995                 subservices = service.subServices()
996                 n = subservices.getNumberOfSubservices()
997                 if n > 0:
998                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
999
1000         def subserviceSelected(self, service):
1001                 if not service is None:
1002                         self.session.nav.playService(service)
1003
1004 class InfoBarAdditionalInfo:
1005         def __init__(self):
1006                 self["DolbyActive"] = Pixmap()
1007                 self["CryptActive"] = Pixmap()
1008                 self["FormatActive"] = Pixmap()
1009                 
1010                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1011                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1012                 self.onShown.append(self["ButtonRed"].update)
1013                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1014                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1015                 self.onShown.append(self["ButtonRedText"].update)
1016
1017                 self["ButtonGreen"] = Pixmap()
1018                 self["ButtonGreenText"] = Label(_("Subservices"))
1019
1020                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1021                 self["ButtonYellow"].setConnect(lambda: False)
1022
1023                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1024                 self["ButtonBlue"].setConnect(lambda: False)
1025
1026                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1027
1028         def hideSubServiceIndication(self):
1029                 self["ButtonGreen"].hideWidget()
1030                 self["ButtonGreenText"].hide()
1031
1032         def showSubServiceIndication(self):
1033                 self["ButtonGreen"].showWidget()
1034                 self["ButtonGreenText"].show()
1035
1036         def checkFormat(self, service):
1037                 info = service.info()
1038                 if info is not None:
1039                         aspect = info.getInfo(iServiceInformation.sAspect)
1040                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1041                                 self["FormatActive"].showWidget()
1042                         else:
1043                                 self["FormatActive"].hideWidget()
1044
1045         def checkSubservices(self, service):
1046                 if service.subServices().getNumberOfSubservices() > 0:
1047                         self.showSubServiceIndication()
1048                 else:
1049                         self.hideSubServiceIndication()
1050
1051         def checkDolby(self, service):
1052                 # FIXME
1053                 dolby = False
1054                 audio = service.audioTracks()
1055                 if audio is not None:
1056                         n = audio.getNumberOfTracks()
1057                         for x in range(n):
1058                                 i = audio.getTrackInfo(x)
1059                                 description = i.getDescription();
1060                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1061                                         dolby = True
1062                                         break
1063                 if dolby:
1064                         self["DolbyActive"].showWidget()
1065                 else:
1066                         self["DolbyActive"].hideWidget()
1067
1068         def checkCrypted(self, service):
1069                 info = service.info()
1070                 if info is not None:
1071                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1072                                 self["CryptActive"].showWidget()
1073                         else:
1074                                 self["CryptActive"].hideWidget()
1075
1076         def gotServiceEvent(self, ev):
1077                 service = self.session.nav.getCurrentService()
1078                 if ev == iPlayableService.evUpdatedEventInfo:
1079                         self.checkSubservices(service)
1080                         self.checkFormat(service)
1081                 elif ev == iPlayableService.evUpdatedInfo:
1082                         self.checkCrypted(service)
1083                         self.checkDolby(service)
1084                 elif ev == iPlayableService.evEnd:
1085                         self.hideSubServiceIndication()
1086                         self["CryptActive"].hideWidget()
1087                         self["DolbyActive"].hideWidget()
1088                         self["FormatActive"].hideWidget()
1089
1090 class InfoBarNotifications:
1091         def __init__(self):
1092                 self.onExecBegin.append(self.checkNotifications)
1093                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1094         
1095         def checkNotificationsIfExecing(self):
1096                 if self.execing:
1097                         self.checkNotifications()
1098
1099         def checkNotifications(self):
1100                 if len(Notifications.notifications):
1101                         n = Notifications.notifications[0]
1102                         Notifications.notifications = Notifications.notifications[1:]
1103                         print "open",n
1104                         cb = n[0]
1105                         if cb is not None:
1106                                 self.session.openWithCallback(cb, *n[1:])
1107                         else:
1108                                 self.session.open(*n[1:])
1109
1110 class InfoBarServiceNotifications:
1111         def __init__(self):
1112                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1113                         {
1114                                 iPlayableService.evEnd: self.serviceHasEnded
1115                         })
1116
1117         def serviceHasEnded(self):
1118                 print "service end!"
1119
1120                 try:
1121                         self.setSeekState(self.SEEK_STATE_PLAY)
1122                 except:
1123                         pass