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