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