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