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