77fca5597aaf287ca8f21b276519aa93d00ca778
[enigma2.git] / 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                 self.session.open(EPGSelection, ref)
400
401         def openSimilarList(self, eventid, refstr):
402                 self.session.open(EPGSelection, refstr, None, eventid)
403
404         def openEventView(self):
405                 self.epglist = [ ]
406                 service = self.session.nav.getCurrentService()
407                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
408                 info = service.info()
409                 ptr=info.getEvent(0)
410                 if ptr:
411                         self.epglist.append(ptr)
412                 ptr=info.getEvent(1)
413                 if ptr:
414                         self.epglist.append(ptr)
415                 if len(self.epglist) == 0:
416                         epg = eEPGCache.getInstance()
417                         ptr = epg.lookupEventTime(ref, -1)
418                         if ptr:
419                                 self.epglist.append(ptr)
420                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
421                                 if ptr:
422                                         self.epglist.append(ptr)
423                 if len(self.epglist) > 0:
424                         self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
425                 else:
426                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
427                         self.openMultiServiceEPG(False)
428
429         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
430                 if len(self.epglist) > 1:
431                         tmp = self.epglist[0]
432                         self.epglist[0]=self.epglist[1]
433                         self.epglist[1]=tmp
434                         setEvent(self.epglist[0])
435
436 class InfoBarTuner:
437         """provides a snr/agc/ber display"""
438         def __init__(self):
439                 self["snr"] = Label()
440                 self["agc"] = Label()
441                 self["ber"] = Label()
442                 self["snr_percent"] = TunerInfo(TunerInfo.SNR_PERCENTAGE, servicefkt = self.session.nav.getCurrentService)
443                 self["agc_percent"] = TunerInfo(TunerInfo.AGC_PERCENTAGE, servicefkt = self.session.nav.getCurrentService)
444                 self["ber_count"] = TunerInfo(TunerInfo.BER_VALUE, servicefkt = self.session.nav.getCurrentService)
445                 self["snr_progress"] = TunerInfo(TunerInfo.SNR_BAR, servicefkt = self.session.nav.getCurrentService)
446                 self["agc_progress"] = TunerInfo(TunerInfo.AGC_BAR, servicefkt = self.session.nav.getCurrentService)
447                 self["ber_progress"] = TunerInfo(TunerInfo.BER_BAR, servicefkt = self.session.nav.getCurrentService)
448                 self.timer = eTimer()
449                 self.timer.timeout.get().append(self.updateTunerInfo)
450                 self.timer.start(1000)
451
452         def updateTunerInfo(self):
453                 if self.instance.isVisible():
454                         self["snr_percent"].update()
455                         self["agc_percent"].update()
456                         self["ber_count"].update()
457                         self["snr_progress"].update()
458                         self["agc_progress"].update()
459                         self["ber_progress"].update()
460
461 class InfoBarEvent:
462         """provides a current/next event info display"""
463         def __init__(self):
464                 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
465                 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
466                                 
467                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
468                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
469
470                 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
471                 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
472
473                 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
474
475 class InfoBarServiceName:
476         def __init__(self):
477                 self["ServiceName"] = ServiceName(self.session.nav)
478
479 class InfoBarSeek:
480         """handles actions like seeking, pause"""
481         
482         # ispause, isff, issm
483         SEEK_STATE_PLAY = (0, 0, 0, ">")
484         SEEK_STATE_PAUSE = (1, 0, 0, "||")
485         SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
486         SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
487         SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
488         SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
489         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
490         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
491         
492         SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
493         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
494         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
495         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
496         
497         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
498         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
499         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
500         
501         def __init__(self):
502                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
503                         {
504                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
505                                 iPlayableService.evStart: self.__serviceStarted,
506                                 
507                                 iPlayableService.evEOF: self.__evEOF,
508                                 iPlayableService.evSOF: self.__evSOF,
509                         })
510
511                 class InfoBarSeekActionMap(HelpableActionMap):
512                         def __init__(self, screen, *args, **kwargs):
513                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
514                                 self.screen = screen
515                                 
516                         def action(self, contexts, action):
517                                 if action[:5] == "seek:":
518                                         time = int(action[5:])
519                                         self.screen.seekRelative(time * 90000)
520                                         return 1
521                                 else:
522                                         return HelpableActionMap.action(self, contexts, action)
523
524                 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
525                         {
526                                 "pauseService": (self.pauseService, "pause"),
527                                 "unPauseService": (self.unPauseService, "continue"),
528                                 
529                                 "seekFwd": (self.seekFwd, "skip forward"),
530                                 "seekFwdDown": self.seekFwdDown,
531                                 "seekFwdUp": self.seekFwdUp,
532                                 "seekBack": (self.seekBack, "skip backward"),
533                                 "seekBackDown": self.seekBackDown,
534                                 "seekBackUp": self.seekBackUp,
535                         }, prio=-1)
536                         # give them a little more priority to win over color buttons
537
538                 self.seekstate = self.SEEK_STATE_PLAY
539                 self.onClose.append(self.delTimer)
540                 
541                 self.fwdtimer = False
542                 self.fwdKeyTimer = eTimer()
543                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
544
545                 self.rwdtimer = False
546                 self.rwdKeyTimer = eTimer()
547                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
548                 
549                 self.onPlayStateChanged = [ ]
550                 
551                 self.lockedBecauseOfSkipping = False
552         
553         def up(self):
554                 pass
555         
556         def down(self):
557                 pass
558         
559         def delTimer(self):
560                 del self.fwdKeyTimer
561                 del self.rwdKeyTimer
562         
563         def getSeek(self):
564                 service = self.session.nav.getCurrentService()
565                 if service is None:
566                         return None
567
568                 seek = service.seek()
569
570                 if seek is None or not seek.isCurrentlySeekable():
571                         return None
572                 
573                 return seek
574         
575         def isSeekable(self):
576                 if self.getSeek() is None:
577                         return False
578                 return True
579
580         def __seekableStatusChanged(self):
581                 print "seekable status changed!"
582                 if not self.isSeekable():
583                         self["SeekActions"].setEnabled(False)
584                         print "not seekable, return to play"
585                         self.setSeekState(self.SEEK_STATE_PLAY)
586                 else:
587                         self["SeekActions"].setEnabled(True)
588                         print "seekable"
589
590         def __serviceStarted(self):
591                 self.seekstate = self.SEEK_STATE_PLAY
592
593         def setSeekState(self, state):
594                 service = self.session.nav.getCurrentService()
595                 
596                 if service is None:
597                         return False
598                 
599                 if not self.isSeekable():
600                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
601                                 state = self.SEEK_STATE_PLAY
602                 
603                 pauseable = service.pause()
604
605                 if pauseable is None:
606                         print "not pauseable."
607                         state = self.SEEK_STATE_PLAY
608                 
609                 oldstate = self.seekstate
610                 self.seekstate = state
611                 
612                 for i in range(3):
613                         if oldstate[i] != self.seekstate[i]:
614                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
615
616                 for c in self.onPlayStateChanged:
617                         c(self.seekstate)
618                 
619                 self.checkSkipShowHideLock()
620
621                 return True
622
623         def pauseService(self):
624                 if self.seekstate == self.SEEK_STATE_PAUSE:
625                         print "pause, but in fact unpause"
626                         self.unPauseService()
627                 else:
628                         if self.seekstate == self.SEEK_STATE_PLAY:
629                                 print "yes, playing."
630                         else:
631                                 print "no", self.seekstate
632                         print "pause"
633                         self.setSeekState(self.SEEK_STATE_PAUSE);
634                 
635         def unPauseService(self):
636                 print "unpause"
637                 self.setSeekState(self.SEEK_STATE_PLAY);
638         
639         def doSeek(self, seektime):
640                 print "doseek", seektime
641                 service = self.session.nav.getCurrentService()
642                 if service is None:
643                         return
644                 
645                 seekable = self.getSeek()
646                 if seekable is None:
647                         return
648                 
649                 seekable.seekTo(90 * seektime)
650
651         def seekFwdDown(self):
652                 print "start fwd timer"
653                 self.fwdtimer = True
654                 self.fwdKeyTimer.start(1000)
655
656         def seekBackDown(self):
657                 print "start rewind timer"
658                 self.rwdtimer = True
659                 self.rwdKeyTimer.start(1000)
660
661         def seekFwdUp(self):
662                 print "seekFwdUp"
663                 if self.fwdtimer:
664                         self.fwdKeyTimer.stop()
665                         self.fwdtimer = False
666                         self.seekFwd()
667
668         def seekFwd(self):
669                 lookup = {
670                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
671                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
672                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
673                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
674                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
675                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
676                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
677                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
678                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
679                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
680                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
681                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
682                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
683                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
684                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
685                         }
686                 self.setSeekState(lookup[self.seekstate])
687         
688         def seekBackUp(self):
689                 print "seekBackUp"
690                 if self.rwdtimer:
691                         self.rwdKeyTimer.stop()
692                         self.rwdtimer = False
693                         self.seekBack()
694                 
695         def seekBack(self):
696                 lookup = {
697                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
698                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
699                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
700                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
701                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
702                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
703                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
704                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
705                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
706                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
707                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
708                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
709                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
710                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
711                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
712                         }
713                 self.setSeekState(lookup[self.seekstate])
714                 
715                 if self.seekstate == self.SEEK_STATE_PAUSE:
716                         seekable = self.getSeek()
717                         if seekable is not None:
718                                 seekable.seekRelative(-1, 3)
719
720         def fwdTimerFire(self):
721                 print "Display seek fwd"
722                 self.fwdKeyTimer.stop()
723                 self.fwdtimer = False
724                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
725                 
726         def fwdSeekTo(self, minutes):
727                 print "Seek", minutes, "minutes forward"
728                 if minutes != 0:
729                         seekable = self.getSeek()
730                         if seekable is not None:
731                                 seekable.seekRelative(1, minutes * 60 * 90000)
732         
733         def rwdTimerFire(self):
734                 print "rwdTimerFire"
735                 self.rwdKeyTimer.stop()
736                 self.rwdtimer = False
737                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
738         
739         def rwdSeekTo(self, minutes):
740                 print "rwdSeekTo"
741                 self.fwdSeekTo(0 - minutes)
742         
743         def checkSkipShowHideLock(self):
744                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
745                 
746                 if self.lockedBecauseOfSkipping and not wantlock:
747                         self.unlockShow()
748                         self.lockedBecauseOfSkipping = False
749                 
750                 if wantlock and not self.lockedBecauseOfSkipping:
751                         self.lockShow()
752                         self.lockedBecauseOfSkipping = True
753
754         def __evEOF(self):
755                 if self.seekstate != self.SEEK_STATE_PLAY:
756                         self.setSeekState(self.SEEK_STATE_PAUSE)
757                         # HACK
758                         #self.getSeek().seekRelative(1, -90000)
759                         self.setSeekState(self.SEEK_STATE_PLAY)
760                 else:
761                         self.setSeekState(self.SEEK_STATE_PAUSE)
762         
763         def __evSOF(self):
764                 self.setSeekState(self.SEEK_STATE_PLAY)
765                 self.doSeek(0)
766
767         def seekRelative(self, diff):
768                 seekable = self.getSeek()
769                 if seekable is not None:
770                         seekable.seekRelative(1, diff)
771
772 from Screens.PVRState import PVRState, TimeshiftState
773
774 class InfoBarPVRState:
775         def __init__(self, screen=PVRState):
776                 self.onPlayStateChanged.append(self.__playStateChanged)
777                 self.pvrStateDialog = self.session.instantiateDialog(screen)
778                 self.onShow.append(self.__mayShow)
779                 self.onHide.append(self.pvrStateDialog.hide)
780         
781         def __mayShow(self):
782                 if self.seekstate != self.SEEK_STATE_PLAY and self.execing:
783                         self.pvrStateDialog.show()
784
785         def __playStateChanged(self, state):
786                 playstateString = state[3]
787                 self.pvrStateDialog["state"].setText(playstateString)
788                 self.__mayShow()
789
790 class InfoBarTimeshiftState(InfoBarPVRState):
791         def __init__(self):
792                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
793
794
795 class InfoBarShowMovies:
796
797         # i don't really like this class. 
798         # it calls a not further specified "movie list" on up/down/movieList,
799         # so this is not more than an action map
800         def __init__(self):
801                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
802                         {
803                                 "movieList": (self.showMovies, "movie list"),
804                                 "up": (self.showMovies, "movie list"),
805                                 "down": (self.showMovies, "movie list")
806                         })
807
808 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
809
810 # Hrmf.
811 #
812 # Timeshift works the following way:
813 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
814 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
815 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
816 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
817 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
818 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
819 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
820 #
821
822 # in other words:
823 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
824 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
825 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
826 # - the user can now PVR around
827 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
828 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
829 # after!
830 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
831 # - if the user rewinds, or press pause, timeshift will be activated again
832
833 # note that a timeshift can be enabled ("recording") and
834 # activated (currently time-shifting).
835
836 class InfoBarTimeshift:
837         def __init__(self):
838                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
839                         {
840                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
841                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
842                         }, prio=1)
843                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
844                         {
845                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
846                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
847                         }, prio=-1) # priority over record
848
849                 self.timeshift_enabled = 0
850                 self.timeshift_state = 0
851                 self.ts_pause_timer = eTimer()
852                 self.ts_pause_timer.timeout.get().append(self.pauseService)
853
854                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
855                         {
856                                 iPlayableService.evStart: self.__serviceStarted,
857                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
858                         })
859         
860         def getTimeshift(self):
861                 service = self.session.nav.getCurrentService()
862                 return service and service.timeshift()
863
864         def startTimeshift(self):
865                 print "enable timeshift"
866                 ts = self.getTimeshift()
867                 if ts is None:
868                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
869                         print "no ts interface"
870                         return
871                 
872                 if self.timeshift_enabled:
873                         print "hu, timeshift already enabled?"
874                 else:
875                         if not ts.startTimeshift():
876                                 import time
877                                 self.timeshift_enabled = 1
878                                 self.pvrStateDialog["timeshift"].setRelative(time.time())
879                                 
880                                 # PAUSE.
881                                 self.setSeekState(self.SEEK_STATE_PAUSE)
882                                 
883                                 # enable the "TimeshiftEnableActions", which will override
884                                 # the startTimeshift actions
885                                 self.__seekableStatusChanged()
886                         else:
887                                 print "timeshift failed"
888
889         def stopTimeshift(self):
890                 if not self.timeshift_enabled:
891                         return
892                 print "disable timeshift"
893                 ts = self.getTimeshift()
894                 if ts is None:
895                         return
896                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
897
898         def stopTimeshiftConfirmed(self, confirmed):
899                 if not confirmed:
900                         return
901
902                 ts = self.getTimeshift()
903                 if ts is None:
904                         return
905
906                 ts.stopTimeshift()
907                 self.timeshift_enabled = 0
908
909                 # disable actions
910                 self.__seekableStatusChanged()
911         
912         # activates timeshift, and seeks to (almost) the end
913         def activateTimeshiftEnd(self):
914                 ts = self.getTimeshift()
915                 
916                 if ts is None:
917                         return
918                 
919                 if ts.isTimeshiftActive():
920                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
921                         self.pauseService()
922                 else:
923                         self.setSeekState(self.SEEK_STATE_PLAY)
924                         ts.activateTimeshift()
925                         self.seekRelative(0)
926         
927         # same as activateTimeshiftEnd, but pauses afterwards.
928         def activateTimeshiftEndAndPause(self):
929                 state = self.seekstate
930                 self.activateTimeshiftEnd()
931                 
932                 # well, this is "andPause", but it could be pressed from pause,
933                 # when pausing on the (fake-)"live" picture, so an un-pause
934                 # is perfectly ok.
935                 
936                 print "now, pauseService"
937                 if state == self.SEEK_STATE_PLAY:
938                         print "is PLAYING, start pause timer"
939                         self.ts_pause_timer.start(200, 1)
940                 else:
941                         print "unpause"
942                         self.unPauseService()
943         
944         def __seekableStatusChanged(self):
945                 enabled = False
946                 
947                 print "self.isSeekable", self.isSeekable()
948                 print "self.timeshift_enabled", self.timeshift_enabled
949                 
950                 # when this service is not seekable, but timeshift
951                 # is enabled, this means we can activate
952                 # the timeshift
953                 if not self.isSeekable() and self.timeshift_enabled:
954                         enabled = True
955
956                 print "timeshift activate:", enabled
957                 self["TimeshiftActivateActions"].setEnabled(enabled)
958
959         def __serviceStarted(self):
960                 self.timeshift_enabled = False
961                 self.__seekableStatusChanged()
962
963 class InfoBarExtensions:
964         def __init__(self):
965                 self.pipshown = False
966                 
967                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
968                         {
969                                 #"extensions": (self.extensions, "Extensions..."),
970                         })
971                         
972         def extensions(self):
973                 list = []
974                 if self.pipshown == False:
975                         list.append((_("Activate Picture in Picture"), "pipon"))
976                 elif self.pipshown == True:
977                         list.append((_("Disable Picture in Picture"), "pipoff"))
978                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list)
979
980         def extensionCallback(self, answer):
981                 if answer is not None:
982                         if answer[1] == "pipon":
983                                 self.session.nav.stopService()
984                                 self.pip = self.session.instantiateDialog(PictureInPicture)
985                                 #self.pip.show()
986                                 
987                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
988                                 self.pipservice = eServiceCenter.getInstance().play(newservice)
989                                 if self.pipservice and not self.pipservice.setTarget(1):
990                                         self.pipservice.start()
991                                         self.pipshown = True
992                                 else:
993                                         self.pipservice = None
994                                         del self.pip
995         
996                         elif answer[1] == "pipoff":
997                                 #self.pip.hide()
998                                 self.pipservice = None
999                                 del self.pip
1000                                 self.pipshown = False
1001
1002 from RecordTimer import parseEvent
1003
1004 class InfoBarInstantRecord:
1005         """Instant Record - handles the instantRecord action in order to 
1006         start/stop instant records"""
1007         def __init__(self):
1008                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1009                         {
1010                                 "instantRecord": (self.instantRecord, "Instant Record..."),
1011                         })
1012                 self.recording = []
1013                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1014                 self["BlinkingPoint"].hide()
1015                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1016
1017         def stopCurrentRecording(self, entry = -1):     
1018                 if entry is not None and entry != -1:
1019                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1020                         self.recording.remove(self.recording[entry])
1021                         
1022         def startInstantRecording(self, limitEvent = False):
1023                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1024                 
1025                 # try to get event info
1026                 event = None
1027                 try:
1028                         service = self.session.nav.getCurrentService()
1029                         epg = eEPGCache.getInstance()
1030                         event = epg.lookupEventTime(serviceref, -1, 0)
1031                         if event is None:
1032                                 info = service.info()
1033                                 ev = info.getEvent(0)
1034                                 event = ev
1035                 except:
1036                         pass
1037
1038                 begin = time.time()
1039                 end = time.time() + 3600 * 10
1040                 name = "instant record"
1041                 description = ""
1042                 eventid = None
1043                 
1044                 if event is not None:
1045                         curEvent = parseEvent(event)
1046                         name = curEvent[2]
1047                         description = curEvent[3]
1048                         eventid = curEvent[4]
1049                         if limitEvent:
1050                                 end = curEvent[1]
1051                 else:
1052                         if limitEvent:
1053                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1054                                 
1055                 data = (begin, end, name, description, eventid)
1056                 
1057                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1058                 recording.dontSave = True
1059                 self.recording.append(recording)
1060                 
1061                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1062                 
1063         def isInstantRecordRunning(self):
1064                 print "self.recording:", self.recording
1065                 if len(self.recording) > 0:
1066                         for x in self.recording:
1067                                 if x.isRunning():
1068                                         return True
1069                 return False
1070
1071         def recordQuestionCallback(self, answer):
1072                 print "pre:\n", self.recording
1073                 
1074                 if answer is None or answer[1] == "no":
1075                         return
1076                 list = []
1077                 recording = self.recording[:]
1078                 for x in recording:
1079                         if not x in self.session.nav.RecordTimer.timer_list:
1080                                 self.recording.remove(x)
1081                         elif x.dontSave and x.isRunning():
1082                                 list.append(TimerEntryComponent(x, False))              
1083
1084                 if answer[1] == "changeduration":
1085                         if len(self.recording) == 1:
1086                                 self.changeDuration(0)
1087                         else:
1088                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1089                 elif answer[1] == "stop":
1090                         if len(self.recording) == 1:
1091                                 self.stopCurrentRecording(0)
1092                         else:
1093                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1094                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1095                         limitEvent = False
1096                         if answer[1] == "event":
1097                                 limitEvent = True
1098                         if answer[1] == "manualduration":
1099                                 self.selectedEntry = len(self.recording)
1100                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1101                         self.startInstantRecording(limitEvent = limitEvent)
1102                         
1103                 print "after:\n", self.recording
1104
1105         def changeDuration(self, entry):
1106                 if entry is not None:
1107                         self.selectedEntry = entry
1108                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1109
1110         def inputCallback(self, value):
1111                 if value is not None:
1112                         print "stopping recording after", int(value), "minutes."
1113                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1114                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1115
1116         def instantRecord(self):
1117                 try:
1118                         stat = os.stat(resolveFilename(SCOPE_HDD))
1119                 except:
1120                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1121                         return
1122         
1123                 if self.isInstantRecordRunning():
1124                         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")])
1125                 else:
1126                         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")])
1127
1128 from Screens.AudioSelection import AudioSelection
1129
1130 class InfoBarAudioSelection:
1131         def __init__(self):
1132                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1133                         {
1134                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1135                         })
1136
1137         def audioSelection(self):
1138                 service = self.session.nav.getCurrentService()
1139                 audio = service.audioTracks()
1140                 n = audio.getNumberOfTracks()
1141                 if n > 0:
1142                         self.session.open(AudioSelection, audio)
1143
1144 from Screens.SubserviceSelection import SubserviceSelection
1145
1146 class InfoBarSubserviceSelection:
1147         def __init__(self):
1148                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1149                         {
1150                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1151                         })
1152
1153         def subserviceSelection(self):
1154                 service = self.session.nav.getCurrentService()
1155                 subservices = service.subServices()
1156                 n = subservices.getNumberOfSubservices()
1157                 if n > 0:
1158                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1159
1160         def subserviceSelected(self, service):
1161                 if not service is None:
1162                         self.session.nav.playService(service)
1163
1164 class InfoBarAdditionalInfo:
1165         def __init__(self):
1166                 self["DolbyActive"] = Pixmap()
1167                 self["CryptActive"] = Pixmap()
1168                 self["FormatActive"] = Pixmap()
1169                 
1170                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1171                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1172                 self.onLayoutFinish.append(self["ButtonRed"].update)
1173                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1174                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1175                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1176
1177                 self["ButtonGreen"] = Pixmap()
1178                 self["ButtonGreenText"] = Label(_("Subservices"))
1179
1180                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1181                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1182                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1183                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1184                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1185                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1186
1187                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1188                 self["ButtonBlue"].setConnect(lambda: False)
1189                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1190                 self["ButtonBlueText"].setConnect(lambda: False)
1191                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1192                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1193
1194                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1195
1196         def hideSubServiceIndication(self):
1197                 self["ButtonGreen"].hide()
1198                 self["ButtonGreenText"].hide()
1199
1200         def showSubServiceIndication(self):
1201                 self["ButtonGreen"].show()
1202                 self["ButtonGreenText"].show()
1203
1204         def checkFormat(self, service):
1205                 info = service.info()
1206                 if info is not None:
1207                         aspect = info.getInfo(iServiceInformation.sAspect)
1208                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1209                                 self["FormatActive"].show()
1210                         else:
1211                                 self["FormatActive"].hide()
1212
1213         def checkSubservices(self, service):
1214                 if service.subServices().getNumberOfSubservices() > 0:
1215                         self.showSubServiceIndication()
1216                 else:
1217                         self.hideSubServiceIndication()
1218
1219         def checkDolby(self, service):
1220                 # FIXME
1221                 dolby = False
1222                 audio = service.audioTracks()
1223                 if audio is not None:
1224                         n = audio.getNumberOfTracks()
1225                         for x in range(n):
1226                                 i = audio.getTrackInfo(x)
1227                                 description = i.getDescription();
1228                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1229                                         dolby = True
1230                                         break
1231                 if dolby:
1232                         self["DolbyActive"].show()
1233                 else:
1234                         self["DolbyActive"].hide()
1235
1236         def checkCrypted(self, service):
1237                 info = service.info()
1238                 if info is not None:
1239                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1240                                 self["CryptActive"].show()
1241                         else:
1242                                 self["CryptActive"].hide()
1243
1244         def gotServiceEvent(self, ev):
1245                 service = self.session.nav.getCurrentService()
1246                 if ev == iPlayableService.evUpdatedEventInfo:
1247                         self.checkSubservices(service)
1248                         self.checkFormat(service)
1249                 elif ev == iPlayableService.evUpdatedInfo:
1250                         self.checkCrypted(service)
1251                         self.checkDolby(service)
1252                 elif ev == iPlayableService.evEnd:
1253                         self.hideSubServiceIndication()
1254                         self["CryptActive"].hide()
1255                         self["DolbyActive"].hide()
1256                         self["FormatActive"].hide()
1257
1258 class InfoBarNotifications:
1259         def __init__(self):
1260                 self.onExecBegin.append(self.checkNotifications)
1261                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1262         
1263         def checkNotificationsIfExecing(self):
1264                 if self.execing:
1265                         self.checkNotifications()
1266
1267         def checkNotifications(self):
1268                 if len(Notifications.notifications):
1269                         n = Notifications.notifications[0]
1270                         Notifications.notifications = Notifications.notifications[1:]
1271                         print "open",n
1272                         cb = n[0]
1273                         if cb is not None:
1274                                 self.session.openWithCallback(cb, *n[1:])
1275                         else:
1276                                 self.session.open(*n[1:])
1277
1278 class InfoBarServiceNotifications:
1279         def __init__(self):
1280                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1281                         {
1282                                 iPlayableService.evEnd: self.serviceHasEnded
1283                         })
1284
1285         def serviceHasEnded(self):
1286                 print "service end!"
1287
1288                 try:
1289                         self.setSeekState(self.SEEK_STATE_PLAY)
1290                 except:
1291                         pass
1292
1293 class InfoBarCueSheetSupport:
1294         CUT_TYPE_IN = 0
1295         CUT_TYPE_OUT = 1
1296         CUT_TYPE_MARK = 2
1297         
1298         def __init__(self):
1299                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1300                         {
1301                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1302                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1303                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1304                         }, prio=1) 
1305                 
1306                 self.cut_list = [ ]
1307                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1308                         {
1309                                 iPlayableService.evStart: self.__serviceStarted,
1310                         })
1311
1312         def __serviceStarted(self):
1313                 print "new service started! trying to download cuts!"
1314                 self.downloadCuesheet()
1315
1316         def __getSeekable(self):
1317                 service = self.session.nav.getCurrentService()
1318                 if service is None:
1319                         return None
1320                 return service.seek()
1321
1322         def cueGetCurrentPosition(self):
1323                 seek = self.__getSeekable()
1324                 if seek is None:
1325                         return None
1326                 r = seek.getPlayPosition()
1327                 if r[0]:
1328                         return None
1329                 return long(r[1])
1330
1331         def jumpPreviousNextMark(self, cmp, alternative=None):
1332                 current_pos = self.cueGetCurrentPosition()
1333                 if current_pos is None:
1334                         return
1335                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1336                 if mark is not None:
1337                         pts = mark[0]
1338                 elif alternative is not None:
1339                         pts = alternative
1340                 else:
1341                         return
1342
1343                 seekable = self.__getSeekable()
1344                 if seekable is not None:
1345                         seekable.seekTo(pts)
1346
1347         def jumpPreviousMark(self):
1348                 # we add 2 seconds, so if the play position is <2s after
1349                 # the mark, the mark before will be used
1350                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1351
1352         def jumpNextMark(self):
1353                 self.jumpPreviousNextMark(lambda x: x)
1354
1355         def getNearestCutPoint(self, pts, cmp=abs):
1356                 # can be optimized
1357                 nearest = None
1358                 for cp in self.cut_list:
1359                         diff = cmp(cp[0] - pts)
1360                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1361                                 nearest = cp
1362                 return nearest
1363
1364         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1365                 current_pos = self.cueGetCurrentPosition()
1366                 if current_pos is None:
1367                         print "not seekable"
1368                         return
1369                 
1370                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1371                 
1372                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1373                         if onlyreturn:
1374                                 return nearest_cutpoint
1375                         if not onlyadd:
1376                                 self.removeMark(nearest_cutpoint)
1377                 elif not onlyremove and not onlyreturn:
1378                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1379                 
1380                 if onlyreturn:
1381                         return None
1382
1383         def addMark(self, point):
1384                 bisect.insort(self.cut_list, point)
1385                 self.uploadCuesheet()
1386
1387         def removeMark(self, point):
1388                 self.cut_list.remove(point)
1389                 self.uploadCuesheet()
1390
1391         def __getCuesheet(self):
1392                 service = self.session.nav.getCurrentService()
1393                 if service is None:
1394                         return None
1395                 return service.cueSheet()
1396
1397         def uploadCuesheet(self):
1398                 cue = self.__getCuesheet()
1399
1400                 if cue is None:
1401                         print "upload failed, no cuesheet interface"
1402                         return
1403                 cue.setCutList(self.cut_list)
1404
1405         def downloadCuesheet(self):
1406                 cue = self.__getCuesheet()
1407
1408                 if cue is None:
1409                         print "upload failed, no cuesheet interface"
1410                         return
1411                 self.cut_list = cue.getCutList()
1412
1413 class InfoBarSummary(Screen):
1414         skin = """
1415         <screen position="0,0" size="132,64">
1416                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1417                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1418         </screen>"""
1419
1420         def __init__(self, session, parent):
1421                 Screen.__init__(self, session)
1422                 self["CurrentService"] = ServiceName(self.session.nav)
1423                 self["Clock"] = Clock()
1424
1425 class InfoBarSummarySupport:
1426         def __init__(self):
1427                 pass
1428         
1429         def createSummary(self):
1430                 return InfoBarSummary
1431
1432 class InfoBarTeletextPlugin:
1433         def __init__(self):
1434                 self.teletext_plugin = None
1435                 
1436                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1437                         self.teletext_plugin = p
1438                 
1439                 if self.teletext_plugin is not None:
1440                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1441                                 {
1442                                         "startTeletext": (self.startTeletext, "View teletext...")
1443                                 })
1444                 else:
1445                         print "no teletext plugin found!"
1446
1447         def startTeletext(self):
1448                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())