seek to start when bouncing at SOF
[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                 service = self.session.nav.getCurrentService()
708                 if service is None:
709                         return
710                 seekable = service.seek()
711                 if seekable is None:
712                         return
713                 seekable.seekRelative(0, 0)
714                 
715
716 from Screens.PVRState import PVRState
717
718 class InfoBarPVRState:
719         def __init__(self):
720                 self.onPlayStateChanged.append(self.__playStateChanged)
721                 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
722                 self.onShow.append(self.__mayShow)
723                 self.onHide.append(self.pvrStateDialog.hide)
724         
725         def __mayShow(self):
726                 if self.seekstate != self.SEEK_STATE_PLAY:
727                         self.pvrStateDialog.show()
728
729         def __playStateChanged(self, state):
730                 playstateString = state[3]
731                 self.pvrStateDialog["state"].setText(playstateString)
732                 self.__mayShow()
733
734 class InfoBarShowMovies:
735
736         # i don't really like this class. 
737         # it calls a not further specified "movie list" on up/down/movieList,
738         # so this is not more than an action map
739         def __init__(self):
740                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
741                         {
742                                 "movieList": (self.showMovies, "movie list"),
743                                 "up": (self.showMovies, "movie list"),
744                                 "down": (self.showMovies, "movie list")
745                         })
746
747 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
748
749 # Hrmf.
750 #
751 # Timeshift works the following way:
752 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
753 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
754 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
755 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
756 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
757 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
758 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
759 #
760
761 # in other words:
762 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
763 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
764 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
765 # - the user can now PVR around
766 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
767 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
768 # after!
769 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
770 # - if the user rewinds, or press pause, timeshift will be activated again
771
772 # note that a timeshift can be enabled ("recording") and
773 # activated (currently time-shifting).
774
775 class InfoBarTimeshift:
776         def __init__(self):
777                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
778                         {
779                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
780                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
781                         }, prio=1)
782                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
783                         {
784                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
785                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
786                         })
787
788                 self.timeshift_enabled = 0
789                 self.timeshift_state = 0
790                 self.ts_pause_timer = eTimer()
791                 self.ts_pause_timer.timeout.get().append(self.pauseService)
792
793                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
794                         {
795                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
796                         })
797         
798         def getTimeshift(self):
799                 service = self.session.nav.getCurrentService()
800                 return service.timeshift()
801
802         def startTimeshift(self):
803                 # TODO: check for harddisk! (or do this in the interface? would make
804                 # more sense... for example radio could be timeshifted in memory,
805                 # and the decision can't be made here)
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         # nyi
829         def stopTimeshift(self):
830                 print "disable timeshift"
831                 ts = self.getTimeshift()
832                 if ts is None:
833                         return
834                 ts.stopTimeshift()
835                 self.timeshift_enabled = 0
836
837                 # disable actions
838                 self.__seekableStatusChanged()
839         
840         # activates timeshift, and seeks to (almost) the end
841         def activateTimeshiftEnd(self):
842                 ts = self.getTimeshift()
843                 
844                 if ts is None:
845                         return
846                 
847                 if ts.isTimeshiftActive():
848                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
849                         self.pauseService()
850                 else:
851                         self.setSeekState(self.SEEK_STATE_PLAY)
852                         ts.activateTimeshift()
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