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