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