decrease skip ratio a bit
[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
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, _("next channel")),
280                                 "zapDown": (self.zapDown, _("previous 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 InfoBarEPG:
330         """ EPG - Opens an EPG list when the showEPGList action fires """
331         def __init__(self):
332                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
333                         {
334                                 "showEventInfo": (self.openEventView, _("show EPG...")),
335                         })
336
337         def zapToService(self, service):
338                 if not service is None:
339                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
340                                 self.servicelist.clearPath()
341                                 if self.servicelist.bouquet_root != self.epg_bouquet:
342                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
343                                 self.servicelist.enterPath(self.epg_bouquet)
344                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
345                         self.servicelist.zap()
346
347         def openBouquetEPG(self, bouquet, withCallback=True):
348                 ptr=eEPGCache.getInstance()
349                 services = [ ]
350                 servicelist = eServiceCenter.getInstance().list(bouquet)
351                 if not servicelist is None:
352                         while True:
353                                 service = servicelist.getNext()
354                                 if not service.valid(): #check if end of list
355                                         break
356                                 if service.flags: #ignore non playable services
357                                         continue
358                                 services.append(ServiceReference(service))
359                 if len(services):
360                         self.epg_bouquet = bouquet
361                         if withCallback:
362                                 self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
363                         else:
364                                 self.session.open(EPGSelection, services, self.zapToService)
365
366         def closed(self, ret):
367                 if ret:
368                         self.close(ret)
369
370         def openMultiServiceEPG(self, withCallback=True):
371                 bouquets = self.servicelist.getBouquetList()
372                 if bouquets is None:
373                         cnt = 0
374                 else:
375                         cnt = len(bouquets)
376                 if cnt > 1: # show bouquet list
377                         if withCallback:
378                                 self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
379                         else:
380                                 self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
381                 elif cnt == 1: 
382                         self.openBouquetEPG(bouquets[0][1], withCallback)
383
384         def openSingleServiceEPG(self):
385                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
386                 ptr=eEPGCache.getInstance()
387                 self.session.openWithCallback(self.closed, EPGSelection, ref)
388
389         def openEventView(self):
390                 self.epglist = [ ]
391                 service = self.session.nav.getCurrentService()
392                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
393                 info = service.info()
394                 ptr=info.getEvent(0)
395                 if ptr:
396                         self.epglist.append(ptr)
397                 ptr=info.getEvent(1)
398                 if ptr:
399                         self.epglist.append(ptr)
400                 if len(self.epglist) == 0:
401                         epg = eEPGCache.getInstance()
402                         ptr = epg.lookupEventTime(ref, -1)
403                         if ptr:
404                                 self.epglist.append(ptr)
405                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
406                                 if ptr:
407                                         self.epglist.append(ptr)
408                 if len(self.epglist) > 0:
409                         self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
410                 else:
411                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
412                         self.openMultiServiceEPG(False)
413
414         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
415                 if len(self.epglist) > 1:
416                         tmp = self.epglist[0]
417                         self.epglist[0]=self.epglist[1]
418                         self.epglist[1]=tmp
419                         setEvent(self.epglist[0])
420
421 from math import log
422
423 class InfoBarTuner:
424         """provides a snr/agc/ber display"""
425         def __init__(self):
426                 self["snr"] = Label()
427                 self["agc"] = Label()
428                 self["ber"] = Label()
429                 self["snr_percent"] = Label()
430                 self["agc_percent"] = Label()
431                 self["ber_count"] = Label()
432                 self["snr_progress"] = ProgressBar()
433                 self["agc_progress"] = ProgressBar()
434                 self["ber_progress"] = ProgressBar()
435                 self.timer = eTimer()
436                 self.timer.timeout.get().append(self.updateTunerInfo)
437                 self.timer.start(1000)
438
439         def calc(self,val):
440                 if not val:
441                         return 0
442                 if val < 2500:
443                         return (long)(log(val)/log(2))
444                 return val*100/65535
445
446         def updateTunerInfo(self):
447                 if self.instance.isVisible():
448                         service = self.session.nav.getCurrentService()
449                         snr=0
450                         agc=0
451                         ber=0
452                         if service is not None:
453                                 feinfo = service.frontendStatusInfo()
454                                 if feinfo is not None:
455                                         ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
456                                         snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
457                                         agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
458                         self["snr_percent"].setText("%d%%"%(snr))
459                         self["agc_percent"].setText("%d%%"%(agc))
460                         self["ber_count"].setText("%d"%(ber))
461                         self["snr_progress"].setValue(snr)
462                         self["agc_progress"].setValue(agc)
463                         self["ber_progress"].setValue(self.calc(ber))
464
465 class InfoBarEvent:
466         """provides a current/next event info display"""
467         def __init__(self):
468                 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
469                 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
470                                 
471                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
472                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
473
474                 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
475                 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
476
477                 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
478
479 class InfoBarServiceName:
480         def __init__(self):
481                 self["ServiceName"] = ServiceName(self.session.nav)
482
483 class InfoBarSeek:
484         """handles actions like seeking, pause"""
485         
486         # ispause, isff, issm
487         SEEK_STATE_PLAY = (0, 0, 0, ">")
488         SEEK_STATE_PAUSE = (1, 0, 0, "||")
489         SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
490         SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
491         SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
492         SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
493         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
494         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
495         
496         SEEK_STATE_BACK_4X = (0, -4, 0, "<< 4x")
497         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
498         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
499         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
500         
501         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
502         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
503         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
504         
505         def __init__(self):
506                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
507                         {
508                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
509                                 iPlayableService.evStart: self.__serviceStarted,
510                                 
511                                 iPlayableService.evEOF: self.__evEOF,
512                                 iPlayableService.evSOF: self.__evSOF,
513                         })
514                 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions", 
515                         {
516                                 "pauseService": (self.pauseService, "pause"),
517                                 "unPauseService": (self.unPauseService, "continue"),
518                                 
519                                 "seekFwd": (self.seekFwd, "skip forward"),
520                                 "seekFwdUp": (self.seekFwdUp, "skip forward"),
521                                 "seekBack": (self.seekBack, "skip backward"),
522                                 "seekBackUp": (self.seekBackUp, "skip backward"),
523                         }, prio=-1)
524                         # give them a little more priority to win over color buttons
525
526                 self.seekstate = self.SEEK_STATE_PLAY
527                 self.onClose.append(self.delTimer)
528                 
529                 self.fwdtimer = False
530                 self.fwdKeyTimer = eTimer()
531                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
532
533                 self.rwdtimer = False
534                 self.rwdKeyTimer = eTimer()
535                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
536                 
537                 self.onPlayStateChanged = [ ]
538                 
539                 self.lockedBecauseOfSkipping = False
540         
541         def up(self):
542                 pass
543         
544         def down(self):
545                 pass
546         
547         def delTimer(self):
548                 del self.fwdKeyTimer
549                 del self.rwdKeyTimer
550         
551         def getSeek(self):
552                 service = self.session.nav.getCurrentService()
553                 if service is None:
554                         return False
555
556                 seek = service.seek()
557
558                 if seek is None or not seek.isCurrentlySeekable():
559                         return None
560                 
561                 return seek
562         
563         def isSeekable(self):
564                 if self.getSeek() is None:
565                         return False
566                 return True
567
568         def __seekableStatusChanged(self):
569                 print "seekable status changed!"
570                 if not self.isSeekable():
571                         self["SeekActions"].setEnabled(False)
572                         print "not seekable, return to play"
573                         self.setSeekState(self.SEEK_STATE_PLAY)
574                 else:
575                         self["SeekActions"].setEnabled(True)
576                         print "seekable"
577
578         def __serviceStarted(self):
579                 self.seekstate = self.SEEK_STATE_PLAY
580
581         def setSeekState(self, state):
582                 service = self.session.nav.getCurrentService()
583                 
584                 if service is None:
585                         return False
586                 
587                 if not self.isSeekable():
588                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
589                                 state = self.SEEK_STATE_PLAY
590                 
591                 pauseable = service.pause()
592
593                 if pauseable is None:
594                         print "not pauseable."
595                         state = self.SEEK_STATE_PLAY
596                 
597                 oldstate = self.seekstate
598                 self.seekstate = state
599                 
600                 for i in range(3):
601                         if oldstate[i] != self.seekstate[i]:
602                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
603
604                 for c in self.onPlayStateChanged:
605                         c(self.seekstate)
606                 
607                 self.checkSkipShowHideLock()
608
609                 return True
610                 
611         def pauseService(self):
612                 if self.seekstate == self.SEEK_STATE_PAUSE:
613                         print "pause, but in fact unpause"
614                         self.unPauseService()
615                 else:
616                         if self.seekstate == self.SEEK_STATE_PLAY:
617                                 print "yes, playing."
618                         else:
619                                 print "no", self.seekstate
620                         print "pause"
621                         self.setSeekState(self.SEEK_STATE_PAUSE);
622                 
623         def unPauseService(self):
624                 print "unpause"
625                 self.setSeekState(self.SEEK_STATE_PLAY);
626         
627         def doSeek(self, seektime):
628                 print "doseek", seektime
629                 service = self.session.nav.getCurrentService()
630                 if service is None:
631                         return
632                 
633                 seekable = self.getSeek()
634                 if seekable is None:
635                         return
636                 
637                 seekable.seekTo(90 * seektime)
638
639         def seekFwd(self):
640                 print "start fwd timer"
641                 self.fwdtimer = True
642                 self.fwdKeyTimer.start(500)
643
644         def seekBack(self):
645                 print "start rewind timer"
646                 self.rwdtimer = True
647                 self.rwdKeyTimer.start(500)
648
649         def seekFwdUp(self):
650                 print "seekFwdUp"
651                 if self.fwdtimer:
652                         self.fwdKeyTimer.stop()
653                         self.fwdtimer = False
654                         lookup = {
655                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
656                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
657                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
658                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
659                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
660                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
661                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
662                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
663                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_PLAY,
664                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_4X,
665                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
666                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
667                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
668                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
669                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
670                                 }
671                         self.setSeekState(lookup[self.seekstate]);
672         
673         def seekBackUp(self):
674                 print "seekBackUp"
675                 if self.rwdtimer:
676                         self.rwdKeyTimer.stop()
677                         self.rwdtimer = False
678                 
679                         lookup = {
680                                         self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_4X,
681                                         self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
682                                         self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
683                                         self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
684                                         self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
685                                         self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
686                                         self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
687                                         self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
688                                         self.SEEK_STATE_BACK_4X: self.SEEK_STATE_BACK_32X,
689                                         self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
690                                         self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
691                                         self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
692                                         self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
693                                         self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
694                                         self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
695                                 }
696                         self.setSeekState(lookup[self.seekstate]);
697                 
698         def fwdTimerFire(self):
699                 print "Display seek fwd"
700                 self.fwdKeyTimer.stop()
701                 self.fwdtimer = False
702                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
703                 
704         def fwdSeekTo(self, minutes):
705                 print "Seek", minutes, "minutes forward"
706                 if minutes != 0:
707                         seekable = self.getSeek()
708                         if seekable is not None:
709                                 seekable.seekRelative(1, minutes * 60 * 90000)
710         
711         def rwdTimerFire(self):
712                 print "rwdTimerFire"
713                 self.rwdKeyTimer.stop()
714                 self.rwdtimer = False
715                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
716         
717         def rwdSeekTo(self, minutes):
718                 print "rwdSeekTo"
719                 self.fwdSeekTo(0 - minutes)
720         
721         def checkSkipShowHideLock(self):
722                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
723                 
724                 if self.lockedBecauseOfSkipping and not wantlock:
725                         self.unlockShow()
726                         self.lockedBecauseOfSkipping = False
727                 
728                 if wantlock and not self.lockedBecauseOfSkipping:
729                         self.lockShow()
730                         self.lockedBecauseOfSkipping = True
731
732         def __evEOF(self):
733                 self.setSeekState(self.SEEK_STATE_PAUSE)
734         
735         def __evSOF(self):
736                 self.setSeekState(self.SEEK_STATE_PLAY)
737                 self.doSeek(0)
738
739         def seekRelative(self, diff):
740                 seekable = self.getSeek()
741                 if seekable is not None:
742                         seekable.seekRelative(0, diff)
743
744 from Screens.PVRState import PVRState
745
746 class InfoBarPVRState:
747         def __init__(self):
748                 self.onPlayStateChanged.append(self.__playStateChanged)
749                 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
750                 self.onShow.append(self.__mayShow)
751                 self.onHide.append(self.pvrStateDialog.hide)
752         
753         def __mayShow(self):
754                 if self.seekstate != self.SEEK_STATE_PLAY:
755                         self.pvrStateDialog.show()
756
757         def __playStateChanged(self, state):
758                 playstateString = state[3]
759                 self.pvrStateDialog["state"].setText(playstateString)
760                 self.__mayShow()
761
762 class InfoBarShowMovies:
763
764         # i don't really like this class. 
765         # it calls a not further specified "movie list" on up/down/movieList,
766         # so this is not more than an action map
767         def __init__(self):
768                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
769                         {
770                                 "movieList": (self.showMovies, "movie list"),
771                                 "up": (self.showMovies, "movie list"),
772                                 "down": (self.showMovies, "movie list")
773                         })
774
775 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
776
777 # Hrmf.
778 #
779 # Timeshift works the following way:
780 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
781 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
782 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
783 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
784 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
785 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
786 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
787 #
788
789 # in other words:
790 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
791 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
792 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
793 # - the user can now PVR around
794 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
795 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
796 # after!
797 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
798 # - if the user rewinds, or press pause, timeshift will be activated again
799
800 # note that a timeshift can be enabled ("recording") and
801 # activated (currently time-shifting).
802
803 class InfoBarTimeshift:
804         def __init__(self):
805                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
806                         {
807                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
808                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
809                         }, prio=1)
810                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
811                         {
812                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
813                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
814                         }, prio=-1) # priority over record
815
816                 self.timeshift_enabled = 0
817                 self.timeshift_state = 0
818                 self.ts_pause_timer = eTimer()
819                 self.ts_pause_timer.timeout.get().append(self.pauseService)
820
821                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
822                         {
823                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
824                         })
825         
826         def getTimeshift(self):
827                 service = self.session.nav.getCurrentService()
828                 return service.timeshift()
829
830         def startTimeshift(self):
831                 print "enable timeshift"
832                 ts = self.getTimeshift()
833                 if ts is None:
834                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
835                         print "no ts interface"
836                         return
837                 
838                 if self.timeshift_enabled:
839                         print "hu, timeshift already enabled?"
840                 else:
841                         if not ts.startTimeshift():
842                                 self.timeshift_enabled = 1
843                                 
844                                 # PAUSE.
845                                 self.setSeekState(self.SEEK_STATE_PAUSE)
846                                 
847                                 # enable the "TimeshiftEnableActions", which will override
848                                 # the startTimeshift actions
849                                 self.__seekableStatusChanged()
850                         else:
851                                 print "timeshift failed"
852
853         def stopTimeshift(self):
854                 if not self.timeshift_enabled:
855                         return
856                 print "disable timeshift"
857                 ts = self.getTimeshift()
858                 if ts is None:
859                         return
860                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
861
862         def stopTimeshiftConfirmed(self, confirmed):
863                 if not confirmed:
864                         return
865
866                 ts = self.getTimeshift()
867                 if ts is None:
868                         return
869
870                 ts.stopTimeshift()
871                 self.timeshift_enabled = 0
872
873                 # disable actions
874                 self.__seekableStatusChanged()
875         
876         # activates timeshift, and seeks to (almost) the end
877         def activateTimeshiftEnd(self):
878                 ts = self.getTimeshift()
879                 
880                 if ts is None:
881                         return
882                 
883                 if ts.isTimeshiftActive():
884                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
885                         self.pauseService()
886                 else:
887                         self.setSeekState(self.SEEK_STATE_PLAY)
888                         ts.activateTimeshift()
889                         self.seekRelative(0)
890         
891         # same as activateTimeshiftEnd, but pauses afterwards.
892         def activateTimeshiftEndAndPause(self):
893                 state = self.seekstate
894                 self.activateTimeshiftEnd()
895                 
896                 # well, this is "andPause", but it could be pressed from pause,
897                 # when pausing on the (fake-)"live" picture, so an un-pause
898                 # is perfectly ok.
899                 
900                 print "now, pauseService"
901                 if state == self.SEEK_STATE_PLAY:
902                         print "is PLAYING, start pause timer"
903                         self.ts_pause_timer.start(200, 1)
904                 else:
905                         print "unpause"
906                         self.unPauseService()
907         
908         def __seekableStatusChanged(self):
909                 enabled = False
910                 
911                 print "self.isSeekable", self.isSeekable()
912                 print "self.timeshift_enabled", self.timeshift_enabled
913                 
914                 # when this service is not seekable, but timeshift
915                 # is enabled, this means we can activate
916                 # the timeshift
917                 if not self.isSeekable() and self.timeshift_enabled:
918                         enabled = True
919
920                 print "timeshift activate:", enabled
921                 self["TimeshiftActivateActions"].setEnabled(enabled)
922
923 from RecordTimer import parseEvent
924
925 class InfoBarInstantRecord:
926         """Instant Record - handles the instantRecord action in order to 
927         start/stop instant records"""
928         def __init__(self):
929                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
930                         {
931                                 "instantRecord": (self.instantRecord, "Instant Record..."),
932                         })
933                 self.recording = None
934                 self["BlinkingPoint"] = BlinkingPixmapConditional()
935                 self.onLayoutFinish.append(self["BlinkingPoint"].hideWidget)
936                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
937
938         def stopCurrentRecording(self): 
939                 self.session.nav.RecordTimer.removeEntry(self.recording)
940                 self.recording = None
941
942         def startInstantRecording(self):
943                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
944                 
945                 # try to get event info
946                 event = None
947                 try:
948                         service = self.session.nav.getCurrentService()
949                         info = service.info()
950                         ev = info.getEvent(0)
951                         event = ev
952                 except:
953                         pass
954                 
955                 if event is not None:
956                         data = parseEvent(event)
957                         begin = time.time()
958                         end = begin + 3600 * 10
959                         
960                         data = (begin, end, data[2], data[3], data[4])
961                 else:
962                         data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
963                 
964                 # fix me, description. 
965                 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
966                 self.recording.dontSave = True
967                 
968                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
969                 
970         def isInstantRecordRunning(self):
971                 if self.recording != None:
972                         if self.recording.isRunning():
973                                 return True
974                 return False
975
976         def recordQuestionCallback(self, answer):
977                 if answer == False:
978                         return
979                 
980                 if self.isInstantRecordRunning():
981                         self.stopCurrentRecording()
982                 else:
983                         self.startInstantRecording()
984
985         def instantRecord(self):
986                 try:
987                         stat = os.stat(resolveFilename(SCOPE_HDD))
988                 except:
989                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
990                         return
991         
992                 if self.isInstantRecordRunning():
993                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
994                 else:
995                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
996
997 from Screens.AudioSelection import AudioSelection
998
999 class InfoBarAudioSelection:
1000         def __init__(self):
1001                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1002                         {
1003                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1004                         })
1005
1006         def audioSelection(self):
1007                 service = self.session.nav.getCurrentService()
1008                 audio = service.audioTracks()
1009                 n = audio.getNumberOfTracks()
1010                 if n > 0:
1011                         self.session.open(AudioSelection, audio)
1012
1013 from Screens.SubserviceSelection import SubserviceSelection
1014
1015 class InfoBarSubserviceSelection:
1016         def __init__(self):
1017                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1018                         {
1019                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1020                         })
1021
1022         def subserviceSelection(self):
1023                 service = self.session.nav.getCurrentService()
1024                 subservices = service.subServices()
1025                 n = subservices.getNumberOfSubservices()
1026                 if n > 0:
1027                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1028
1029         def subserviceSelected(self, service):
1030                 if not service is None:
1031                         self.session.nav.playService(service)
1032
1033 class InfoBarAdditionalInfo:
1034         def __init__(self):
1035                 self["DolbyActive"] = Pixmap()
1036                 self["CryptActive"] = Pixmap()
1037                 self["FormatActive"] = Pixmap()
1038                 
1039                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1040                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1041                 self.onLayoutFinish.append(self["ButtonRed"].update)
1042                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1043                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1044                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1045
1046                 self["ButtonGreen"] = Pixmap()
1047                 self["ButtonGreenText"] = Label(_("Subservices"))
1048
1049                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1050                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1051                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1052                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1053                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1054                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1055
1056                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1057                 self["ButtonBlue"].setConnect(lambda: False)
1058                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1059                 self["ButtonBlueText"].setConnect(lambda: False)
1060                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1061                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1062
1063                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1064
1065         def hideSubServiceIndication(self):
1066                 self["ButtonGreen"].hideWidget()
1067                 self["ButtonGreenText"].hide()
1068
1069         def showSubServiceIndication(self):
1070                 self["ButtonGreen"].showWidget()
1071                 self["ButtonGreenText"].show()
1072
1073         def checkFormat(self, service):
1074                 info = service.info()
1075                 if info is not None:
1076                         aspect = info.getInfo(iServiceInformation.sAspect)
1077                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1078                                 self["FormatActive"].showWidget()
1079                         else:
1080                                 self["FormatActive"].hideWidget()
1081
1082         def checkSubservices(self, service):
1083                 if service.subServices().getNumberOfSubservices() > 0:
1084                         self.showSubServiceIndication()
1085                 else:
1086                         self.hideSubServiceIndication()
1087
1088         def checkDolby(self, service):
1089                 # FIXME
1090                 dolby = False
1091                 audio = service.audioTracks()
1092                 if audio is not None:
1093                         n = audio.getNumberOfTracks()
1094                         for x in range(n):
1095                                 i = audio.getTrackInfo(x)
1096                                 description = i.getDescription();
1097                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1098                                         dolby = True
1099                                         break
1100                 if dolby:
1101                         self["DolbyActive"].showWidget()
1102                 else:
1103                         self["DolbyActive"].hideWidget()
1104
1105         def checkCrypted(self, service):
1106                 info = service.info()
1107                 if info is not None:
1108                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1109                                 self["CryptActive"].showWidget()
1110                         else:
1111                                 self["CryptActive"].hideWidget()
1112
1113         def gotServiceEvent(self, ev):
1114                 service = self.session.nav.getCurrentService()
1115                 if ev == iPlayableService.evUpdatedEventInfo:
1116                         self.checkSubservices(service)
1117                         self.checkFormat(service)
1118                 elif ev == iPlayableService.evUpdatedInfo:
1119                         self.checkCrypted(service)
1120                         self.checkDolby(service)
1121                 elif ev == iPlayableService.evEnd:
1122                         self.hideSubServiceIndication()
1123                         self["CryptActive"].hideWidget()
1124                         self["DolbyActive"].hideWidget()
1125                         self["FormatActive"].hideWidget()
1126
1127 class InfoBarNotifications:
1128         def __init__(self):
1129                 self.onExecBegin.append(self.checkNotifications)
1130                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1131         
1132         def checkNotificationsIfExecing(self):
1133                 if self.execing:
1134                         self.checkNotifications()
1135
1136         def checkNotifications(self):
1137                 if len(Notifications.notifications):
1138                         n = Notifications.notifications[0]
1139                         Notifications.notifications = Notifications.notifications[1:]
1140                         print "open",n
1141                         cb = n[0]
1142                         if cb is not None:
1143                                 self.session.openWithCallback(cb, *n[1:])
1144                         else:
1145                                 self.session.open(*n[1:])
1146
1147 class InfoBarServiceNotifications:
1148         def __init__(self):
1149                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1150                         {
1151                                 iPlayableService.evEnd: self.serviceHasEnded
1152                         })
1153
1154         def serviceHasEnded(self):
1155                 print "service end!"
1156
1157                 try:
1158                         self.setSeekState(self.SEEK_STATE_PLAY)
1159                 except:
1160                         pass
1161
1162 class InfoBarCueSheetSupport:
1163         CUT_TYPE_IN = 0
1164         CUT_TYPE_OUT = 1
1165         CUT_TYPE_MARK = 2
1166         
1167         def __init__(self):
1168                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1169                         {
1170                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1171                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1172                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1173                         }, prio=1) 
1174                 
1175                 self.cut_list = [ ]
1176                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1177                         {
1178                                 iPlayableService.evStart: self.__serviceStarted,
1179                         })
1180
1181         def __serviceStarted(self):
1182                 print "new service started! trying to download cuts!"
1183                 self.downloadCuesheet()
1184
1185         def __getSeekable(self):
1186                 service = self.session.nav.getCurrentService()
1187                 if service is None:
1188                         return None
1189                 return service.seek()
1190
1191         def __getCurrentPosition(self):
1192                 seek = self.__getSeekable()
1193                 if seek is None:
1194                         return None
1195                 r = seek.getPlayPosition()
1196                 if r[0]:
1197                         return None
1198                 return long(r[1])
1199
1200         def jumpPreviousNextMark(self, cmp, alternative=None):
1201                 current_pos = self.__getCurrentPosition()
1202                 if current_pos is None:
1203                         return
1204                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1205                 if mark is not None:
1206                         pts = mark[0]
1207                 elif alternative is not None:
1208                         pts = alternative
1209                 else:
1210                         return
1211
1212                 seekable = self.__getSeekable()
1213                 if seekable is not None:
1214                         seekable.seekTo(pts)
1215
1216         def jumpPreviousMark(self):
1217                 print "jumpPreviousMark"
1218                 # we add 2 seconds, so if the play position is <2s after
1219                 # the mark, the mark before will be used
1220                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1221
1222         def jumpNextMark(self):
1223                 print "jumpNextMark"
1224                 self.jumpPreviousNextMark(lambda x: x)
1225
1226         def getNearestCutPoint(self, pts, cmp=abs):
1227                 # can be optimized
1228                 nearest = None
1229                 for cp in self.cut_list:
1230                         diff = cmp(cp[0] - pts)
1231                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1232                                 nearest = cp
1233                 return nearest
1234
1235         def toggleMark(self):
1236                 print "toggleMark"
1237                 current_pos = self.__getCurrentPosition()
1238                 if current_pos is None:
1239                         print "not seekable"
1240                         return
1241                 
1242                 print "current position: ", current_pos
1243
1244                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1245                 print "nearest_cutpoint: ", nearest_cutpoint
1246                 
1247                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < 5*90000:
1248                         self.cut_list.remove(nearest_cutpoint)
1249                 else:
1250                         bisect.insort(self.cut_list, (current_pos, self.CUT_TYPE_MARK))
1251                 
1252                 self.uploadCuesheet()
1253
1254         def __getCuesheet(self):
1255                 service = self.session.nav.getCurrentService()
1256                 if service is None:
1257                         return None
1258                 return service.cueSheet()
1259
1260         def uploadCuesheet(self):
1261                 cue = self.__getCuesheet()
1262
1263                 if cue is None:
1264                         print "upload failed, no cuesheet interface"
1265                         return
1266                 cue.setCutList(self.cut_list)
1267
1268         def downloadCuesheet(self):
1269                 cue = self.__getCuesheet()
1270
1271                 if cue is None:
1272                         print "upload failed, no cuesheet interface"
1273                         return
1274                 self.cut_list = cue.getCutList()
1275
1276                 print "cuts:", self.cut_list