revert changes from some hours ago (aka disable PiP again)
[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                 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 None
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 and 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 is not None:
980                         if answer[1] == "pipon":
981                                 self.session.nav.stopService()
982                                 self.pip = self.session.instantiateDialog(PictureInPicture)
983                                 #self.pip.show()
984                                 
985                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
986                                 self.pipservice = eServiceCenter.getInstance().play(newservice)
987                                 if self.pipservice and not self.pipservice.setTarget(1):
988                                         self.pipservice.start()
989                                         self.pipshown = True
990                                 else:
991                                         self.pipservice = None
992                                         del self.pip
993         
994                         elif answer[1] == "pipoff":
995                                 #self.pip.hide()
996                                 self.pipservice = None
997                                 del self.pip
998                                 self.pipshown = False
999
1000 from RecordTimer import parseEvent
1001
1002 class InfoBarInstantRecord:
1003         """Instant Record - handles the instantRecord action in order to 
1004         start/stop instant records"""
1005         def __init__(self):
1006                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1007                         {
1008                                 "instantRecord": (self.instantRecord, "Instant Record..."),
1009                         })
1010                 self.recording = []
1011                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1012                 self["BlinkingPoint"].hide()
1013                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1014
1015         def stopCurrentRecording(self, entry = -1):     
1016                 if entry is not None and entry != -1:
1017                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1018                         self.recording.remove(self.recording[entry])
1019                         
1020         def startInstantRecording(self, limitEvent = False):
1021                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1022                 
1023                 # try to get event info
1024                 event = None
1025                 try:
1026                         service = self.session.nav.getCurrentService()
1027                         epg = eEPGCache.getInstance()
1028                         event = epg.lookupEventTime(serviceref, -1, 0)
1029                         if event is None:
1030                                 info = service.info()
1031                                 ev = info.getEvent(0)
1032                                 event = ev
1033                 except:
1034                         pass
1035
1036                 begin = time.time()
1037                 end = time.time() + 3600 * 10
1038                 name = "instant record"
1039                 description = ""
1040                 eventid = None
1041                 
1042                 if event is not None:
1043                         curEvent = parseEvent(event)
1044                         name = curEvent[2]
1045                         description = curEvent[3]
1046                         eventid = curEvent[4]
1047                         if limitEvent:
1048                                 end = curEvent[1]
1049                 else:
1050                         if limitEvent:
1051                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1052                                 
1053                 data = (begin, end, name, description, eventid)
1054                 
1055                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1056                 recording.dontSave = True
1057                 self.recording.append(recording)
1058                 
1059                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1060                 
1061         def isInstantRecordRunning(self):
1062                 print "self.recording:", self.recording
1063                 if len(self.recording) > 0:
1064                         for x in self.recording:
1065                                 if x.isRunning():
1066                                         return True
1067                 return False
1068
1069         def recordQuestionCallback(self, answer):
1070                 print "pre:\n", self.recording
1071                 
1072                 if answer is None or answer[1] == "no":
1073                         return
1074                 list = []
1075                 recording = self.recording[:]
1076                 for x in recording:
1077                         if not x in self.session.nav.RecordTimer.timer_list:
1078                                 self.recording.remove(x)
1079                         elif x.dontSave and x.isRunning():
1080                                 list.append(TimerEntryComponent(x, False))              
1081
1082                 if answer[1] == "changeduration":
1083                         if len(self.recording) == 1:
1084                                 self.changeDuration(0)
1085                         else:
1086                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1087                 elif answer[1] == "stop":
1088                         if len(self.recording) == 1:
1089                                 self.stopCurrentRecording(0)
1090                         else:
1091                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1092                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1093                         limitEvent = False
1094                         if answer[1] == "event":
1095                                 limitEvent = True
1096                         if answer[1] == "manualduration":
1097                                 self.selectedEntry = len(self.recording)
1098                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1099                         self.startInstantRecording(limitEvent = limitEvent)
1100                         
1101                 print "after:\n", self.recording
1102
1103         def changeDuration(self, entry):
1104                 if entry is not None:
1105                         self.selectedEntry = entry
1106                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1107
1108         def inputCallback(self, value):
1109                 if value is not None:
1110                         print "stopping recording after", int(value), "minutes."
1111                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1112                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1113
1114         def instantRecord(self):
1115                 try:
1116                         stat = os.stat(resolveFilename(SCOPE_HDD))
1117                 except:
1118                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1119                         return
1120         
1121                 if self.isInstantRecordRunning():
1122                         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")])
1123                 else:
1124                         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")])
1125
1126 from Screens.AudioSelection import AudioSelection
1127
1128 class InfoBarAudioSelection:
1129         def __init__(self):
1130                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1131                         {
1132                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1133                         })
1134
1135         def audioSelection(self):
1136                 service = self.session.nav.getCurrentService()
1137                 audio = service.audioTracks()
1138                 n = audio.getNumberOfTracks()
1139                 if n > 0:
1140                         self.session.open(AudioSelection, audio)
1141
1142 from Screens.SubserviceSelection import SubserviceSelection
1143
1144 class InfoBarSubserviceSelection:
1145         def __init__(self):
1146                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1147                         {
1148                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1149                         })
1150
1151         def subserviceSelection(self):
1152                 service = self.session.nav.getCurrentService()
1153                 subservices = service.subServices()
1154                 n = subservices.getNumberOfSubservices()
1155                 if n > 0:
1156                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1157
1158         def subserviceSelected(self, service):
1159                 if not service is None:
1160                         self.session.nav.playService(service)
1161
1162 class InfoBarAdditionalInfo:
1163         def __init__(self):
1164                 self["DolbyActive"] = Pixmap()
1165                 self["CryptActive"] = Pixmap()
1166                 self["FormatActive"] = Pixmap()
1167                 
1168                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1169                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1170                 self.onLayoutFinish.append(self["ButtonRed"].update)
1171                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1172                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1173                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1174
1175                 self["ButtonGreen"] = Pixmap()
1176                 self["ButtonGreenText"] = Label(_("Subservices"))
1177
1178                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1179                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1180                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1181                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1182                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1183                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1184
1185                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1186                 self["ButtonBlue"].setConnect(lambda: False)
1187                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1188                 self["ButtonBlueText"].setConnect(lambda: False)
1189                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1190                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1191
1192                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1193
1194         def hideSubServiceIndication(self):
1195                 self["ButtonGreen"].hide()
1196                 self["ButtonGreenText"].hide()
1197
1198         def showSubServiceIndication(self):
1199                 self["ButtonGreen"].show()
1200                 self["ButtonGreenText"].show()
1201
1202         def checkFormat(self, service):
1203                 info = service.info()
1204                 if info is not None:
1205                         aspect = info.getInfo(iServiceInformation.sAspect)
1206                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1207                                 self["FormatActive"].show()
1208                         else:
1209                                 self["FormatActive"].hide()
1210
1211         def checkSubservices(self, service):
1212                 if service.subServices().getNumberOfSubservices() > 0:
1213                         self.showSubServiceIndication()
1214                 else:
1215                         self.hideSubServiceIndication()
1216
1217         def checkDolby(self, service):
1218                 # FIXME
1219                 dolby = False
1220                 audio = service.audioTracks()
1221                 if audio is not None:
1222                         n = audio.getNumberOfTracks()
1223                         for x in range(n):
1224                                 i = audio.getTrackInfo(x)
1225                                 description = i.getDescription();
1226                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1227                                         dolby = True
1228                                         break
1229                 if dolby:
1230                         self["DolbyActive"].show()
1231                 else:
1232                         self["DolbyActive"].hide()
1233
1234         def checkCrypted(self, service):
1235                 info = service.info()
1236                 if info is not None:
1237                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1238                                 self["CryptActive"].show()
1239                         else:
1240                                 self["CryptActive"].hide()
1241
1242         def gotServiceEvent(self, ev):
1243                 service = self.session.nav.getCurrentService()
1244                 if ev == iPlayableService.evUpdatedEventInfo:
1245                         self.checkSubservices(service)
1246                         self.checkFormat(service)
1247                 elif ev == iPlayableService.evUpdatedInfo:
1248                         self.checkCrypted(service)
1249                         self.checkDolby(service)
1250                 elif ev == iPlayableService.evEnd:
1251                         self.hideSubServiceIndication()
1252                         self["CryptActive"].hide()
1253                         self["DolbyActive"].hide()
1254                         self["FormatActive"].hide()
1255
1256 class InfoBarNotifications:
1257         def __init__(self):
1258                 self.onExecBegin.append(self.checkNotifications)
1259                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1260         
1261         def checkNotificationsIfExecing(self):
1262                 if self.execing:
1263                         self.checkNotifications()
1264
1265         def checkNotifications(self):
1266                 if len(Notifications.notifications):
1267                         n = Notifications.notifications[0]
1268                         Notifications.notifications = Notifications.notifications[1:]
1269                         print "open",n
1270                         cb = n[0]
1271                         if cb is not None:
1272                                 self.session.openWithCallback(cb, *n[1:])
1273                         else:
1274                                 self.session.open(*n[1:])
1275
1276 class InfoBarServiceNotifications:
1277         def __init__(self):
1278                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1279                         {
1280                                 iPlayableService.evEnd: self.serviceHasEnded
1281                         })
1282
1283         def serviceHasEnded(self):
1284                 print "service end!"
1285
1286                 try:
1287                         self.setSeekState(self.SEEK_STATE_PLAY)
1288                 except:
1289                         pass
1290
1291 class InfoBarCueSheetSupport:
1292         CUT_TYPE_IN = 0
1293         CUT_TYPE_OUT = 1
1294         CUT_TYPE_MARK = 2
1295         
1296         def __init__(self):
1297                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1298                         {
1299                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1300                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1301                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1302                         }, prio=1) 
1303                 
1304                 self.cut_list = [ ]
1305                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1306                         {
1307                                 iPlayableService.evStart: self.__serviceStarted,
1308                         })
1309
1310         def __serviceStarted(self):
1311                 print "new service started! trying to download cuts!"
1312                 self.downloadCuesheet()
1313
1314         def __getSeekable(self):
1315                 service = self.session.nav.getCurrentService()
1316                 if service is None:
1317                         return None
1318                 return service.seek()
1319
1320         def cueGetCurrentPosition(self):
1321                 seek = self.__getSeekable()
1322                 if seek is None:
1323                         return None
1324                 r = seek.getPlayPosition()
1325                 if r[0]:
1326                         return None
1327                 return long(r[1])
1328
1329         def jumpPreviousNextMark(self, cmp, alternative=None):
1330                 current_pos = self.cueGetCurrentPosition()
1331                 if current_pos is None:
1332                         return
1333                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1334                 if mark is not None:
1335                         pts = mark[0]
1336                 elif alternative is not None:
1337                         pts = alternative
1338                 else:
1339                         return
1340
1341                 seekable = self.__getSeekable()
1342                 if seekable is not None:
1343                         seekable.seekTo(pts)
1344
1345         def jumpPreviousMark(self):
1346                 # we add 2 seconds, so if the play position is <2s after
1347                 # the mark, the mark before will be used
1348                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1349
1350         def jumpNextMark(self):
1351                 self.jumpPreviousNextMark(lambda x: x)
1352
1353         def getNearestCutPoint(self, pts, cmp=abs):
1354                 # can be optimized
1355                 nearest = None
1356                 for cp in self.cut_list:
1357                         diff = cmp(cp[0] - pts)
1358                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1359                                 nearest = cp
1360                 return nearest
1361
1362         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1363                 current_pos = self.cueGetCurrentPosition()
1364                 if current_pos is None:
1365                         print "not seekable"
1366                         return
1367                 
1368                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1369                 
1370                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1371                         if onlyreturn:
1372                                 return nearest_cutpoint
1373                         if not onlyadd:
1374                                 self.removeMark(nearest_cutpoint)
1375                 elif not onlyremove and not onlyreturn:
1376                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1377                 
1378                 if onlyreturn:
1379                         return None
1380
1381         def addMark(self, point):
1382                 bisect.insort(self.cut_list, point)
1383                 self.uploadCuesheet()
1384
1385         def removeMark(self, point):
1386                 self.cut_list.remove(point)
1387                 self.uploadCuesheet()
1388
1389         def __getCuesheet(self):
1390                 service = self.session.nav.getCurrentService()
1391                 if service is None:
1392                         return None
1393                 return service.cueSheet()
1394
1395         def uploadCuesheet(self):
1396                 cue = self.__getCuesheet()
1397
1398                 if cue is None:
1399                         print "upload failed, no cuesheet interface"
1400                         return
1401                 cue.setCutList(self.cut_list)
1402
1403         def downloadCuesheet(self):
1404                 cue = self.__getCuesheet()
1405
1406                 if cue is None:
1407                         print "upload failed, no cuesheet interface"
1408                         return
1409                 self.cut_list = cue.getCutList()
1410
1411 class InfoBarSummary(Screen):
1412         skin = """
1413         <screen position="0,0" size="132,64">
1414                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1415                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1416         </screen>"""
1417
1418         def __init__(self, session, parent):
1419                 Screen.__init__(self, session)
1420                 self["CurrentService"] = ServiceName(self.session.nav)
1421                 self["Clock"] = Clock()
1422
1423 class InfoBarSummarySupport:
1424         def __init__(self):
1425                 pass
1426         
1427         def createSummary(self):
1428                 return InfoBarSummary
1429
1430 class InfoBarTeletextPlugin:
1431         def __init__(self):
1432                 self.teletext_plugin = None
1433                 
1434                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1435                         self.teletext_plugin = p
1436                 
1437                 if self.teletext_plugin is not None:
1438                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1439                                 {
1440                                         "startTeletext": (self.startTeletext, "View teletext...")
1441                                 })
1442                 else:
1443                         print "no teletext plugin found!"
1444
1445         def startTeletext(self):
1446                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())