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