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