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