8224d20d936cc59396594037befe91b12132e153
[vuplus_dvbapp] / lib / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector
2
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.BlinkingPixmap import BlinkingPixmapConditional
6 from Components.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 LanguageCodes.has_key(language):
1346                                         language = LanguageCodes[language][0]
1347         
1348                                 if len(description):
1349                                         description += " (" + language + ")"
1350                                 else:
1351                                         description = language
1352         
1353                                 tlist.append((description, x))
1354                         
1355                         selectedAudio = tlist[0][1]
1356                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1357
1358                         selection = 2
1359                         for x in tlist:
1360                                 if x[1] != selectedAudio:
1361                                         selection += 1
1362                                 else:
1363                                         break
1364
1365                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1366                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1367                 else:
1368                         del self.audioTracks
1369
1370         def audioSelected(self, audio):
1371                 if audio is not None:
1372                         if isinstance(audio[1], str):
1373                                 if audio[1] == "mode":
1374                                         keys = ["red", "green", "yellow"]
1375                                         selection = self.audioChannel.getCurrentChannel()
1376                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1377                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1378                         else:
1379                                 del self.audioChannel
1380                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1381                                         self.audioTracks.selectTrack(audio[1])
1382                 else:
1383                         del self.audioChannel
1384                 del self.audioTracks
1385
1386         def modeSelected(self, mode):
1387                 if mode is not None:
1388                         self.audioChannel.selectChannel(mode[1])
1389                 del self.audioChannel
1390
1391 class InfoBarSubserviceSelection:
1392         def __init__(self):
1393                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1394                         {
1395                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1396                         })
1397
1398                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1399                         {
1400                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1401                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1402                         }, -1)
1403                 self["SubserviceQuickzapAction"].setEnabled(False)
1404
1405                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1406
1407         def checkSubservicesAvail(self, ev):
1408                 if ev == iPlayableService.evUpdatedEventInfo:
1409                         service = self.session.nav.getCurrentService()
1410                         subservices = service and service.subServices()
1411                         if not subservices or subservices.getNumberOfSubservices() == 0:
1412                                 self["SubserviceQuickzapAction"].setEnabled(False)
1413
1414         def nextSubservice(self):
1415                 self.changeSubservice(+1)
1416
1417         def prevSubservice(self):
1418                 self.changeSubservice(-1)
1419
1420         def changeSubservice(self, direction):
1421                 service = self.session.nav.getCurrentService()
1422                 subservices = service and service.subServices()
1423                 n = subservices and subservices.getNumberOfSubservices()
1424                 if n and n > 0:
1425                         selection = -1
1426                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1427                         for x in range(n):
1428                                 if subservices.getSubservice(x).toString() == ref.toString():
1429                                         selection = x
1430                         if selection != -1:
1431                                 selection += direction
1432                                 if selection >= n:
1433                                         selection=0
1434                                 elif selection < 0:
1435                                         selection=n-1
1436                                 newservice = subservices.getSubservice(selection)
1437                                 if newservice.valid():
1438                                         del subservices
1439                                         del service
1440                                         if config.usage.show_infobar_on_zap.value:
1441                                                 self.doShow()
1442                                         self.session.nav.playService(newservice)
1443
1444         def subserviceSelection(self):
1445                 service = self.session.nav.getCurrentService()
1446                 subservices = service and service.subServices()
1447                 
1448                 n = subservices and subservices.getNumberOfSubservices()
1449                 selection = 0
1450                 if n and n > 0:
1451                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1452                         tlist = []
1453                         for x in range(n):
1454                                 i = subservices.getSubservice(x)
1455                                 if i.toString() == ref.toString():
1456                                         selection = x
1457                                 tlist.append((i.getName(), i))
1458
1459                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1460
1461                         keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1462
1463                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection + 2, keys = keys)
1464
1465         def subserviceSelected(self, service):
1466                 if not service is None:
1467                         if isinstance(service[1], str):
1468                                 if service[1] == "quickzap":
1469                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1470                                         self.session.open(SubservicesQuickzap, service[2])
1471                         else:
1472                                 self["SubserviceQuickzapAction"].setEnabled(True)
1473                                 if config.usage.show_infobar_on_zap.value:
1474                                         self.doShow()
1475                                 self.session.nav.playService(service[1])
1476
1477 class InfoBarAdditionalInfo:
1478         def __init__(self):
1479                 self["NimA"] = Pixmap()
1480                 self["NimB"] = Pixmap()
1481                 self["NimA_Active"] = Pixmap()
1482                 self["NimB_Active"] = Pixmap()
1483
1484                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1485                 self["TimeshiftPossible"] = self["RecordingPossible"]
1486                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1487
1488                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1489                 res_mgr = eDVBResourceManagerPtr()
1490                 if eDVBResourceManager.getInstance(res_mgr) == 0:
1491                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1492
1493         def tunerUseMaskChanged(self, mask):
1494                 if mask&1:
1495                         self["NimA_Active"].show()
1496                 else:
1497                         self["NimA_Active"].hide()
1498                 if mask&2:
1499                         self["NimB_Active"].show()
1500                 else:
1501                         self["NimB_Active"].hide()
1502
1503         def checkTunerState(self, service):
1504                 info = service.frontendInfo()
1505                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1506                 if feNumber is None:
1507                         self["NimA"].hide()
1508                         self["NimB"].hide()
1509                 elif feNumber == 0:
1510                         self["NimB"].hide()
1511                         self["NimA"].show()
1512                 elif feNumber == 1:
1513                         self["NimA"].hide()
1514                         self["NimB"].show()
1515
1516         def gotServiceEvent(self, ev):
1517                 service = self.session.nav.getCurrentService()
1518                 if ev == iPlayableService.evStart:
1519                         self.checkTunerState(service)
1520
1521 class InfoBarNotifications:
1522         def __init__(self):
1523                 self.onExecBegin.append(self.checkNotifications)
1524                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1525                 self.onClose.append(self.__removeNotification)
1526         
1527         def __removeNotification(self):
1528                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1529         
1530         def checkNotificationsIfExecing(self):
1531                 if self.execing:
1532                         self.checkNotifications()
1533
1534         def checkNotifications(self):
1535                 if len(Notifications.notifications):
1536                         n = Notifications.notifications[0]
1537                         Notifications.notifications = Notifications.notifications[1:]
1538                         cb = n[0]
1539                         if cb is not None:
1540                                 self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1541                         else:
1542                                 self.session.open(n[1], *n[2], **n[3])
1543
1544 class InfoBarServiceNotifications:
1545         def __init__(self):
1546                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1547                         {
1548                                 iPlayableService.evEnd: self.serviceHasEnded
1549                         })
1550
1551         def serviceHasEnded(self):
1552                 print "service end!"
1553
1554                 try:
1555                         self.setSeekState(self.SEEK_STATE_PLAY)
1556                 except:
1557                         pass
1558
1559 class InfoBarCueSheetSupport:
1560         CUT_TYPE_IN = 0
1561         CUT_TYPE_OUT = 1
1562         CUT_TYPE_MARK = 2
1563         CUT_TYPE_LAST = 3
1564         
1565         ENABLE_RESUME_SUPPORT = False
1566         
1567         def __init__(self):
1568                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1569                         {
1570                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1571                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1572                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1573                         }, prio=1) 
1574                 
1575                 self.cut_list = [ ]
1576                 self.is_closing = False
1577                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1578                         {
1579                                 iPlayableService.evStart: self.__serviceStarted,
1580                         })
1581
1582         def __serviceStarted(self):
1583                 if self.is_closing:
1584                         return
1585                 print "new service started! trying to download cuts!"
1586                 self.downloadCuesheet()
1587                 
1588                 if self.ENABLE_RESUME_SUPPORT:
1589                         last = None
1590                         
1591                         for (pts, what) in self.cut_list:
1592                                 if what == self.CUT_TYPE_LAST:
1593                                         last = pts
1594                         
1595                         if last is not None:
1596                                 self.resume_point = last
1597                                 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1598
1599         def playLastCB(self, answer):
1600                 if answer == True:
1601                         seekable = self.__getSeekable()
1602                         if seekable is not None:
1603                                 seekable.seekTo(self.resume_point)
1604
1605         def __getSeekable(self):
1606                 service = self.session.nav.getCurrentService()
1607                 if service is None:
1608                         return None
1609                 return service.seek()
1610
1611         def cueGetCurrentPosition(self):
1612                 seek = self.__getSeekable()
1613                 if seek is None:
1614                         return None
1615                 r = seek.getPlayPosition()
1616                 if r[0]:
1617                         return None
1618                 return long(r[1])
1619
1620         def jumpPreviousNextMark(self, cmp, alternative=None):
1621                 current_pos = self.cueGetCurrentPosition()
1622                 if current_pos is None:
1623                         return
1624                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1625                 if mark is not None:
1626                         pts = mark[0]
1627                 elif alternative is not None:
1628                         pts = alternative
1629                 else:
1630                         return
1631
1632                 seekable = self.__getSeekable()
1633                 if seekable is not None:
1634                         seekable.seekTo(pts)
1635
1636         def jumpPreviousMark(self):
1637                 # we add 2 seconds, so if the play position is <2s after
1638                 # the mark, the mark before will be used
1639                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1640
1641         def jumpNextMark(self):
1642                 self.jumpPreviousNextMark(lambda x: x)
1643
1644         def getNearestCutPoint(self, pts, cmp=abs):
1645                 # can be optimized
1646                 nearest = None
1647                 for cp in self.cut_list:
1648                         diff = cmp(cp[0] - pts)
1649                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1650                                 nearest = cp
1651                 return nearest
1652
1653         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1654                 current_pos = self.cueGetCurrentPosition()
1655                 if current_pos is None:
1656                         print "not seekable"
1657                         return
1658                 
1659                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1660                 
1661                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1662                         if onlyreturn:
1663                                 return nearest_cutpoint
1664                         if not onlyadd:
1665                                 self.removeMark(nearest_cutpoint)
1666                 elif not onlyremove and not onlyreturn:
1667                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1668                 
1669                 if onlyreturn:
1670                         return None
1671
1672         def addMark(self, point):
1673                 bisect.insort(self.cut_list, point)
1674                 self.uploadCuesheet()
1675
1676         def removeMark(self, point):
1677                 self.cut_list.remove(point)
1678                 self.uploadCuesheet()
1679
1680         def __getCuesheet(self):
1681                 service = self.session.nav.getCurrentService()
1682                 if service is None:
1683                         return None
1684                 return service.cueSheet()
1685
1686         def uploadCuesheet(self):
1687                 cue = self.__getCuesheet()
1688
1689                 if cue is None:
1690                         print "upload failed, no cuesheet interface"
1691                         return
1692                 cue.setCutList(self.cut_list)
1693
1694         def downloadCuesheet(self):
1695                 cue = self.__getCuesheet()
1696
1697                 if cue is None:
1698                         print "upload failed, no cuesheet interface"
1699                         return
1700                 self.cut_list = cue.getCutList()
1701
1702 class InfoBarSummary(Screen):
1703         skin = """
1704         <screen position="0,0" size="132,64">
1705                 <widget source="CurrentTime" render="Label" position="56,46" size="82,18" font="Regular;16" >
1706                         <convert type="ClockToText">WithSeconds</convert>
1707                 </widget>
1708                 <widget source="CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
1709                         <convert type="ServiceName">Name</convert>
1710                 </widget>
1711         </screen>"""
1712
1713         def __init__(self, session, parent):
1714                 Screen.__init__(self, session)
1715                 self["CurrentService"] = CurrentService(self.session.nav)
1716                 self["CurrentTime"] = Clock()
1717
1718 class InfoBarSummarySupport:
1719         def __init__(self):
1720                 pass
1721         
1722         def createSummary(self):
1723                 return InfoBarSummary
1724
1725 class InfoBarTeletextPlugin:
1726         def __init__(self):
1727                 self.teletext_plugin = None
1728                 
1729                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1730                         self.teletext_plugin = p
1731                 
1732                 if self.teletext_plugin is not None:
1733                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1734                                 {
1735                                         "startTeletext": (self.startTeletext, _("View teletext..."))
1736                                 })
1737                 else:
1738                         print "no teletext plugin found!"
1739
1740         def startTeletext(self):
1741                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1742
1743 class InfoBarSubtitleSupport(object):
1744         def __init__(self):
1745                 object.__init__(self)
1746                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1747                 self.__subtitles_enabled = False
1748
1749                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1750                         {
1751                                 iPlayableService.evEnd: self.__serviceStopped,
1752                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
1753                         })
1754                 self.cached_subtitle_checked = False
1755
1756         def __serviceStopped(self):
1757                 self.subtitle_window.hide()
1758                 self.__subtitles_enabled = False
1759                 self.cached_subtitle_checked = 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)