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