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