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