358ef44d87eee53733ee6a55ae65be9b4dbad498
[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.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1053                         self.startInstantRecording(limitEvent = limitEvent)
1054
1055         def changeDuration(self, entry):
1056                 if entry is not None:
1057                         self.selectedEntry = entry
1058                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1059
1060         def inputCallback(self, value):
1061                 if value is not None:
1062                         print "stopping recording after", int(value), "minutes."
1063                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1064                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1065
1066         def instantRecord(self):
1067                 try:
1068                         stat = os.stat(resolveFilename(SCOPE_HDD))
1069                 except:
1070                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1071                         return
1072         
1073                 if self.isInstantRecordRunning():
1074                         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")])
1075                 else:
1076                         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")])
1077
1078 from Screens.AudioSelection import AudioSelection
1079
1080 class InfoBarAudioSelection:
1081         def __init__(self):
1082                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1083                         {
1084                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1085                         })
1086
1087         def audioSelection(self):
1088                 service = self.session.nav.getCurrentService()
1089                 audio = service.audioTracks()
1090                 n = audio.getNumberOfTracks()
1091                 if n > 0:
1092                         self.session.open(AudioSelection, audio)
1093
1094 from Screens.SubserviceSelection import SubserviceSelection
1095
1096 class InfoBarSubserviceSelection:
1097         def __init__(self):
1098                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1099                         {
1100                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1101                         })
1102
1103         def subserviceSelection(self):
1104                 service = self.session.nav.getCurrentService()
1105                 subservices = service.subServices()
1106                 n = subservices.getNumberOfSubservices()
1107                 if n > 0:
1108                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1109
1110         def subserviceSelected(self, service):
1111                 if not service is None:
1112                         self.session.nav.playService(service)
1113
1114 class InfoBarAdditionalInfo:
1115         def __init__(self):
1116                 self["DolbyActive"] = Pixmap()
1117                 self["CryptActive"] = Pixmap()
1118                 self["FormatActive"] = Pixmap()
1119                 
1120                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1121                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1122                 self.onLayoutFinish.append(self["ButtonRed"].update)
1123                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1124                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1125                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1126
1127                 self["ButtonGreen"] = Pixmap()
1128                 self["ButtonGreenText"] = Label(_("Subservices"))
1129
1130                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1131                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1132                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1133                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1134                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1135                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1136
1137                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1138                 self["ButtonBlue"].setConnect(lambda: False)
1139                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1140                 self["ButtonBlueText"].setConnect(lambda: False)
1141                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1142                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1143
1144                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1145
1146         def hideSubServiceIndication(self):
1147                 self["ButtonGreen"].hide()
1148                 self["ButtonGreenText"].hide()
1149
1150         def showSubServiceIndication(self):
1151                 self["ButtonGreen"].show()
1152                 self["ButtonGreenText"].show()
1153
1154         def checkFormat(self, service):
1155                 info = service.info()
1156                 if info is not None:
1157                         aspect = info.getInfo(iServiceInformation.sAspect)
1158                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1159                                 self["FormatActive"].show()
1160                         else:
1161                                 self["FormatActive"].hide()
1162
1163         def checkSubservices(self, service):
1164                 if service.subServices().getNumberOfSubservices() > 0:
1165                         self.showSubServiceIndication()
1166                 else:
1167                         self.hideSubServiceIndication()
1168
1169         def checkDolby(self, service):
1170                 # FIXME
1171                 dolby = False
1172                 audio = service.audioTracks()
1173                 if audio is not None:
1174                         n = audio.getNumberOfTracks()
1175                         for x in range(n):
1176                                 i = audio.getTrackInfo(x)
1177                                 description = i.getDescription();
1178                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1179                                         dolby = True
1180                                         break
1181                 if dolby:
1182                         self["DolbyActive"].show()
1183                 else:
1184                         self["DolbyActive"].hide()
1185
1186         def checkCrypted(self, service):
1187                 info = service.info()
1188                 if info is not None:
1189                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1190                                 self["CryptActive"].show()
1191                         else:
1192                                 self["CryptActive"].hide()
1193
1194         def gotServiceEvent(self, ev):
1195                 service = self.session.nav.getCurrentService()
1196                 if ev == iPlayableService.evUpdatedEventInfo:
1197                         self.checkSubservices(service)
1198                         self.checkFormat(service)
1199                 elif ev == iPlayableService.evUpdatedInfo:
1200                         self.checkCrypted(service)
1201                         self.checkDolby(service)
1202                 elif ev == iPlayableService.evEnd:
1203                         self.hideSubServiceIndication()
1204                         self["CryptActive"].hide()
1205                         self["DolbyActive"].hide()
1206                         self["FormatActive"].hide()
1207
1208 class InfoBarNotifications:
1209         def __init__(self):
1210                 self.onExecBegin.append(self.checkNotifications)
1211                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1212         
1213         def checkNotificationsIfExecing(self):
1214                 if self.execing:
1215                         self.checkNotifications()
1216
1217         def checkNotifications(self):
1218                 if len(Notifications.notifications):
1219                         n = Notifications.notifications[0]
1220                         Notifications.notifications = Notifications.notifications[1:]
1221                         print "open",n
1222                         cb = n[0]
1223                         if cb is not None:
1224                                 self.session.openWithCallback(cb, *n[1:])
1225                         else:
1226                                 self.session.open(*n[1:])
1227
1228 class InfoBarServiceNotifications:
1229         def __init__(self):
1230                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1231                         {
1232                                 iPlayableService.evEnd: self.serviceHasEnded
1233                         })
1234
1235         def serviceHasEnded(self):
1236                 print "service end!"
1237
1238                 try:
1239                         self.setSeekState(self.SEEK_STATE_PLAY)
1240                 except:
1241                         pass
1242
1243 class InfoBarCueSheetSupport:
1244         CUT_TYPE_IN = 0
1245         CUT_TYPE_OUT = 1
1246         CUT_TYPE_MARK = 2
1247         
1248         def __init__(self):
1249                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1250                         {
1251                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1252                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1253                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1254                         }, prio=1) 
1255                 
1256                 self.cut_list = [ ]
1257                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1258                         {
1259                                 iPlayableService.evStart: self.__serviceStarted,
1260                         })
1261
1262         def __serviceStarted(self):
1263                 print "new service started! trying to download cuts!"
1264                 self.downloadCuesheet()
1265
1266         def __getSeekable(self):
1267                 service = self.session.nav.getCurrentService()
1268                 if service is None:
1269                         return None
1270                 return service.seek()
1271
1272         def cueGetCurrentPosition(self):
1273                 seek = self.__getSeekable()
1274                 if seek is None:
1275                         return None
1276                 r = seek.getPlayPosition()
1277                 if r[0]:
1278                         return None
1279                 return long(r[1])
1280
1281         def jumpPreviousNextMark(self, cmp, alternative=None):
1282                 current_pos = self.cueGetCurrentPosition()
1283                 if current_pos is None:
1284                         return
1285                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1286                 if mark is not None:
1287                         pts = mark[0]
1288                 elif alternative is not None:
1289                         pts = alternative
1290                 else:
1291                         return
1292
1293                 seekable = self.__getSeekable()
1294                 if seekable is not None:
1295                         seekable.seekTo(pts)
1296
1297         def jumpPreviousMark(self):
1298                 # we add 2 seconds, so if the play position is <2s after
1299                 # the mark, the mark before will be used
1300                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1301
1302         def jumpNextMark(self):
1303                 self.jumpPreviousNextMark(lambda x: x)
1304
1305         def getNearestCutPoint(self, pts, cmp=abs):
1306                 # can be optimized
1307                 nearest = None
1308                 for cp in self.cut_list:
1309                         diff = cmp(cp[0] - pts)
1310                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1311                                 nearest = cp
1312                 return nearest
1313
1314         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1315                 current_pos = self.cueGetCurrentPosition()
1316                 if current_pos is None:
1317                         print "not seekable"
1318                         return
1319                 
1320                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1321                 
1322                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1323                         if onlyreturn:
1324                                 return nearest_cutpoint
1325                         if not onlyadd:
1326                                 self.removeMark(nearest_cutpoint)
1327                 elif not onlyremove and not onlyreturn:
1328                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1329                 
1330                 if onlyreturn:
1331                         return None
1332
1333         def addMark(self, point):
1334                 bisect.insort(self.cut_list, point)
1335                 self.uploadCuesheet()
1336
1337         def removeMark(self, point):
1338                 self.cut_list.remove(point)
1339                 self.uploadCuesheet()
1340
1341         def __getCuesheet(self):
1342                 service = self.session.nav.getCurrentService()
1343                 if service is None:
1344                         return None
1345                 return service.cueSheet()
1346
1347         def uploadCuesheet(self):
1348                 cue = self.__getCuesheet()
1349
1350                 if cue is None:
1351                         print "upload failed, no cuesheet interface"
1352                         return
1353                 cue.setCutList(self.cut_list)
1354
1355         def downloadCuesheet(self):
1356                 cue = self.__getCuesheet()
1357
1358                 if cue is None:
1359                         print "upload failed, no cuesheet interface"
1360                         return
1361                 self.cut_list = cue.getCutList()
1362
1363 class InfoBarSummary(Screen):
1364         skin = """
1365         <screen position="0,0" size="132,64">
1366                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1367                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1368         </screen>"""
1369
1370         def __init__(self, session, parent):
1371                 Screen.__init__(self, session)
1372                 self["CurrentService"] = ServiceName(self.session.nav)
1373                 self["Clock"] = Clock()
1374
1375 class InfoBarSummarySupport:
1376         def __init__(self):
1377                 pass
1378         
1379         def createSummary(self):
1380                 return InfoBarSummary
1381
1382 class InfoBarTeletextPlugin:
1383         def __init__(self):
1384                 self.teletext_plugin = None
1385                 
1386                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1387                         self.teletext_plugin = p
1388                 
1389                 if self.teletext_plugin is not None:
1390                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1391                                 {
1392                                         "startTeletext": (self.startTeletext, "View teletext...")
1393                                 })
1394                 else:
1395                         print "no teletext plugin found!"
1396
1397         def startTeletext(self):
1398                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())