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