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