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