make getCurrentPosition useable from other classes
[vuplus_dvbapp] / lib / python / Screens / InfoBarGenerics.py
1 from Screen import Screen
2 from Components.ActionMap import ActionMap, HelpableActionMap
3 from Components.ActionMap import NumberActionMap
4 from Components.Label import *
5 from Components.ProgressBar import *
6 from Components.config import configfile, configsequencearg
7 from Components.config import config, configElement, ConfigSubsection, configSequence
8 from ChannelSelection import ChannelSelection, BouquetSelector
9
10 from Components.Pixmap import Pixmap, PixmapConditional
11 from Components.BlinkingPixmap import BlinkingPixmapConditional
12 from Components.ServiceName import ServiceName
13 from Components.EventInfo import EventInfo, EventInfoProgress
14
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                 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions", 
544                         {
545                                 "pauseService": (self.pauseService, "pause"),
546                                 "unPauseService": (self.unPauseService, "continue"),
547                                 
548                                 "seekFwd": (self.seekFwd, "skip forward"),
549                                 "seekFwdDown": self.seekFwdDown,
550                                 "seekFwdUp": self.seekFwdUp,
551                                 "seekBack": (self.seekBack, "skip backward"),
552                                 "seekBackDown": self.seekBackDown,
553                                 "seekBackUp": self.seekBackUp,
554                         }, prio=-1)
555                         # give them a little more priority to win over color buttons
556
557                 self.seekstate = self.SEEK_STATE_PLAY
558                 self.onClose.append(self.delTimer)
559                 
560                 self.fwdtimer = False
561                 self.fwdKeyTimer = eTimer()
562                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
563
564                 self.rwdtimer = False
565                 self.rwdKeyTimer = eTimer()
566                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
567                 
568                 self.onPlayStateChanged = [ ]
569                 
570                 self.lockedBecauseOfSkipping = False
571         
572         def up(self):
573                 pass
574         
575         def down(self):
576                 pass
577         
578         def delTimer(self):
579                 del self.fwdKeyTimer
580                 del self.rwdKeyTimer
581         
582         def getSeek(self):
583                 service = self.session.nav.getCurrentService()
584                 if service is None:
585                         return False
586
587                 seek = service.seek()
588
589                 if seek is None or not seek.isCurrentlySeekable():
590                         return None
591                 
592                 return seek
593         
594         def isSeekable(self):
595                 if self.getSeek() is None:
596                         return False
597                 return True
598
599         def __seekableStatusChanged(self):
600                 print "seekable status changed!"
601                 if not self.isSeekable():
602                         self["SeekActions"].setEnabled(False)
603                         print "not seekable, return to play"
604                         self.setSeekState(self.SEEK_STATE_PLAY)
605                 else:
606                         self["SeekActions"].setEnabled(True)
607                         print "seekable"
608
609         def __serviceStarted(self):
610                 self.seekstate = self.SEEK_STATE_PLAY
611
612         def setSeekState(self, state):
613                 service = self.session.nav.getCurrentService()
614                 
615                 if service is None:
616                         return False
617                 
618                 if not self.isSeekable():
619                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
620                                 state = self.SEEK_STATE_PLAY
621                 
622                 pauseable = service.pause()
623
624                 if pauseable is None:
625                         print "not pauseable."
626                         state = self.SEEK_STATE_PLAY
627                 
628                 oldstate = self.seekstate
629                 self.seekstate = state
630                 
631                 for i in range(3):
632                         if oldstate[i] != self.seekstate[i]:
633                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
634
635                 for c in self.onPlayStateChanged:
636                         c(self.seekstate)
637                 
638                 self.checkSkipShowHideLock()
639
640                 return True
641                 
642         def pauseService(self):
643                 if self.seekstate == self.SEEK_STATE_PAUSE:
644                         print "pause, but in fact unpause"
645                         self.unPauseService()
646                 else:
647                         if self.seekstate == self.SEEK_STATE_PLAY:
648                                 print "yes, playing."
649                         else:
650                                 print "no", self.seekstate
651                         print "pause"
652                         self.setSeekState(self.SEEK_STATE_PAUSE);
653                 
654         def unPauseService(self):
655                 print "unpause"
656                 self.setSeekState(self.SEEK_STATE_PLAY);
657         
658         def doSeek(self, seektime):
659                 print "doseek", seektime
660                 service = self.session.nav.getCurrentService()
661                 if service is None:
662                         return
663                 
664                 seekable = self.getSeek()
665                 if seekable is None:
666                         return
667                 
668                 seekable.seekTo(90 * seektime)
669
670         def seekFwdDown(self):
671                 print "start fwd timer"
672                 self.fwdtimer = True
673                 self.fwdKeyTimer.start(1000)
674
675         def seekBackDown(self):
676                 print "start rewind timer"
677                 self.rwdtimer = True
678                 self.rwdKeyTimer.start(1000)
679
680         def seekFwdUp(self):
681                 print "seekFwdUp"
682                 if self.fwdtimer:
683                         self.fwdKeyTimer.stop()
684                         self.fwdtimer = False
685                         self.seekFwd()
686
687         def seekFwd(self):
688                 lookup = {
689                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
690                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
691                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
692                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
693                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
694                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
695                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
696                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
697                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
698                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
699                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
700                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
701                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
702                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
703                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
704                         }
705                 self.setSeekState(lookup[self.seekstate])
706         
707         def seekBackUp(self):
708                 print "seekBackUp"
709                 if self.rwdtimer:
710                         self.rwdKeyTimer.stop()
711                         self.rwdtimer = False
712                         self.seekBack()
713                 
714         def seekBack(self):
715                 lookup = {
716                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
717                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
718                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
719                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
720                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
721                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
722                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
723                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
724                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
725                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
726                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
727                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
728                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
729                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
730                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
731                         }
732                 self.setSeekState(lookup[self.seekstate])
733
734         def fwdTimerFire(self):
735                 print "Display seek fwd"
736                 self.fwdKeyTimer.stop()
737                 self.fwdtimer = False
738                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
739                 
740         def fwdSeekTo(self, minutes):
741                 print "Seek", minutes, "minutes forward"
742                 if minutes != 0:
743                         seekable = self.getSeek()
744                         if seekable is not None:
745                                 seekable.seekRelative(1, minutes * 60 * 90000)
746         
747         def rwdTimerFire(self):
748                 print "rwdTimerFire"
749                 self.rwdKeyTimer.stop()
750                 self.rwdtimer = False
751                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
752         
753         def rwdSeekTo(self, minutes):
754                 print "rwdSeekTo"
755                 self.fwdSeekTo(0 - minutes)
756         
757         def checkSkipShowHideLock(self):
758                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
759                 
760                 if self.lockedBecauseOfSkipping and not wantlock:
761                         self.unlockShow()
762                         self.lockedBecauseOfSkipping = False
763                 
764                 if wantlock and not self.lockedBecauseOfSkipping:
765                         self.lockShow()
766                         self.lockedBecauseOfSkipping = True
767
768         def __evEOF(self):
769                 if self.seekstate != self.SEEK_STATE_PLAY:
770                         self.setSeekState(self.SEEK_STATE_PAUSE)
771                         # HACK
772                         #self.getSeek().seekRelative(1, -90000)
773                         self.setSeekState(self.SEEK_STATE_PLAY)
774                 else:
775                         self.setSeekState(self.SEEK_STATE_PAUSE)
776         
777         def __evSOF(self):
778                 self.setSeekState(self.SEEK_STATE_PLAY)
779                 self.doSeek(0)
780
781         def seekRelative(self, diff):
782                 seekable = self.getSeek()
783                 if seekable is not None:
784                         seekable.seekRelative(0, diff)
785
786 from Screens.PVRState import PVRState
787
788 class InfoBarPVRState:
789         def __init__(self):
790                 self.onPlayStateChanged.append(self.__playStateChanged)
791                 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
792                 self.onShow.append(self.__mayShow)
793                 self.onHide.append(self.pvrStateDialog.hide)
794         
795         def __mayShow(self):
796                 if self.seekstate != self.SEEK_STATE_PLAY:
797                         self.pvrStateDialog.show()
798
799         def __playStateChanged(self, state):
800                 playstateString = state[3]
801                 self.pvrStateDialog["state"].setText(playstateString)
802                 self.__mayShow()
803
804 class InfoBarShowMovies:
805
806         # i don't really like this class. 
807         # it calls a not further specified "movie list" on up/down/movieList,
808         # so this is not more than an action map
809         def __init__(self):
810                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
811                         {
812                                 "movieList": (self.showMovies, "movie list"),
813                                 "up": (self.showMovies, "movie list"),
814                                 "down": (self.showMovies, "movie list")
815                         })
816
817 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
818
819 # Hrmf.
820 #
821 # Timeshift works the following way:
822 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
823 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
824 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
825 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
826 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
827 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
828 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
829 #
830
831 # in other words:
832 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
833 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
834 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
835 # - the user can now PVR around
836 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
837 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
838 # after!
839 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
840 # - if the user rewinds, or press pause, timeshift will be activated again
841
842 # note that a timeshift can be enabled ("recording") and
843 # activated (currently time-shifting).
844
845 class InfoBarTimeshift:
846         def __init__(self):
847                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
848                         {
849                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
850                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
851                         }, prio=1)
852                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
853                         {
854                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
855                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
856                         }, prio=-1) # priority over record
857
858                 self.timeshift_enabled = 0
859                 self.timeshift_state = 0
860                 self.ts_pause_timer = eTimer()
861                 self.ts_pause_timer.timeout.get().append(self.pauseService)
862
863                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
864                         {
865                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
866                         })
867         
868         def getTimeshift(self):
869                 service = self.session.nav.getCurrentService()
870                 return service.timeshift()
871
872         def startTimeshift(self):
873                 print "enable timeshift"
874                 ts = self.getTimeshift()
875                 if ts is None:
876                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
877                         print "no ts interface"
878                         return
879                 
880                 if self.timeshift_enabled:
881                         print "hu, timeshift already enabled?"
882                 else:
883                         if not ts.startTimeshift():
884                                 self.timeshift_enabled = 1
885                                 
886                                 # PAUSE.
887                                 self.setSeekState(self.SEEK_STATE_PAUSE)
888                                 
889                                 # enable the "TimeshiftEnableActions", which will override
890                                 # the startTimeshift actions
891                                 self.__seekableStatusChanged()
892                         else:
893                                 print "timeshift failed"
894
895         def stopTimeshift(self):
896                 if not self.timeshift_enabled:
897                         return
898                 print "disable timeshift"
899                 ts = self.getTimeshift()
900                 if ts is None:
901                         return
902                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
903
904         def stopTimeshiftConfirmed(self, confirmed):
905                 if not confirmed:
906                         return
907
908                 ts = self.getTimeshift()
909                 if ts is None:
910                         return
911
912                 ts.stopTimeshift()
913                 self.timeshift_enabled = 0
914
915                 # disable actions
916                 self.__seekableStatusChanged()
917         
918         # activates timeshift, and seeks to (almost) the end
919         def activateTimeshiftEnd(self):
920                 ts = self.getTimeshift()
921                 
922                 if ts is None:
923                         return
924                 
925                 if ts.isTimeshiftActive():
926                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
927                         self.pauseService()
928                 else:
929                         self.setSeekState(self.SEEK_STATE_PLAY)
930                         ts.activateTimeshift()
931                         self.seekRelative(0)
932         
933         # same as activateTimeshiftEnd, but pauses afterwards.
934         def activateTimeshiftEndAndPause(self):
935                 state = self.seekstate
936                 self.activateTimeshiftEnd()
937                 
938                 # well, this is "andPause", but it could be pressed from pause,
939                 # when pausing on the (fake-)"live" picture, so an un-pause
940                 # is perfectly ok.
941                 
942                 print "now, pauseService"
943                 if state == self.SEEK_STATE_PLAY:
944                         print "is PLAYING, start pause timer"
945                         self.ts_pause_timer.start(200, 1)
946                 else:
947                         print "unpause"
948                         self.unPauseService()
949         
950         def __seekableStatusChanged(self):
951                 enabled = False
952                 
953                 print "self.isSeekable", self.isSeekable()
954                 print "self.timeshift_enabled", self.timeshift_enabled
955                 
956                 # when this service is not seekable, but timeshift
957                 # is enabled, this means we can activate
958                 # the timeshift
959                 if not self.isSeekable() and self.timeshift_enabled:
960                         enabled = True
961
962                 print "timeshift activate:", enabled
963                 self["TimeshiftActivateActions"].setEnabled(enabled)
964
965 from RecordTimer import parseEvent
966
967 class InfoBarInstantRecord:
968         """Instant Record - handles the instantRecord action in order to 
969         start/stop instant records"""
970         def __init__(self):
971                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
972                         {
973                                 "instantRecord": (self.instantRecord, "Instant Record..."),
974                         })
975                 self.recording = None
976                 self["BlinkingPoint"] = BlinkingPixmapConditional()
977                 self["BlinkingPoint"].hide()
978                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
979
980         def stopCurrentRecording(self): 
981                 self.session.nav.RecordTimer.removeEntry(self.recording)
982                 self.recording = None
983
984         def startInstantRecording(self):
985                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
986                 
987                 # try to get event info
988                 event = None
989                 try:
990                         service = self.session.nav.getCurrentService()
991                         epg = eEPGCache.getInstance()
992                         event = epg.lookupEventTime(serviceref, -1, 0)
993                         if event is None:
994                                 info = service.info()
995                                 ev = info.getEvent(0)
996                                 event = ev
997                 except:
998                         pass
999
1000                 if event is not None:
1001                         data = parseEvent(event)
1002                         begin = time.time()
1003                         end = begin + 3600 * 10
1004                         data = (begin, end, data[2], data[3], data[4])
1005                 else:
1006                         data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
1007                 
1008                 # fix me, description. 
1009                 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
1010                 self.recording.dontSave = True
1011                 
1012                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1013                 
1014         def isInstantRecordRunning(self):
1015                 if self.recording != None:
1016                         if self.recording.isRunning():
1017                                 return True
1018                 return False
1019
1020         def recordQuestionCallback(self, answer):
1021                 if answer == False:
1022                         return
1023                 
1024                 if self.isInstantRecordRunning():
1025                         self.stopCurrentRecording()
1026                 else:
1027                         self.startInstantRecording()
1028
1029         def instantRecord(self):
1030                 try:
1031                         stat = os.stat(resolveFilename(SCOPE_HDD))
1032                 except:
1033                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1034                         return
1035         
1036                 if self.isInstantRecordRunning():
1037                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
1038                 else:
1039                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
1040
1041 from Screens.AudioSelection import AudioSelection
1042
1043 class InfoBarAudioSelection:
1044         def __init__(self):
1045                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1046                         {
1047                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1048                         })
1049
1050         def audioSelection(self):
1051                 service = self.session.nav.getCurrentService()
1052                 audio = service.audioTracks()
1053                 n = audio.getNumberOfTracks()
1054                 if n > 0:
1055                         self.session.open(AudioSelection, audio)
1056
1057 from Screens.SubserviceSelection import SubserviceSelection
1058
1059 class InfoBarSubserviceSelection:
1060         def __init__(self):
1061                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1062                         {
1063                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1064                         })
1065
1066         def subserviceSelection(self):
1067                 service = self.session.nav.getCurrentService()
1068                 subservices = service.subServices()
1069                 n = subservices.getNumberOfSubservices()
1070                 if n > 0:
1071                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1072
1073         def subserviceSelected(self, service):
1074                 if not service is None:
1075                         self.session.nav.playService(service)
1076
1077 class InfoBarAdditionalInfo:
1078         def __init__(self):
1079                 self["DolbyActive"] = Pixmap()
1080                 self["CryptActive"] = Pixmap()
1081                 self["FormatActive"] = Pixmap()
1082                 
1083                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1084                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1085                 self.onLayoutFinish.append(self["ButtonRed"].update)
1086                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1087                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1088                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1089
1090                 self["ButtonGreen"] = Pixmap()
1091                 self["ButtonGreenText"] = Label(_("Subservices"))
1092
1093                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1094                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1095                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1096                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1097                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1098                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1099
1100                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1101                 self["ButtonBlue"].setConnect(lambda: False)
1102                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1103                 self["ButtonBlueText"].setConnect(lambda: False)
1104                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1105                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1106
1107                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1108
1109         def hideSubServiceIndication(self):
1110                 self["ButtonGreen"].hide()
1111                 self["ButtonGreenText"].hide()
1112
1113         def showSubServiceIndication(self):
1114                 self["ButtonGreen"].show()
1115                 self["ButtonGreenText"].show()
1116
1117         def checkFormat(self, service):
1118                 info = service.info()
1119                 if info is not None:
1120                         aspect = info.getInfo(iServiceInformation.sAspect)
1121                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1122                                 self["FormatActive"].show()
1123                         else:
1124                                 self["FormatActive"].hide()
1125
1126         def checkSubservices(self, service):
1127                 if service.subServices().getNumberOfSubservices() > 0:
1128                         self.showSubServiceIndication()
1129                 else:
1130                         self.hideSubServiceIndication()
1131
1132         def checkDolby(self, service):
1133                 # FIXME
1134                 dolby = False
1135                 audio = service.audioTracks()
1136                 if audio is not None:
1137                         n = audio.getNumberOfTracks()
1138                         for x in range(n):
1139                                 i = audio.getTrackInfo(x)
1140                                 description = i.getDescription();
1141                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1142                                         dolby = True
1143                                         break
1144                 if dolby:
1145                         self["DolbyActive"].show()
1146                 else:
1147                         self["DolbyActive"].hide()
1148
1149         def checkCrypted(self, service):
1150                 info = service.info()
1151                 if info is not None:
1152                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1153                                 self["CryptActive"].show()
1154                         else:
1155                                 self["CryptActive"].hide()
1156
1157         def gotServiceEvent(self, ev):
1158                 service = self.session.nav.getCurrentService()
1159                 if ev == iPlayableService.evUpdatedEventInfo:
1160                         self.checkSubservices(service)
1161                         self.checkFormat(service)
1162                 elif ev == iPlayableService.evUpdatedInfo:
1163                         self.checkCrypted(service)
1164                         self.checkDolby(service)
1165                 elif ev == iPlayableService.evEnd:
1166                         self.hideSubServiceIndication()
1167                         self["CryptActive"].hide()
1168                         self["DolbyActive"].hide()
1169                         self["FormatActive"].hide()
1170
1171 class InfoBarNotifications:
1172         def __init__(self):
1173                 self.onExecBegin.append(self.checkNotifications)
1174                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1175         
1176         def checkNotificationsIfExecing(self):
1177                 if self.execing:
1178                         self.checkNotifications()
1179
1180         def checkNotifications(self):
1181                 if len(Notifications.notifications):
1182                         n = Notifications.notifications[0]
1183                         Notifications.notifications = Notifications.notifications[1:]
1184                         print "open",n
1185                         cb = n[0]
1186                         if cb is not None:
1187                                 self.session.openWithCallback(cb, *n[1:])
1188                         else:
1189                                 self.session.open(*n[1:])
1190
1191 class InfoBarServiceNotifications:
1192         def __init__(self):
1193                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1194                         {
1195                                 iPlayableService.evEnd: self.serviceHasEnded
1196                         })
1197
1198         def serviceHasEnded(self):
1199                 print "service end!"
1200
1201                 try:
1202                         self.setSeekState(self.SEEK_STATE_PLAY)
1203                 except:
1204                         pass
1205
1206 class InfoBarCueSheetSupport:
1207         CUT_TYPE_IN = 0
1208         CUT_TYPE_OUT = 1
1209         CUT_TYPE_MARK = 2
1210         
1211         def __init__(self):
1212                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1213                         {
1214                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1215                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1216                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1217                         }, prio=1) 
1218                 
1219                 self.cut_list = [ ]
1220                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1221                         {
1222                                 iPlayableService.evStart: self.__serviceStarted,
1223                         })
1224
1225         def __serviceStarted(self):
1226                 print "new service started! trying to download cuts!"
1227                 self.downloadCuesheet()
1228
1229         def __getSeekable(self):
1230                 service = self.session.nav.getCurrentService()
1231                 if service is None:
1232                         return None
1233                 return service.seek()
1234
1235         def cueGetCurrentPosition(self):
1236                 seek = self.__getSeekable()
1237                 if seek is None:
1238                         return None
1239                 r = seek.getPlayPosition()
1240                 if r[0]:
1241                         return None
1242                 return long(r[1])
1243
1244         def jumpPreviousNextMark(self, cmp, alternative=None):
1245                 current_pos = self.cueGetCurrentPosition()
1246                 if current_pos is None:
1247                         return
1248                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1249                 if mark is not None:
1250                         pts = mark[0]
1251                 elif alternative is not None:
1252                         pts = alternative
1253                 else:
1254                         return
1255
1256                 seekable = self.__getSeekable()
1257                 if seekable is not None:
1258                         seekable.seekTo(pts)
1259
1260         def jumpPreviousMark(self):
1261                 # we add 2 seconds, so if the play position is <2s after
1262                 # the mark, the mark before will be used
1263                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1264
1265         def jumpNextMark(self):
1266                 self.jumpPreviousNextMark(lambda x: x)
1267
1268         def getNearestCutPoint(self, pts, cmp=abs):
1269                 # can be optimized
1270                 nearest = None
1271                 for cp in self.cut_list:
1272                         diff = cmp(cp[0] - pts)
1273                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1274                                 nearest = cp
1275                 return nearest
1276
1277         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000):
1278                 current_pos = self.cueGetCurrentPosition()
1279                 if current_pos is None:
1280                         print "not seekable"
1281                         return
1282                 
1283                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1284                 
1285                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1286                         if not onlyadd:
1287                                 self.removeMark(nearest_cutpoint)
1288                 elif not onlyremove:
1289                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1290
1291         def addMark(self, point):
1292                 bisect.insort(self.cut_list, point)
1293                 self.uploadCuesheet()
1294
1295         def removeMark(self, point):
1296                 self.cut_list.remove(point)
1297                 self.uploadCuesheet()
1298
1299         def __getCuesheet(self):
1300                 service = self.session.nav.getCurrentService()
1301                 if service is None:
1302                         return None
1303                 return service.cueSheet()
1304
1305         def uploadCuesheet(self):
1306                 cue = self.__getCuesheet()
1307
1308                 if cue is None:
1309                         print "upload failed, no cuesheet interface"
1310                         return
1311                 cue.setCutList(self.cut_list)
1312
1313         def downloadCuesheet(self):
1314                 cue = self.__getCuesheet()
1315
1316                 if cue is None:
1317                         print "upload failed, no cuesheet interface"
1318                         return
1319                 self.cut_list = cue.getCutList()