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