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