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