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