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