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