fix possible blue screen
[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.session.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.session.pipshown == False:
1048                         list.append((_("Activate Picture in Picture"), self.PIPON))
1049                 elif self.session.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.session.pip = self.session.instantiateDialog(PictureInPicture)
1066                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1067                                 if self.session.pip.playService(newservice):
1068                                         self.session.pipshown = True
1069                                         self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1070                                 else:
1071                                         self.session.pipshown = False
1072                                         del self.session.pip
1073                                 self.session.nav.playService(newservice)
1074                         elif answer[1] == self.PIPOFF:
1075                                 del self.session.pip
1076                                 self.session.pipshown = False
1077                         elif answer[1] == self.PIPSWAP:
1078                                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1079                                 if self.session.pip.servicePath:
1080                                         servicepath = self.servicelist.getCurrentServicePath()
1081                                         ref=servicepath[len(servicepath)-1]
1082                                         pipref=self.session.pip.getCurrentService()
1083                                         self.session.pip.playService(swapservice)
1084                                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1085                                         if pipref.toString() != ref.toString(): # is a subservice ?
1086                                                 self.session.nav.stopService() # stop portal
1087                                                 self.session.nav.playService(pipref) # start subservice
1088                                         self.session.pip.servicePath=servicepath
1089                         elif answer[1] == self.MOVEPIP:
1090                                 self.session.open(PiPSetup, pip = self.session.pip)
1091                         elif answer[1] == self.ENABLE_SUBTITLE:
1092                                 self.selected_subtitle = answer[2]
1093                                 self.subtitles_enabled = True
1094
1095 from RecordTimer import parseEvent
1096
1097 class InfoBarInstantRecord:
1098         """Instant Record - handles the instantRecord action in order to 
1099         start/stop instant records"""
1100         def __init__(self):
1101                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1102                         {
1103                                 "instantRecord": (self.instantRecord, "Instant Record..."),
1104                         })
1105                 self.recording = []
1106                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1107                 self["BlinkingPoint"].hide()
1108                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1109
1110         def stopCurrentRecording(self, entry = -1):     
1111                 if entry is not None and entry != -1:
1112                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1113                         self.recording.remove(self.recording[entry])
1114
1115         def startInstantRecording(self, limitEvent = False):
1116                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1117                 
1118                 # try to get event info
1119                 event = None
1120                 try:
1121                         service = self.session.nav.getCurrentService()
1122                         epg = eEPGCache.getInstance()
1123                         event = epg.lookupEventTime(serviceref, -1, 0)
1124                         if event is None:
1125                                 info = service.info()
1126                                 ev = info.getEvent(0)
1127                                 event = ev
1128                 except:
1129                         pass
1130
1131                 begin = time.time()
1132                 end = time.time() + 3600 * 10
1133                 name = "instant record"
1134                 description = ""
1135                 eventid = None
1136                 
1137                 if event is not None:
1138                         curEvent = parseEvent(event)
1139                         name = curEvent[2]
1140                         description = curEvent[3]
1141                         eventid = curEvent[4]
1142                         if limitEvent:
1143                                 end = curEvent[1]
1144                 else:
1145                         if limitEvent:
1146                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1147                                 
1148                 data = (begin, end, name, description, eventid)
1149                 
1150                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1151                 recording.dontSave = True
1152                 self.recording.append(recording)
1153                 
1154                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1155                 
1156         def isInstantRecordRunning(self):
1157                 print "self.recording:", self.recording
1158                 if len(self.recording) > 0:
1159                         for x in self.recording:
1160                                 if x.isRunning():
1161                                         return True
1162                 return False
1163
1164         def recordQuestionCallback(self, answer):
1165                 print "pre:\n", self.recording
1166                 
1167                 if answer is None or answer[1] == "no":
1168                         return
1169                 list = []
1170                 recording = self.recording[:]
1171                 for x in recording:
1172                         if not x in self.session.nav.RecordTimer.timer_list:
1173                                 self.recording.remove(x)
1174                         elif x.dontSave and x.isRunning():
1175                                 list.append(TimerEntryComponent(x, False))              
1176
1177                 if answer[1] == "changeduration":
1178                         if len(self.recording) == 1:
1179                                 self.changeDuration(0)
1180                         else:
1181                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1182                 elif answer[1] == "stop":
1183                         if len(self.recording) == 1:
1184                                 self.stopCurrentRecording(0)
1185                         else:
1186                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1187                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1188                         limitEvent = False
1189                         if answer[1] == "event":
1190                                 limitEvent = True
1191                         if answer[1] == "manualduration":
1192                                 self.selectedEntry = len(self.recording)
1193                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1194                         self.startInstantRecording(limitEvent = limitEvent)
1195                         
1196                 print "after:\n", self.recording
1197
1198         def changeDuration(self, entry):
1199                 if entry is not None:
1200                         self.selectedEntry = entry
1201                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1202
1203         def inputCallback(self, value):
1204                 if value is not None:
1205                         print "stopping recording after", int(value), "minutes."
1206                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1207                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1208
1209         def instantRecord(self):
1210                 try:
1211                         stat = os.stat(resolveFilename(SCOPE_HDD))
1212                 except:
1213                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1214                         return
1215         
1216                 if self.isInstantRecordRunning():
1217                         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")])
1218                 else:
1219                         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")])
1220
1221 from Tools.ISO639 import LanguageCodes
1222
1223 class InfoBarAudioSelection:
1224         def __init__(self):
1225                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1226                         {
1227                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1228                         })
1229
1230         def audioSelection(self):
1231                 service = self.session.nav.getCurrentService()
1232                 audio = service and service.audioTracks()
1233                 self.audioTracks = audio
1234                 n = audio and audio.getNumberOfTracks()
1235                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1236                 tlist = []
1237                 print "tlist:", tlist
1238                 if n and n > 0:
1239                         self.audioChannel = service.audioChannel()
1240
1241                         for x in range(n):
1242                                 i = audio.getTrackInfo(x)
1243                                 language = i.getLanguage()
1244                                 description = i.getDescription()
1245         
1246                                 if len(language) == 3:
1247                                         if language in LanguageCodes:
1248                                                 language = LanguageCodes[language][0]
1249         
1250                                 if len(description):
1251                                         description += " (" + language + ")"
1252                                 else:
1253                                         description = language
1254         
1255                                 tlist.append((description, x))
1256                         
1257                         selectedAudio = tlist[0][1]
1258                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1259
1260                         selection = 2
1261                         for x in tlist:
1262                                 if x[1] != selectedAudio:
1263                                         selection += 1
1264                                 else:
1265                                         break
1266
1267                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1268                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1269                 else:
1270                         del self.audioTracks
1271
1272         def audioSelected(self, audio):
1273                 if audio is not None:
1274                         if isinstance(audio[1], str):
1275                                 if audio[1] == "mode":
1276                                         keys = ["red", "green", "yellow"]
1277                                         selection = self.audioChannel.getCurrentChannel()
1278                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1279                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1280                         else:
1281                                 del self.audioChannel
1282                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1283                                         self.audioTracks.selectTrack(audio[1])
1284                 else:
1285                         del self.audioChannel
1286                 del self.audioTracks
1287
1288         def modeSelected(self, mode):
1289                 if mode is not None:
1290                         self.audioChannel.selectChannel(mode[1])
1291                 del self.audioChannel
1292
1293 class InfoBarSubserviceSelection:
1294         def __init__(self):
1295                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1296                         {
1297                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1298                         })
1299
1300                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1301                         {
1302                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1303                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1304                         }, -1)
1305                 self["SubserviceQuickzapAction"].setEnabled(False)
1306
1307                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1308
1309         def checkSubservicesAvail(self, ev):
1310                 if ev == iPlayableService.evUpdatedEventInfo:
1311                         service = self.session.nav.getCurrentService()
1312                         subservices = service and service.subServices()
1313                         if not subservices or subservices.getNumberOfSubservices() == 0:
1314                                 self["SubserviceQuickzapAction"].setEnabled(False)
1315
1316         def nextSubservice(self):
1317                 self.changeSubservice(+1)
1318
1319         def prevSubservice(self):
1320                 self.changeSubservice(-1)
1321
1322         def changeSubservice(self, direction):
1323                 service = self.session.nav.getCurrentService()
1324                 subservices = service and service.subServices()
1325                 n = subservices and subservices.getNumberOfSubservices()
1326                 if n and n > 0:
1327                         selection = -1
1328                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1329                         for x in range(n):
1330                                 if subservices.getSubservice(x).toString() == ref.toString():
1331                                         selection = x
1332                         if selection != -1:
1333                                 selection += direction
1334                                 if selection >= n:
1335                                         selection=0
1336                                 elif selection < 0:
1337                                         selection=n-1
1338                                 newservice = subservices.getSubservice(selection)
1339                                 if newservice.valid():
1340                                         del subservices
1341                                         del service
1342                                         self.session.nav.playService(newservice)
1343
1344         def subserviceSelection(self):
1345                 service = self.session.nav.getCurrentService()
1346                 subservices = service and service.subServices()
1347                 
1348                 n = subservices and subservices.getNumberOfSubservices()
1349                 selection = 0
1350                 if n and n > 0:
1351                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1352                         tlist = []
1353                         for x in range(n):
1354                                 i = subservices.getSubservice(x)
1355                                 if i.toString() == ref.toString():
1356                                         selection = x
1357                                 tlist.append((i.getName(), i))
1358
1359                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection)
1360
1361         def subserviceSelected(self, service):
1362                 if not service is None:
1363                         self["SubserviceQuickzapAction"].setEnabled(True)
1364                         self.session.nav.playService(service[1])
1365
1366 class InfoBarAdditionalInfo:
1367         def __init__(self):
1368                 self["NimA"] = Pixmap()
1369                 self["NimB"] = Pixmap()
1370                 self["TextActive"] = Pixmap()
1371                 self["DolbyActive"] = Pixmap()
1372                 self["CryptActive"] = Pixmap()
1373                 self["FormatActive"] = Pixmap()
1374                 self["NimA_Active"] = Pixmap()
1375                 self["NimB_Active"] = Pixmap()
1376
1377                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1378                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1379                 self.onLayoutFinish.append(self["ButtonRed"].update)
1380                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1381                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1382                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1383
1384                 self["ButtonGreen"] = Pixmap()
1385                 self["ButtonGreenText"] = Label(_("Subservices"))
1386
1387                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1388                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1389                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1390                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1391                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1392                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1393
1394                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1395                 self["ButtonBlue"].setConnect(lambda: True)
1396                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1397                 self["ButtonBlueText"].setConnect(lambda: True)
1398                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1399                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1400
1401                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1402                 res_mgr = eDVBResourceManagerPtr()
1403                 if eDVBResourceManager.getInstance(res_mgr) == 0:
1404                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1405
1406         def tunerUseMaskChanged(self, mask):
1407                 if mask&1:
1408                         self["NimA_Active"].show()
1409                 else:
1410                         self["NimA_Active"].hide()
1411                 if mask&2:
1412                         self["NimB_Active"].show()
1413                 else:
1414                         self["NimB_Active"].hide()
1415
1416         def hideSubServiceIndication(self):
1417                 self["ButtonGreen"].hide()
1418                 self["ButtonGreenText"].hide()
1419
1420         def showSubServiceIndication(self):
1421                 self["ButtonGreen"].show()
1422                 self["ButtonGreenText"].show()
1423
1424         def checkFormat(self, service):
1425                 info = service.info()
1426                 if info:
1427                         aspect = info.getInfo(iServiceInformation.sAspect)
1428                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1429                                 self["FormatActive"].show()
1430                                 return
1431                 self["FormatActive"].hide()
1432
1433         def checkText(self, service):
1434                 info = service.info()
1435                 if info:
1436                         tpid = info.getInfo(iServiceInformation.sTXTPID)
1437                         if tpid != -1:
1438                                 self["TextActive"].show()
1439                                 return
1440                 self["TextActive"].hide()
1441
1442         def checkSubservices(self, service):
1443                 subservices = service.subServices()
1444                 if subservices and subservices.getNumberOfSubservices() > 0:
1445                         self.showSubServiceIndication()
1446                 else:
1447                         self.hideSubServiceIndication()
1448
1449         def checkDolby(self, service):
1450                 # FIXME
1451                 dolby = False
1452                 audio = service.audioTracks()
1453                 if audio:
1454                         n = audio.getNumberOfTracks()
1455                         for x in range(n):
1456                                 i = audio.getTrackInfo(x)
1457                                 description = i.getDescription();
1458                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1459                                         dolby = True
1460                                         break
1461                 if dolby:
1462                         self["DolbyActive"].show()
1463                 else:
1464                         self["DolbyActive"].hide()
1465
1466         def checkCrypted(self, service):
1467                 info = service.info()
1468                 if info and info.getInfo(iServiceInformation.sIsCrypted) > 0:
1469                         self["CryptActive"].show()
1470                 else:
1471                         self["CryptActive"].hide()
1472
1473         def checkTunerState(self, service):
1474                 info = service.frontendInfo()
1475                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1476                 if feNumber is None:
1477                         self["NimA"].hide()
1478                         self["NimB"].hide()
1479                 elif feNumber == 0:
1480                         self["NimB"].hide()
1481                         self["NimA"].show()
1482                 elif feNumber == 1:
1483                         self["NimA"].hide()
1484                         self["NimB"].show()
1485
1486         def gotServiceEvent(self, ev):
1487                 service = self.session.nav.getCurrentService()
1488                 if ev == iPlayableService.evStart:
1489                         self.checkTunerState(service)
1490                 elif ev == iPlayableService.evUpdatedEventInfo:
1491                         self.checkSubservices(service)
1492                         self.checkFormat(service)
1493                 elif ev == iPlayableService.evUpdatedInfo:
1494                         self.checkCrypted(service)
1495                         self.checkDolby(service)
1496                         self.checkText(service)
1497                 elif ev == iPlayableService.evEnd:
1498                         self.hideSubServiceIndication()
1499                         self["CryptActive"].hide()
1500                         self["DolbyActive"].hide()
1501                         self["FormatActive"].hide()
1502                         self["TextActive"].hide()
1503
1504 class InfoBarNotifications:
1505         def __init__(self):
1506                 self.onExecBegin.append(self.checkNotifications)
1507                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1508                 self.onClose.append(self.__removeNotification)
1509         
1510         def __removeNotification(self):
1511                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1512         
1513         def checkNotificationsIfExecing(self):
1514                 if self.execing:
1515                         self.checkNotifications()
1516
1517         def checkNotifications(self):
1518                 if len(Notifications.notifications):
1519                         n = Notifications.notifications[0]
1520                         Notifications.notifications = Notifications.notifications[1:]
1521                         cb = n[0]
1522                         if cb is not None:
1523                                 self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1524                         else:
1525                                 self.session.open(n[1], *n[2], **n[3])
1526
1527 class InfoBarServiceNotifications:
1528         def __init__(self):
1529                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1530                         {
1531                                 iPlayableService.evEnd: self.serviceHasEnded
1532                         })
1533
1534         def serviceHasEnded(self):
1535                 print "service end!"
1536
1537                 try:
1538                         self.setSeekState(self.SEEK_STATE_PLAY)
1539                 except:
1540                         pass
1541
1542 class InfoBarCueSheetSupport:
1543         CUT_TYPE_IN = 0
1544         CUT_TYPE_OUT = 1
1545         CUT_TYPE_MARK = 2
1546         
1547         def __init__(self):
1548                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1549                         {
1550                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1551                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1552                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1553                         }, prio=1) 
1554                 
1555                 self.cut_list = [ ]
1556                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1557                         {
1558                                 iPlayableService.evStart: self.__serviceStarted,
1559                         })
1560
1561         def __serviceStarted(self):
1562                 print "new service started! trying to download cuts!"
1563                 self.downloadCuesheet()
1564
1565         def __getSeekable(self):
1566                 service = self.session.nav.getCurrentService()
1567                 if service is None:
1568                         return None
1569                 return service.seek()
1570
1571         def cueGetCurrentPosition(self):
1572                 seek = self.__getSeekable()
1573                 if seek is None:
1574                         return None
1575                 r = seek.getPlayPosition()
1576                 if r[0]:
1577                         return None
1578                 return long(r[1])
1579
1580         def jumpPreviousNextMark(self, cmp, alternative=None):
1581                 current_pos = self.cueGetCurrentPosition()
1582                 if current_pos is None:
1583                         return
1584                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1585                 if mark is not None:
1586                         pts = mark[0]
1587                 elif alternative is not None:
1588                         pts = alternative
1589                 else:
1590                         return
1591
1592                 seekable = self.__getSeekable()
1593                 if seekable is not None:
1594                         seekable.seekTo(pts)
1595
1596         def jumpPreviousMark(self):
1597                 # we add 2 seconds, so if the play position is <2s after
1598                 # the mark, the mark before will be used
1599                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1600
1601         def jumpNextMark(self):
1602                 self.jumpPreviousNextMark(lambda x: x)
1603
1604         def getNearestCutPoint(self, pts, cmp=abs):
1605                 # can be optimized
1606                 nearest = None
1607                 for cp in self.cut_list:
1608                         diff = cmp(cp[0] - pts)
1609                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1610                                 nearest = cp
1611                 return nearest
1612
1613         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1614                 current_pos = self.cueGetCurrentPosition()
1615                 if current_pos is None:
1616                         print "not seekable"
1617                         return
1618                 
1619                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1620                 
1621                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1622                         if onlyreturn:
1623                                 return nearest_cutpoint
1624                         if not onlyadd:
1625                                 self.removeMark(nearest_cutpoint)
1626                 elif not onlyremove and not onlyreturn:
1627                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1628                 
1629                 if onlyreturn:
1630                         return None
1631
1632         def addMark(self, point):
1633                 bisect.insort(self.cut_list, point)
1634                 self.uploadCuesheet()
1635
1636         def removeMark(self, point):
1637                 self.cut_list.remove(point)
1638                 self.uploadCuesheet()
1639
1640         def __getCuesheet(self):
1641                 service = self.session.nav.getCurrentService()
1642                 if service is None:
1643                         return None
1644                 return service.cueSheet()
1645
1646         def uploadCuesheet(self):
1647                 cue = self.__getCuesheet()
1648
1649                 if cue is None:
1650                         print "upload failed, no cuesheet interface"
1651                         return
1652                 cue.setCutList(self.cut_list)
1653
1654         def downloadCuesheet(self):
1655                 cue = self.__getCuesheet()
1656
1657                 if cue is None:
1658                         print "upload failed, no cuesheet interface"
1659                         return
1660                 self.cut_list = cue.getCutList()
1661
1662 class InfoBarSummary(Screen):
1663         skin = """
1664         <screen position="0,0" size="132,64">
1665                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1666                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1667         </screen>"""
1668
1669         def __init__(self, session, parent):
1670                 Screen.__init__(self, session)
1671                 self["CurrentService"] = ServiceName(self.session.nav)
1672                 self["Clock"] = Clock()
1673
1674 class InfoBarSummarySupport:
1675         def __init__(self):
1676                 pass
1677         
1678         def createSummary(self):
1679                 return InfoBarSummary
1680
1681 class InfoBarTeletextPlugin:
1682         def __init__(self):
1683                 self.teletext_plugin = None
1684                 
1685                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1686                         self.teletext_plugin = p
1687                 
1688                 if self.teletext_plugin is not None:
1689                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1690                                 {
1691                                         "startTeletext": (self.startTeletext, "View teletext...")
1692                                 })
1693                 else:
1694                         print "no teletext plugin found!"
1695
1696         def startTeletext(self):
1697                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1698
1699 class InfoBarSubtitleSupport(object):
1700         def __init__(self):
1701                 object.__init__(self)
1702                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1703                 self.__subtitles_enabled = False
1704
1705                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1706                         {
1707                                 iPlayableService.evStart: self.__serviceStarted,
1708                         })
1709
1710         def __serviceStarted(self):
1711                 # reenable if it was enabled
1712                 r = self.__subtitles_enabled
1713                 self.__subtitles_enabled = False
1714                 self.__selected_subtitle = None
1715                 self.setSubtitlesEnable(r)
1716
1717         def getCurrentServiceSubtitle(self):
1718                 service = self.session.nav.getCurrentService()
1719                 return service and service.subtitle()
1720         
1721         def setSubtitlesEnable(self, enable=True):
1722                 subtitle = self.getCurrentServiceSubtitle()
1723                 if enable and self.__selected_subtitle:
1724                         if subtitle and not self.__subtitles_enabled:
1725                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1726                                 self.subtitle_window.show()
1727                                 self.__subtitles_enabled = True
1728                 else:
1729                         if subtitle:
1730                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1731
1732                         self.subtitle_window.hide()
1733                         self.__subtitles_enabled = False
1734
1735         def setSelectedSubtitle(self, subtitle):
1736                 if self.__selected_subtitle != subtitle and self.subtitles_enabled:
1737                         # kick
1738                         self.__selected_subtitle = subtitle
1739                         self.__serviceStarted()
1740                 else:
1741                         self.__selected_subtitle = subtitle
1742
1743         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
1744         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)