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